Chadha Thesis
Chadha Thesis
Chadha Thesis
Karishma Chadha
i
tween these code blocks and the original AI2 blocks. Language isomorphism
guarantees that a round-trip conversion (from text to blocks and back, or
blocks to text and back) begins and ends with the identical program.
To implement the TAIL language, I wrote a grammar for the ANTLR
parser generator to generate a JavaScript lexer and parser for TAIL. I use
actions in the grammar to translate the TAIL parse tree into AI2s XML tree
representation for blocks.
This project aims to (1) increase AI2s usability by providing an efficient
means for reading, constructing, sharing, and reusing programs, and (2) ease
users transitions from blocks programming to text programming.
ii
Contents
Chapter 1 Introduction 1
1.1 Blocks Programming Languages . . . . . . . . . . . . . . . . . 1
1.2 MIT App Inventor . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Limitations in App Inventor . . . . . . . . . . . . . . . . . . . 6
1.3.1 Writing Complex Programs . . . . . . . . . . . . . . . 6
1.3.2 Reading and Searching Programs . . . . . . . . . . . . 7
1.3.3 Reusing or Sharing Programs . . . . . . . . . . . . . . 8
1.4 One Solution to Fix them All . . . . . . . . . . . . . . . . . . 9
1.5 Road Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
iii
2.6 Previous Work on Blocks & Text Conversion in App Inventor 26
2.6.1 The Java Bridge . . . . . . . . . . . . . . . . . . . . . 27
2.6.2 Rendering Android App Inventor Code Blocks as Pseudo-
Python Code . . . . . . . . . . . . . . . . . . . . . . . 28
2.6.3 Venthon: A First Take at a Creating a New Textual
Language for App Inventor . . . . . . . . . . . . . . . . 28
2.7 Code to Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . 33
iv
Chapter 5 Conclusion and Future Work 67
5.1 Current State . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.2 Immediate Future . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.2.1 Adding all AI2 Blocks to the TAIL Grammar . . . . . 68
5.2.2 Creating YAIL Generators for Statements and Decla-
rations . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5.3 Near Future . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.3.1 User Studies . . . . . . . . . . . . . . . . . . . . . . . . 71
5.3.2 Changing Conversion Architecture to Improve Perfor-
mance . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.3.3 Converting Screens, Workspaces, and Entire Projects . 73
5.3.4 Making Text Readable . . . . . . . . . . . . . . . . . . 75
5.3.5 Generalizing TAIL Grammar Rules . . . . . . . . . . . 76
5.3.6 Adding Language Abbreviations . . . . . . . . . . . . . 76
5.3.7 Improving Error Handling . . . . . . . . . . . . . . . . 78
5.3.8 Creating Tutorials & Documentation for AI2 Users . . 78
5.4 Far Future . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.4.1 Venthon, Venti, and More! . . . . . . . . . . . . . . . . 79
5.4.2 TAIL Text Editor . . . . . . . . . . . . . . . . . . . . . 79
5.4.3 Blocks Language for Existing Text Language . . . . . . 79
5.4.4 Interactive Environment to Edit Corresponding Blocks
and Text Languages Side-By-Side . . . . . . . . . . . . 80
v
Chapter 1
Introduction
1
essarily readily available in textual programming languages. For example,
blocks expecting other blocks as inputs often have labels indicating what in-
put they are expecting. Even more simply useful visual information is that,
at the very least, users will have an idea of how many inputs a block takes
based on the shape of the block (i.e. the number of sockets the block has;
see Section 3.2.1 for an explanation of sockets). For all of these and many
more reasons, blocks programming languages are a powerful tool for lowering
the barriers to learning computer science. With these programming environ-
ments, novices can focus on learning the concepts, thinking, and problem
solving skills associated with computer science principles rather than being
hindered by the frustrations of syntax errors that differ in each language.
Blocks programming languages have become quite popular in recent years.
Many such languages have arisen such as: Scratch [Scra], an online environ-
ment to create games and animations; PicoBlocks [Pic], an environment to
program mini-computers to use with physical LEGO [Leg] blocks; Blockly
[Bloa], a blocks language framework that has been used to create numerous
blocks-based microworlds, including the incredibly popular Hour of Code
[Hou] microworlds; and MIT App Inventor [Ai1a; Ai2a], an online environ-
ment to develop mobile applications for Android devices.
Other blocks languages that are worth mentioning, but not discussed in
this paper are StarLogo TNG [Staa], StarLogo Nova [Stab], Tynker [Tyn],
and Hopscotch [Hop].
2
1.2 MIT App Inventor
MIT App Inventor began as a Google project that is now being developed
and maintained in the MIT Media Lab as part of the Center for Mobile
Learning. It has gained wide popularity in recent years, reaching over 1.5
million users worldwide [Ai1b; Ai2b]. App Inventor aims to democratize
programming through its easy to use blocks programming language, and by
removing much of the overhead of the Android SDK, so that even those who
are not familiar with programming can easily build Android applications.
MIT App Inventor has gone through two major iterations, App Inventor
Classic (AI1 ) [Ai1a] and App Inventor 2 (AI2 ) [Ai2a]. The overarching
design of both iterations is the same. The tool consists of two parts, the
Designer (Figure 1.1) and the Blocks Editor (Figure 1.2).
3
be a visual part of the application (such as a button or a text box), or a
non-visual part of the application responsible for communicating with the
devices hardware (such as a timer, or interfaces interacting with the devices
camera or location sensor).
The Blocks Editor is the environment in which users can program the
functionality of the components they have added to their application (using
the Designer). The Blocks Editor uses a blocks programming language for
users to code their applications. The blocks programming language used in
AI1 was based on the MIT Open Blocks framework [Ope], a Java library
created as part of the MIT STEP program for defining blocks programming
languages. The second iteration of App Inventor (AI2) uses a blocks pro-
gramming language designed in JavaScript using Blockly, being developed at
4
Google.
Both iterations of the Blocks Editor (and many other blocks programming
environments) use the simple design of drawers of blocks, and a "workspace".
In App Inventor, there is a list of drawers on the left hand side of the Blocks
Editor and a blank area on the right hand side for users to construct their
programs. The drawers are organized in categories based on what kind of
functionality they provide (control, math, lists, variables, etc.).
The blocks are different colors (colored by the colors of their drawer)
so that they are easier to visually identify and distinguish from one another.
Users drag blocks they want to use from these drawers out into the workspace
to compose their programs. Figure 1.3 shows some simple constructions of
blocks.
5
1.3 Limitations in App Inventor
App Inventor is a low-barrier tool for users of all backgrounds to write An-
droid applications. However, App Inventor has a few key limitations.
Blocks programing languages are easy to learn, and users are able to create
programs quickly in these languages. However, while it is easy and quick to
write simple programs, it becomes increasingly tedious and time consuming
to construct more complex programs. For some users, it may prove to be
easier and faster to type text than to click, drag, and connect blocks together.
Complex programs quickly become inefficient to create. Functions as simple
as the quadratic formula, half of which is depicted in Figure 1.4, use many
blocks (note that Figure 1.4 uses collapsed variable reference blocks; the
fully expanded blocks would greatly increase the length of the entire block
structure). Making an error in composing the blocks the first time adds even
more inefficiency because the users are then required to unplug and replug
several blocks to correct the error(s). Even a seasoned blocks programmer is
likely to make some of these frustrating errors.
6
1.3.2 Reading and Searching Programs
(a) Workspace with Collapsed Blocks (b) Workspace with Expanded Blocks
It is extremely difficult for someone (even the project owners) to read this
program or search it for a particular piece of code. Figure 1.5b shows what
the workspace looks like when all of the collapsed blocks in Figure 1.5a are
expended using App Inventors expand all feature. App Inventor does offer
an organization feature which arranges the blocks so that they are aligned and
not overlapping, however even with all of the blocks organized, the program is
very difficult to read. Additionally App Inventor does not currently offer any
searching features; if someone reading the program needs to find a particular
piece of code, he/she must shuffle through the blocks of the entire program
7
to find it.
The difficulty of reading and searching programs is currently a very large
limitation of App Inventor as there are many people who would wish to read
ones program. These parties include the developers of the app themselves,
App Inventor users who wish to learn from another project (perhaps this
project is hosted in the App Inventor gallery, a place where users can post
their projects publicly), or in the case where App Inventor is taught in a
Computer Science curriculum, instructors who wish to provide feedback and
support to their students having trouble developing these programs. Thus,
making App Inventor easier to read is a high priority.
App Inventor does not currently offer a means of sharing projects or repli-
cating/reusing parts of AI2 blocks programs across projects. The only way
to achieve either of these tasks is for users to download the projects (in the
form of archived files) and send them to the person (or people) they wish to
share the projects with. If the receiving user(s) wishes to incorporate parts of
one project into another (even just between two of the users own projects),
the only way to accomplish this would to be to view the two projects side
by side and reconstruct the part in question in the second project. This is
extremely inefficient, and inhibits users from reusing blocks code in multiple
projects and sharing blocks code with others.
8
1.4 One Solution to Fix them All
Textual languages make writing, reading, searching, sharing and reusing pro-
grams much easier. In order to address the issues outlined above, I have
created a new textual language, TAIL, the Textual App Inventor Language,
that is isomorphic to AI2s blocks programming language, and provided a
mechanism for converting between arbitrary AI2 block assemblies and TAIL
code.
TAIL syntax is designed to provide users with a systematic way to trans-
late the visual information on the blocks into text.
I have extended AI2 with a set of code blocks (Figure 1.6) for users to
specify TAIL code for expressions, statements, and top-level declarations,
representing original AI2 blocks. These code blocks have the same meaning
as the larger block assemblies they represent.
In order to ease the ability to read blocks programs, I provide the ability
to convert between AI2 blocks and TAIL code. Users can convert any set
of AI2 blocks into TAIL code using the TAIL code blocks I have added.
Section 5.3.3 talks about converting entire screens, workspaces, and projects
into TAIL, allowing users to view the code pertaining to the app in a more
concise, textual form, easy for reading. To facilitate the writing and sharing
of AI2 programs, I provide the ability to write App Inventor code in TAIL,
using my added TAIL code blocks, and convert the TAIL text in the code
blocks into the original AI2 blocks language. This improves upon the current
ability to share projects or fragments of projects because users can duplicate
code across projects by first converting projects into TAIL, and copy-pasting
TAIL code into a separate project to duplicate code across projects. Users
9
Figure 1.6: TAIL Code Blocks I have added to App Inventor
can convert this TAIL code back into blocks to continue programming with
the AI2 blocks if they prefer. This project also provides the ability to write
code in TAIL. The TAIL code is semantically equivalent to the AI2 blocks
code it represents, thus allowing users to write AI2 programs solely with
TAIL if they prefer, or to combine TAIL and AI2 blocks together using the
provided TAIL code blocks I added. Language isomorphism guarantees that
round trip conversions (from blocks to text and back or text to blocks and
back) begin and end with the identical program. Figure 1.7 to Figure 1.11
shows some sample conversion in both directions.
If the user starts out with a set of AI2 blocks he/she wishes to convert
to TAIL, the user can click an option in the context menu Convert to TAIL
(Figure 1.8) which will then transform the set of AI2 blocks rooted at the
10
Figure 1.7: AI2 Blocks to be Converted into TAIL Code Blocks
Figure 1.8: Click Context Menu Option for Converting Blocks to TAIL
clicked block, into the TAIL code blocks, replacing the original blocks. The
resulting TAIL code block has the same functionality as the AI2 blocks.
Users can choose which blocks to convert, regardless of their position in
11
Figure 1.10: Converting TAIL to AI2 Blocks
the syntax tree. While Figures 1.7 to 1.11 depict the round-trip conversion
of a top-level, global variable declaration block, Figures 1.12 to 1.14 depicts
three possible conversions of the same set of AI2 blocks. Each figure repre-
sents a conversion of the blocks at a different level of the syntax tree for the
set of blocks or TAIL text. All of the 6 sets of blocks (2 sets of blocks in
each figure) carry the same semantics even though the representations are
12
different. These figures also show that it is possible to use just AI2 blocks,
just a single TAIL text block, or even a combination of AI2 blocks and TAIL
text blocks in any given AI2 program.
13
means for reading, constructing, and sharing programs, and (2) ease users
transitions from blocks programming to text programming.
14
Chapter 2
Related Work
Research of related projects reveals that there are varying notions of what it
means to "convert blocks to text." As I have mentioned above, my aim for
this project is to create and provide a means of converting to/from a textual
languages that is semantically and syntactically isomorphic to AI2s blocks
language. This entails matching TAIL syntax to each and every block in AI2
and designing TAIL in such a way that round trip conversions between the
two languages yield the the original code that began the conversion (regard-
less of whether the conversion begins with blocks or text). I talk more about
the design of TAIL in Chapter 3.
In this chapter, I talk about other projects, both related and unrelated
to App Inventor that explore the relationship between blocks programming
languages (or other visual programming languages) and textual programming
languages, and the different notions of converting blocks to text in each of
these projects.
15
2.1 Scratch
Scratch is a popular blocks programming environment for creating anima-
tions and games. There have been two unique notations of representing
Scratch blocks programs in text form.
16
Figure 2.1: Scratch block scripts for different sprites
2.1.2 ScratchBlocks
17
Figure 2.2: Project summary file corresponding to Figure 2.1
18
mantics of the AI2 blocks code it represents. Figure 2.3 shows a sample
rendering of a ScratchBlocks script as an image of blocks in Scratch (as it
might be displayed on the Scratch Wiki). The documentation for this plu-
gin notes that the plugin "tries to match the code you write as closely as
possible, and doesnt check [for] correct syntax" [Scrc]. There are additional
tools that users of the wiki and the forums can use that will "take blocks
directly from a Scratch project, and turn them into text [to] paste inside a
<scratchblocks> tag". This plugin, in conjunction with some tools pro-
vided by other Scratchers [Scre; Scrf], allows for two kinds of conversions:
from blocks inside a scratch project, to the ScratchBlocks script text (which
will then render this text as an image on the Scratch Wiki); and just the
latter half of the conversion mentioned, converting the ScratchBlocks script
text into an image of a set of Scratch blocks.
It is important to note that the ScratchBlocks and ScratchBlocks2 plugins
generate text code that is only for use on the Scratch Wiki and Scratch forums
(and not in the actual Scratch blocks programming environment). However,
there are additional tools which can convert Scratch projects [Scre] or scripts
that have been added to the Scratch backpack (a mechanism Scratch uses to
share/reuse code between projects) [Scrf] to the ScratchBlocks text for use
on the Wiki/forums.
This take on conversions between blocks and text is entirely different from
that of the project discussed in this paper. These plugins allow the conversion
from Scratch blocks to the text that is responsible for creating an image of
the original Scratch blocks that the text represents. This entirely different
view on blocks to text (to image) conversions puts an entirely different spin
19
on the relationship between different representations of programs.
end
say [ Done !]
</ scratchblocks >
2.2 Blockly
Bockly is a web-based blocks programming environment being developed at
Google. Blockly is referred to as a "visual programming editor"; however,
the underlying concept of Blockly is the same as the blocks languages used
in Scratch and MIT App Inventor 1 and 2. In fact, in the transition from
the Java applet to the JavaScript based blocks editor, AI1 transitioned from
the MIT OpenBlocks framework to using Blockly as the underlying blocks
language framework for AI2. The project wiki for Blockly includes a number
of sample applications built in Blockly which demonstrate different possible
uses of the environment. One such sample application, Code [Blob] allows
exporting a Blockly program into JavaScript, Python, Dart, or XML.
It is worth noting that while this particular application of Blockly does
allow for conversion of Blockly code to executable code in any one of the four
textual programming languages listed above, conversion in the other direction
20
is not possible. This is in part due to the fact that Python, JavaScript, Dart,
and XML respectively each do not have one-to-one correspondences with the
blocks language of Blockly. Arbitrary code written in one of these textual
languages cannot be translated into Blockly because the textual languages
are not isomorphic to Blockly.
Language isomorphism is one of the main design principles that guiding
the creation of TAIL. The work for this project is motivated by the necessity
for App Inventor users to be able to not only convert AI2 blocks into text,
but also to be able to write arbitrary code in a textual language (TAIL),
and be able to convert this textual code to the visual syntax of AI2 blocks.
Isomorphism of the two languages guarantees that any round trip conversions
will yield the original code (whether the origins were AI2 blocks or TAIL
text).
Because it is possible to translate from any Turing-complete textual pro-
gramming language to any other, translating from Blockly to the particular
languages mentioned here doesnt say much about the specific relationship
between blocks and text languages.
2.3 PicoBlocks
PicoBlocks, from The Playful Invention Company [Pic], is another blocks-
programming environment based on research from the MIT Media Lab. This
software is intended for use with physical devices (mini-computers) called Pic-
oCrickets (Figure 2.4) which can be used alongside LEGO pieces to create
interesting mobile, interactive projects. The PicoBlocks programming envi-
21
Figure 2.4: A PicoCricket
22
Figure 2.5: The PicoBlocks environment [Pic]
and text, rather, the two are in separate windows, but they are components
of the same program. The text language allows users to specify functions
and procedures which then appear as single blocks in the blocks window
which users can then use with the rest of their blocks program. Thus, the
text language is just a place for users to write declarations (e.g. new block
definitions, with or without inputs).
Thus this project illustrates yet another way that blocks and text lan-
guages can be used in conjunction.
23
Figure 2.6: A PicoBlocks custom block declaration
Figure 2.7: This block appears in the special MyBlocks drawer after
creating the block definition in Figure 2.6
2.4 SLASH
SLASH [Beh] is a student research project by Kara Behnke at the Univer-
sity of Colorado Boulder, the ATLAS Institute. A modification of Scratch,
SLASH uses blocks-based programming to produce code in the Linden Script-
24
ing Language (LSL) [Beh], to program behavior in the Second Life virtual
world. Behnke notes that "the goal of SLASH is to improve first-time pro-
gramming experiences for non-[CS]-majors by juxtaposing block-based pro-
gramming with [textual] programming language syntax" [Beh]. This research
project is also motivated by the goal of attempting to ease the transition for
novice programmers from blocks programming to textual programming. The
work involves the generation of textual code from blocks code, but does not
allow the conversion in the other direction. There is no connection between
the semantics of the textual language and the semantics of the blocks pro-
gramming language; instead of translating between two syntax tree represen-
tations of the same language, the blocks code in SLASH is used to generate
LSL syntax.
This project has the similar goal of attempting to ease the transition from
blocks programming to textual programming for novice programmers, by
"modifying the Scratch [blocks programming] environment to enable students
to learn [textual] language syntax while they program using blocks" [Beh]
This project attempts to fulfill its goals through the unique approach of
allowing students to create programs using blocks therefore making full
use of all the advantages of blocks programming environments as mentioned
in the previous chapter however the students are required to "compare and
analyze their blocks programs with the generated LSL scripts", thus pushing
students towards translating their understanding of program structure and
composition (which theyve gained from blocks programming), into textual
programming.
25
2.5 Cabana
Cabana, being developed by the Department of Behavior and Logic Inc., is
another web-based application for the development of mobile applications
across platforms. Like App Inventor, Cabana aims to "make app develop-
ment easier" [Dic12]. Instead of a blocks programming language, Cabana uses
an environment designed like a wiring diagram which links together nodes
representing code modules. Cabana comes with pre-existing code modules
representing basic program fragments (for loops, if statements, etc.). Ad-
ditionally, Cabana allows users to specify their own code modules using a
built-in feature to specify nodes in JavaScript code. Thus, Cabana allows
for experienced programmers to write code more efficiently using JavaScript,
whereas novice programmers can still use the built-in wire diagram format
easily regardless of prior programming experience. The developers of Ca-
bana argue that this combination of wiring diagram and JavaScript nodes
"gives Cabana an advantage over App Inventor" by freeing users from the re-
striction of a "novice-centered programming environment like App Inventors
block language" [Dic12].
26
2.6.1 The Java Bridge
A subset of developers of MIT App Inventor have also created the App In-
ventor Java Bridge (I will refer to this as the Java Bridge). The Java Bridge
is designed to make a transition between using App Inventor for developing
Android applications to developing applications in Java using the Android
SDK. The Java Bridge allows App Inventor users to incorporate App In-
ventor components into apps they create in Java with the standard Android
SDK tools. This application is a good transition step for App Inventor users
who are or have become familiar with Java and wish to eventually transition
from App Inventor to the Android SDK for application development.
David Wolber of the App Inventor Developer Team has developed a tool
to use alongside the Java Bridge [Bri] which translates existing App Inventor
applications into Java applications which make calls to the Java Bridge.
Again, the Java Bridge, and David Wolbers tool, mentioned above, offer
a different meaning of the notion of converting blocks to text. This tool
offers a way to convert entire App Inventor projects into full Java projects.
However, there is not a way to translate between just the blocks program in
App Inventor to a Java program, or even more specifically there is no way to
translate a subset of the blocks in the App Inventor blocks editor workspace
to a piece of Java code representing just that subset of blocks. Additionally,
users cannot specify code in Java to translate into the AI2 blocks.
27
2.6.2 Rendering Android App Inventor Code Blocks as
Pseudo-Python Code
28
was designed to take Guos work a step further and allow users to convert
blocks and text in either direction (specifying code in AI blocks and convert-
ing to Venthon or specifying code in Venthon and converting to AI blocks).
29
Figure 2.8: Sample AI1 Blocks to be Rendered in Venthon
As shown in Figure 2.8 and Figure 2.9, top level declarations in AI1 such
as global variable declarations or event handlers look very much the same
as top-level declarations (e.g. global variable declarations and procedure
declarations) in Python. As in Python, a colon followed by indented lines
of code denote a sequence of statements, where the indentation level of the
particular line of code indicates which scope it is a part of.
30
height = 45
fun average (x , y ) =
( x + y ) /2
As can be noted, some of the aspects of Venthon syntax stray quite a bit
from traditional Python syntax. The function declaration at the bottom of
the page looks closer to the syntax of a functional language such as ML. The
keyword fun, representing a function declaration, was chosen to differentiate
between the AI1 function declaration block and the AI1 procedure declara-
tion block. This distinction arises from the fact that the App Inventor blocks
language does not have a "return" statement block, but instead uses other
methods to have a block return an expression as its output. This is a small
example of what kinds of things need to be taken into consideration when
designing a language that is isomorphic to the App Inventor blocks language.
Chapter 3 discusses in detail the design of TAIL.
Venthon was designed as a first take at a textual language for App Inven-
tor, with the idea (or rather future goal) in mind that there could be multiple
syntaxes for a textual language for App Inventor, and ideally each of these
syntaxes would be isomorphic to each other, thus allowing the App Inventor
user to pick and choose which language he/she prefers to use.
31
Erin implemented the AI1 blocks to Venthon conversion, while I imple-
mented the Venthon parser and the Venthon to AI1 blocks conversion.
The implementation of Venthon is very similar to that of TAIL (discussed
in further detail in Section 4.2). I specified Venthon syntax in a grammar
using ANTLR [Ant], a parser generator which takes a language grammar as
input and produces a lexer and parser for the language defined by the input
grammar. Options in the ANTLR grammar file allow the user to specify
aspects of the generated parser and lexer. One of the most important options
to specify in the grammar is the target language in which to generate the
lexer and parser for the language being created.
The Venthon parser generated by ANTLR was configured to produce
an intermediate JSON representation of the information from the parsed
Venthon program. This intermediate representation, called JAIL, (JSON
App Inventor Language) was to be used as a stepping stone between Venthon
and the AI1 blocks language.
Though considerable progress was made on the implementation of Ven-
thon, the transition from AI1 to AI2 required that the Venthon parser and
lexer be converted from Java to JavaScript, just as the rest of the code in
App Inventor had made the same transition. Additionally while the origi-
nal implementation converts a Venthon program into the intermediate JAIL
notation, the AI2 architecture better lends itself to converting the textual
language program into the underlying XML representation of AI2 blocks
(discussed in depth in Chapter 4). Finally, the design of the Venthon syntax
and the Venthon implementation remain incomplete as the language needs
to be redesigned to better adhere to AI2 and there are remaining design
32
decisions for some very specific details of the language.
Thus, while continuing the Venthon implementation, I made the decision
to put Venthon development on hold and instead design a language with a
more systematic syntax which would be easily predictable given any set of
AI2 blocks. This language, TAIL, is the primary focus of this paper, and is
discussed in detail over the next few chapters.
33
blocks language can be expressed as a piece of Python code, and therefore
any valid AI2 program can be translated into valid Python, it is not the case
that any valid Python program can be translated into a valid AI2 program.
In fact, the C2B write-up provides a list of Python constructs that have no
equivalent representation in the AI2 blocks programming language. These
constructs include, but are not limited to the following: lambda functions,
dictionaries, list comprehensions, class definitions, etc..
The work on C2B reinforces the importance of language isomorphism
when considering the conversion from the chosen textual representation to
the AI2 blocks. The language isomorphism principle and its importance are
discussed in more detail in Section 3.1.1.
34
Chapter 3
35
those users who are perhaps more experienced programmers but are looking
to create Android apps through an easy-to-use interface, as well as to those
users who are ready to transition from blocks programming languages to the
more traditional textual programming languages.
In order to address the issue of making App Inventor programs more
readable and searchable through a textual language, App Inventor users must
have a means of converting their existing AI2 projects (or AI2 projects they
create in the future) into the textual language. This one-way conversion is
enough to solve the problem of reading and searching AI2 projects. Thus,
for this specific problem, having read-only text (such as the work by Philip
Guo discussed in Section 2.6.2) would suffice.
Another limitation of App Inventor, mentioned in Section 1.3, is the ineffi-
ciency and tediousness of creating complex applications in a blocks program-
ming language. While App Inventor makes mobile application development
simpler by removing the overhead of the Android SDK and by providing
an environment where a lack of programming experience is not a hindrance,
users (experienced programmers and novices alike) will quickly become ac-
customed to the concept of creating programs by connecting visual program
fragments together. As the user becomes more accustomed to blocks pro-
gramming, the user also begins to create more and more complicated appli-
cations, and consequently, these applications become more tedious to create.
Blocks programming languages have the unfortunate downside that they be-
come increasingly frustrating to use the more one understands how to use
them, because users become quickly attuned to the concept of combining
blocks to make programs, the user wants to be able to perform these actions
36
quickly. However, for the majority of computer users, they are more attuned
to and efficient at typing than they are at clicking and dragging objects with
a mouse or trackpad. The repetitive motion of click-and-drag is much slower
than typing (for the efficient typist). For experienced programmers, pro-
gramming with a mouse is extremely frustrating whereas typing code is the
norm.
Thus, providing App Inventor users with a textual alternative to the
blocks programming language provided would increase efficiency of many
of the users who are already familiar with programming, as well as users
who are ready to transition beyond blocks programming. Allowing users to
convert from the textual programming language to the blocks programming
languages allows the users to personalize their programming experience by
combining the two forms of programming as suits their needs. Thus, for
users who are beginning the transition from blocks programming to textual
programming dont need to give up programming in the AI2 blocks language
in order to attempt programming in TAIL. These users can attempt writing
code in TAIL and convert it to blocks to test their understanding.
Finally, combining the conversion in both directions (blocks to text and
text to blocks) allows for the solution to the third limitation in App Inventor:
sharing code between projects.
Currently, App Inventor users would have to go through something similar
to the following scenario in order to replicate code between multiple projects.
App Inventors Current Sharing Scenario:
37
A and looks at the code in user-A-app and wants to recreate
something similar in one of User Bs own projects (user-B-app).
User A agrees to let User B use part of the code from user-A-app.
Therefore, User A downloads its project (in the form of a .aia
filedenoting an App Inventor project file) and sends it to User
B (through email or some other file transfer method). User B
then uploads User As project into User Bs own App Inventor
account. User B opens user-B-app in a separate browser window.
User B views its copy of user-A-app side-by-side with user-B-app.
User B then meticulously reconstructs part of the code from user-
A-app in user-B-app, by dragging, dropping, and clicking blocks
together.
Providing App Inventor users with the capabilities to convert from blocks
to text and also from text to blocks allows for an easier method of sharing
entire AI2 programs or parts of programs across different projects. The new
sharing scenario would be much simpler:
Above, I talk about "converting" from blocks to text and "converting" from
text to blocks. On a larger scale the term conversion refers to the translation
38
of an entire AI2 project (including information from the Blocks Editor as
well as the Designer, both mentioned in Section 1.2) into TAIL text. As
noted in Chapter 5, I have not currently implemented this, but Section 5.3.3
discusses in detail what this large scale conversion will entail. On a smaller
scale, the term conversion also applies to translating parts of programs such
as individual blocks or sets of blocks. As mentioned in Section 1.4, the App
Inventor user is allowed to choose which block or set of blocks to convert into
TAIL (or vice-versa), regardless of where this block or set of blocks lies in
the syntax tree.
It is also important to note that though I talk specifically about how
conversions allow users to combat the current limitations in App Inventor,
conversions are not necessary for the users mobile app to function correctly.
Specifically I want to point out that TAIL code need not be converted to AI2
blocks before running the app as the piece of TAIL code carries the exact
semantics of the AI2 code it represents.
All of what is mentioned above is possible largely due to the fact that
TAIL and AI2 blocks are isomorphic languages. Formally this means that
there is a bijection between the languages. Informally, this directly implies
that round trip conversions between blocks and text (i.e. from TAIL to AI2
blocks to TAIL, or from AI2 blocks to TAIL to AI2 blocks) will yield the
original code (whether it is TAIL code or AI2 blocks code) that began the
conversion cycle. Language isomorphism is the major design principle that
guides all of the decisions regarding details of the TAIL syntax.
Thus, there are a few important benefits of language isomorphism.
Users cannot express functionality in TAIL that they cannot express in
39
AI2 blocks, and they cannot express functionality in AI2 that they cannot
express in TAIL. This allows users to program AI2 code solely in TAIL.
Thus, users can write a TAIL program to specify an Android app. Users do
not need to convert their TAIL programs back into AI2 blocks in order to
create a working app. Thus the issue found in the Code to Blocks project
(Section 2.7) of users being able to express things in the Python language
that hold no meaning with regards to programming an App Inventor app, is
eliminated with an isomorphic text language.
The round-trip conversion allows users (especially novice programmers)
to help transition from the blocks programming language to textual program-
ming languages because users can write TAIL text and revert back to blocks
to see the notation they are more familiar and comfortable with. Users can
also convert a set of blocks to text to check what the TAIL for a given set of
blocks would look like.
Finally, language isomorphism allows for creating programs that combine
both the text and blocks syntaxes. Instead of not being able to translate
a program into text until the program is complete, with a language that is
isomorphic to the AI2 blocks language, users can translate individual or sets
of fragments of their programs into TAIL. Each AI2 block can be translated
into TAIL individually or sets of blocks can be translated into TAIL as a
whole. Thus, users can choose which parts of their programs they want
to express in TAIL and which parts they want to express in AI2 blocks.
As an example, users can choose to convert certain sub-blocks to TAIL for
notational brevity (e.g. the quadratic formula). In other situations, users
can choose to keep the blocks notation where they prefer it (e.g. to have a
40
visual reminder of the number of arguments that a block takes).
41
Please note that some of these TAIL examples use the symbol {$}. This
symbol is not valid TAIL. Instead, this symbol is a placeholder, for the pur-
pose of these examples, for a valid TAIL expression which should replace
the {$}. Without a valid TAIL expression in place of {$}, the entire TAIL
fragment is invalid (see discussion in Section 5.3.3 about representing incom-
plete AI2 blocks in TAIL). Additionally, note that I have not created a place
holder for empty statement sequences. This is because the empty statement
sequence is a valid statement sequence in AI2 and consequently TAIL.
Expressions
Expression blocks (see sample expression blocks in Figure 3.1) are blocks
with plugs (shown in Figure 3.2a), which indicate that the block outputs a
value. Any AI2 block can have any number of sockets (shown in Figure 3.2b
and Figure 3.2c). A socket on a block indicates that this block expects a
value as input. The shapes of the plugs and sockets indicate that the two
blocks should fit together. Note that there are some sockets that expect a
certain type of input, but this type is not represented with a different plug
shape, so blocks with plugs of the incorrect type will bounce away from the
socket when trying to connect the two, to indicate that these two blocks
cannot be connected together.
TAIL expressions are represented using curly braces. (Figure 3.3)
Note that the left curly brace { is visually similar to the plug on AI2
expression blocks.
42
Figure 3.1: AI2 Expression Blocks
Statements
Statement blocks (see sample statement blocks in Figure 3.4) are blocks
that compose vertically with each other to form a sequence of statements.
The vertical composition of the blocks is indicated by nubs and notches
(Figure 3.5) on the top and bottom of the blocks. Nubs and notches are
referred to as previous connector and next connector in AI2 jargon.
TAIL statements are represented using square brackets.
(Figure 3.6)
43
{0} { not { $ }} { sqrt { $ }} { length { $ }}
{ list }
Declarations
Top level declarations blocks (Figure 3.7) such as global variable declarations,
event handlers, and procedure or function declarations, do not have any
44
(a) A Nub (b) A Nub Indicating (c) A Notch
Expected Statement
Sequence Inside Statement
45
Figure 3.7: AI2 Top Level Declaration Blocks
Given an AI2 block, the corresponding TAIL code will have the
same title as the AI2 block where any spaces in the title of the block
are replaced with underscores.
An example of a title on a block is initialize global on the global
variable declaration block, or for each on the for loop block. These titles
46
( when Button1 . Click do : )
47
I will conclude this section by noting that part of the reason that devel-
opment on Venthon (see Section 2.6.3) was put on hold was that there were
more design details to consider in Venthon because the syntax was not as
systematic (cannot be described as concisely in a few rules) as the TAIL syn-
tax, which systematically follows from visual information on the AI2 blocks.
Thus, even though Venthon uses fewer syntactic markers, the syntactic struc-
ture of Venthon programs is more different from the syntactic structure of
AI2 blocks.
48
Chapter 4
In Chapter 3 I discuss TAIL language design (i.e. why TAIL looks the way
it looks). In this chapter I outline how I integrated TAIL into App Inventor.
This chapter discusses a wide variety of topics from general architecture of
my project, to design details, to my specific implementation.
4.1 Architecture
The AI2 blocks language and TAIL are just two different representations
of the same abstract syntax tree for representing AI2 programs. In addi-
tion to these two representations, there are two more underlying represen-
tations that must be mentioned. AI2 blocks have an underlying XML tree
49
representation where each AI2 block is an XML block element denoted by
<block>...</block>. Underlying the AI2 blocks is another representation,
YAIL (Young Android Intermediate Language) which carries the semantics
of the blocks and is the stepping stone towards generating executable code
(representing the AI2 program) to run on the Android device. These four
representations (an example depicted in Figures 4.1 to 4.4, AI2 blocks, XML,
YAIL, and TAIL are all varying representations of the same syntax tree.
As noted in Section 1.4, I not only create a new language, TAIL, but I also
provide a means for AI2 users to convert from AI2 blocks to TAIL code and
from TAIL code to AI2 blocks. I have accomplished this by adding a new
set of blocks (code blocks) to AI2 (Figure 1.6).
There are three code blocks, one for each of the three different types
program fragments in AI2 (discussed in Section 3.2.1): expression blocks,
statement blocks, and top level declaration blocks.
These new code blocks allow AI2 users to specify TAIL code within the
50
Figure 4.2: XML representation of syntax tree
51
Figure 4.3: YAIL representation of syntax tree (determines the code that is
executed on the Android device)
the type of block it is. This means that the TAIL expression code block must
contain a valid TAIL expression, the TAIL statement code block must contain
a valid TAIL statement, but if the TAIL expression code block contains a
52
valid TAIL statement, the TAIL expression code block is still invalid). If
the edited code block is valid, then this code block carries the semantics
of the AI2 block(s) it represents and can be used just as the corresponding
AI2 block(s) would be used when running the mobile app on an Android
device. Thus users need not convert back to blocks in order to run the app.
Note that since the valid TAIL code block carries the semantics of the AI2
blocks it represents, the TAIL code blocks are shaped like the types of AI2
program fragments they represent. The TAIL expression code block is itself
an expression block because it has a plug. The TAIL statement code block is
itself a statement block because it has nubs and notches. The TAIL top level
declaration code block is itself a top level declaration block because it does
not have any external connectors such as plugs, sockets, nubs, or notches.
See Figure 4.5 for a valid TAIL expression code block.
If the resulting edited code block contains invalid TAIL code, meaning
that the user either specified incorrect TAIL or the wrong type of TAIL
fragment for the specific code block (i.e. the user entered a valid TAIL
expression inside a TAIL statement code block), the code block will display
an error icon (with a corresponding error message), and the code block will
carry the semantics of an AI2 error. See Figure 4.6 for an invalid TAIL code
block.
In addition to the ability to specify TAIL code inside these code blocks
53
Figure 4.6: An invalid TAIL expression code block
and use a TAIL code block as is, users can convert any valid TAIL code block
to the AI2 block or set of blocks it represents.
Section 3.1.1 talks in depth about a two way conversion between the AI2
blocks and TAIL text. The blocks to text conversion proves to be much
simpler than the text to blocks conversion. In order to convert AI2 blocks to
the TAIL code blocks with the appropriate TAIL text, I created a blocks to
text converter. This converter is specified in a JavaScript file inside the AI2
code base.
As mentioned in Section 4.1.1, AI2 blocks have an underlying XML rep-
resentation which is based on their high-level descriptors in the JavaScript
based blocks editor portion of AI2 code. AI2 provides useful functions for
translating a given block or set of blocks to its XML DOM and translating
a given XML DOM for a block or set of blocks to the block/set of blocks
the XML DOM represents. As briefly noted in Section 2.6.3, I use this XML
representation of the blocks as the stepping stone for the blocks and text con-
versions. Thus, instead of having to convert the SVG graphics of the block
to TAIL code, I can just use AI2s built-in XML DOM conversion function
54
to translate a given set of blocks into its underlying XML, and then translate
this XML to TAIL code.
The blocks to text converter first translates a given set of blocks to its
XML DOM. Recall that an XML DOM is actually just a tree, as is any given
program (all programs can be viewed in terms of their abstract syntax tree).
The blocks to text converter walks down the XML Tree starting at the root el-
ement. In AI2s underlying XML representation, the root element will always
be a block element denoted with a block XML tag (<block>...</block>).
As the converter walks down the XML tree, it accumulates a string, convert-
ing each part of the XML tree to a substring representing the part of the
TAIL abstract syntax tree that is equivalent to this part of the XML tree.
By the time the converter has walked down to the end of the tree, the entire
XML tree has been converted into the corresponding TAIL code.
After accumulating the entire string with the TAIL code, the blocks to
text converter creates an XML element representing a TAIL code block (dis-
cussed in Section 4.1.2) with this TAIL code inside its edited label. The
resulting XML element can then be converted into the visual TAIL code
block (containing the new, valid TAIL code) using the built-in AI2 XML to
blocks conversion function.
An App Inventor user can convert a set of AI2 blocks using the context
menu on any given block in the AI2 Blocks Editor workspace. The context
menu for any AI2 block will have a Convert to TAIL option which when
clicked will replace the given block any blocks nested inside of it into a
corresponding TAIL code block. It is important to note that the Convert to
TAIL context menu option will be un-clickable if the AI2 block has any empty
55
sockets as empty sockets are errors in AI2 programs (not in the workspace
itself, but rather in a valid program). In my current implementation, any
errors must be corrected before being able to convert AI2 blocks to TAIL
code blocks. However, it is important to allow users to convert between
the notations as they are programming. This means that I must implement
incomplete blocks (i.e. any blocks with empty sockets) in TAIL. Adding
incomplete blocks to TAIL will allow users to convert between blocks and
text as they are in the midst of programming. For example, if a user starts
off programming in the AI2 blocks language, and in the midst of plugging
blocks together, the user realizes he/she needs to create a long arithmetic
expression that he/she would prefer to write in text, the user should be able
to convert the entire construction to TAIL and type the rest in TAIL (note
that the user can also plug in a TAIL expression code block and type the
TAIL for the arithmetic expression inside this code block).
Allowing incomplete blocks to be represented in TAIL is also necessary
for converting entire workspaces and projects into TAIL. This is discussed in
Section 5.3.3.
Any given AI2 block has the option to convert to TAIL. This option converts
the given AI2 block into a corresponding TAIL code block. The TAIL code
blocks I have added to the AI2 blocks language are themselves AI2 blocks;
thus, TAIL code blocks can also be converted into TAIL. App Inventor users
can have valid, nested TAIL code blocks (Figure 4.7) which are semantically
equivalent to the AI2 blocks represented by the TAIL code block that is most
56
deeply nested. The blocks in Figure 4.7, Figure 4.8, and Figure 4.9 are all
semantically equivalent.
Figure 4.8: TAIL code block at the deepest level of nesting from Figure 4.7
Figure 4.9: AI2 block represented by TAIL code block in Figure 4.8
Conversion from text to blocks proves to be much more complicated than the
conversion in the opposite direction. The following section offers a high-level
description of the three major steps involved in creating TAIL and converting
pieces of TAIL code into blocks.
Step 1: Lexing
57
this function is called a lexer. A lexer takes as its input a string of text of the
entire program. The lexer then breaks up the text of the program into tokens,
the smallest, meaningful units of the programming language. These lexical
tokens often include (but are not limited to) identifiers, strings, whitespace,
syntactic markers (such as parens, semi colons, brackets, etc.), numbers, and
keywords of the language. The lexer will use rules specified in the lexical
grammar (a specification of the lexical syntax) to break up the tokens. The
TAIL lexer is described in detail in Section 4.2.2.
Step 2: Parsing
58
within a Python program. This was a non-trivial component of the parser
for Venthon (which, like Python, also uses whitespace to denote block struc-
ture/scope of a Venthon program), however, whitespace tokens can be thrown
out in the context of parsing TAIL.
The TAIL parser is described in detail in Section 4.2.3.
The final step to take in the context of converting the fully parsed, valid TAIL
program into AI2 blocks is to convert the abstract syntax tree generated by
the parser into the blocks. Section 4.1.3 talks about the underlying XML
representation of the blocks. AI2 has built-in functions to convert an XML
tree (with the valid syntax for the XML representation) to the AI2 blocks it
represents.
I translate the abstract syntax tree generated by the TAIL Parser (dis-
cussed in Section 4.2.3) to the underlying XML representation of the AI2
blocks the parsed TAIL code represents.
To put the tree conversion in the context of the TAIL code blocks (de-
scribed in Section 4.1.2), each TAIL code block is running the TAIL lexer
and parser and parsing (which includes lexing) the text inside the editable
text box on the TAIL code block. If the text inside the editable text box is
invalid TAIL code, a red error icon appears on the TAIL code block. When
clicked on, the red error icon reveals the first parse error for the invalid TAIL
text. When all errors have been corrected and the TAIL code block has cor-
rect TAIL code (in the appropriate TAIL code block), the user can click on
the context menu of the block which has two new options: the Convert to
59
TAIL option, mentioned in Section 4.1.3, and a Convert to Blocks option.
The Convert to Blocks option performs the tree conversion.
The tree conversion is described in more detail in Section 4.2.4.
60
ANTLR grammars have a number of different options that users can set
to obtain different results. One of the most important options the user can
specify is the target language in which ANTLR should generate the parser
and lexer. The target language for the TAIL grammar is JavaScript just as
the rest of the AI2 blocks editor code is in JavaScript.
As an important note, the most recent versions of ANTLR (ANTLR
v.3.4-v.3.5 and ANTLR 4) do not have a working JavaScript target, so I
used ANTLR v.3.3 in this project, the most recent version of ANTLR that
has a working JavaScript target.
I use ANTLR to generate the TAIL lexer by specifying lexer rules for my
TAIL grammar. There are three different kinds of lexer rules. There are
simple lexer rules (Figure 4.10) that specify tokens that are string literals
(examples of such tokens are syntactic markers or keywords in the language).
61
There are complex lexer rules (Figure 4.11) that use a regular expression
syntax to match text. Each instance of these two kinds of lexer rules is a
specification of a different lexical token.
ANTLR grammar files consist of both lexer and parser rules. Parser rules
are similar to lexer rules. Instead of specifying tokens, each parser rule
specifies part of the syntactic structure of a TAIL program. The portion of
the grammar consisting solely of parser rules is a context-free grammar. The
ANTLR generated TAIL parser will follow the grammar rules, starting at
a top level rule (whatever is calling the parser can specify which top level
parser rule to begin with), and following sub-rules, until each the sub-rule
reaches all terminal rules. A terminal rule is a parser rule which matches
62
only lexer rules, thus ending the rule tree. Figures 4.13 to 4.16 depicts some
sample parser rules.
Figure 4.13: The top level parser rule for TAIL expressions
63
Figure 4.16: The terminal parser rule
Once the input TAIL text has been parsed (and if the parser has determined
that it is valid TAIL code), the output abstract syntax tree is converted
into the underlying XML representation of the AI2 blocks corresponding
to the TAIL text. I accomplish this tree conversion simultaneously as the
parser generates the abstract syntax tree using ANTLR actions. ANLTR
grammars have the additional feature of allowing the user to specify bits
of executable code (in the target language for the grammar, JavaScript in
this case) throughout the different grammar rules. These bits of code, called
actions, are executed as the parser matches the tokens in the token stream
provided by the lexer to the parser rules in the grammar. The ANTLR
actions can be interspersed through out a rule to be executed at different
points of the rule matching.
Figure 4.17 depicts the ANTLR parser rule responsible for matching the
64
atom rule from Figure 4.16 with actions included.
65
Figure 4.17: The terminal parser rule atom with ANTLR actions embedded
66
Chapter 5
67
5.2 Immediate Future
There is quite a bit of work that remains to be done before TAIL can be
integrated into a future official release of AI2. Part of this work is discussed
in this section; this is work that can be completed in the short term future (a
few weeks). The rest of this work is discussed in the following section; this
is work that can be completed in the slightly longer term (1-3 months)).
Although many of the AI2 blocks can be expressed in TAIL, not all AI2
blocks have been added to the TAIL language. Currently most individual
AI2 blocks are accounted for in their own parser grammar rules. There
are a few exceptions (e.g. arithmetic expressions, event handlers, compo-
nent methods) in which a single parser grammar rule accounts for many AI2
blocks. For example all AI2 event handlers can be expressed in TAIL using
only a single parser grammar rule. All event handler blocks have a syntactic
structure which can be abstracted into one high level rule. Other than the
few exceptions in which one rule can express multiple AI2 blocks, most AI2
blocks must be accounted for in their own specific parser grammar rules.
Examples of such blocks are depicted in Figure 5.1.
It is necessary to add all of the AI2 blocks to the TAIL grammar. This
will currently require adding an individual rule for the all of the blocks that
TAIL does not currently support.
The process of adding an AI2 block to the TAIL grammar is straightfor-
ward because the TAIL grammar is designed to make the translation from
68
Figure 5.1: Examples of blocks that require individual grammar rules
AI2 blocks to TAIL syntax as easy as possible using the simple language de-
sign rules outlined in Section 3.2.1. However, though this process is straight-
forward, it is very tedious because the developer must not only create a new
TAIL parser rule pertaining to the block, but also add the ANTLR actions
to translate the TAIL text into the AI2 blocks underlying XML representa-
tion (as described in Section 4.2.3 and Section 4.2.4). Section 5.3.5 discusses
the possibility of creating some sort of abstraction to simplify the process of
adding an AI2 block to the TAIL grammar.
In addition to adding the rest of the AI2 blocks to the TAIL grammar,
I want to allow for the conversion of on-block comments. AI2 allows users
to add comments to individual blocks (via the context menu for the block).
These comments appear as an icon next to the blocks title with a ? on it
(Figure 5.2).
When this comment icon is clicked, there is a speech bubble (similar to
that for block errors), with an editable text area in which the user can type
some comments about the block. These comments can easily be incorporated
69
Figure 5.2: Block with comment
into the TAIL language so that they are not lost during blocks to text con-
versions. Figure 5.3 gives an example of what the block in Figure 5.2 might
look like in TAIL, with the comments preserved.
TAIL code blocks carry the semantics of the AI2 blocks they represent. How-
ever, the semantics of TAIL code blocks has so far only been implemented for
the TAIL expression code block. Support for the TAIL statement and decla-
ration code blocks has yet to be added. The implementation of the semantics
(i.e. the YAIL generators) for these remaining code blocks is straightforward
and is similar to the implementation of the semantics for the TAIL expression
code block.
70
5.3 Near Future
As soon as a large enough subset of the most popular AI2 blocks have been
added to TAIL, it is very important that I do at least some preliminary
user testing for different user groups (novice programmers unfamiliar with
blocks programming languages, novice programers with block programming
experience, and more experienced programmers who use AI2).
Preliminary user testing could prove to be very useful in testing details
of the TAIL language before it becomes available to the App Inventor com-
munity. Additionally, it is important and will prove to be very useful to test
whether AI2 users are inclined to start using TAIL to program in AI2. If
there is an inclination to use TAIL, at what stage they are most likely to find
the TAIL code capabilities useful (if at all). Is the TAIL syntax intuitive,
systematic, and predictable as I hope? Is AI2 easier or more difficult to use
with the addition of the text language?
In addition to questions about the language itself, it is also important to
test whether the interface of having additional code blocks in AI2 is useful
or if it is clunky and not likely to be used. However, this is something that
preliminary user testing will not provide, but will require measuring over
time. Thus, it might be useful to have AI2 record when people use the new
text features to see how popular they are.
71
5.3.2 Changing Conversion Architecture to Improve Per-
formance
The current implementation of the TAIL code blocks runs the TAIL parser
constantly, in a loop, when the user connects his/her AI2 project to the An-
droid device for testing. Additionally, the set of AI2 blocks corresponding to
the TAIL code in the TAIL code block are also generated (invisibly), unbe-
knownst to the AI2 user. It is these invisible blocks that are then executed in
when the app is being run on the Android device. This is horribly inefficient
because the parser will execute hundreds of lines of code in order to parse the
TAIL code, and the more complicated the TAIL code, the more parser code
will be executed. This performance issue can easily be improved by changing
the architecture of the implementation slightly. Instead of constantly gener-
ating the AI2 blocks for a given TAIL code block when the app is running
on the Android device, the corresponding AI2 blocks can be invisibly present
at all times, linked to the TAIL code block. App Inventor allows invisible
disabled blocks to be on the workspace. These blocks are skipped at runtime,
and they are invisible to the user.
When a user opts to convert a TAIL code block to AI2 blocks, the hidden
AI2 blocks are simply made visible, and the visible TAIL code block is now
hidden from the user. When the user needs to execute a TAIL code block,
instead of having to run the parser and invisibly generate the set of AI2
blocks to execute in place of the TAIL code block, the YAIL generator for
the TAIL code block can simply run the YAIL generator for the hidden AI2
block that already exists. The invisible AI2 blocks should be updated if the
TAIL code inside the code block has changed. Thus, it is also important to
72
discern when the user has finished typing, because it is impractical to try
and generate the corresponding hidden AI2 block(s) for every character by
character change in the TAIL text. Additional improvement can be made by
tracking which sub-blocks of the hidden AI2 blocks are affected by the change
in the TAIL code, and only converting those sub-blocks that are related to
the most recent change.
The blocks to text conversion can also be improved. Using a process
similar to the one described above, linking pre-generated invisible TAIL code
blocks to existing AI2 blocks, does not add much efficiency, because the
blocks to text conversion process is already very efficient. However, when
converting from blocks to text, it would be more efficient to remember the
set of blocks, so that if no changes have been made to the blocks, the blocks
to text converter need not run again, instead, a cached copy of the text can
be returned.
Making this change will greatly improve the functionality and perfor-
mance of the implementation of the conversion between blocks and text.
Additionally, the improvement mentioned for the text to blocks conversion
fixes a current issue with my implementation.
73
Converting Screens
Converting Workspaces
74
as well as any other important information that may be lost in the conversion
process.
Converting Projects
Converting entire projects is not only limited to the conversion of the AI2
blocks and information from the AI2 blocks editor. This conversion also needs
to keep track of information from the AI2 designer such as which components
have been added to which screens, their arrangement on the screen, and all
of the information associated with these components (if the user has edited
the default initialization of the fields associated with any components). Thus
it is also important to consider how components and associated information
should be translated into TAIL code.
75
text area field for use within the TAIL code blocks. Additionally, it will be
necessary to pretty print (format) the TAIL text that is the result of a blocks
to text conversion.
Section 5.4.2 discusses the idea of creating a text editor for TAIL for a
larger, more long-term project.
Because each AI2 block has its own block type, and each AI2 blocks un-
derlying XML is structured a bit differently, trying to conform the TAIL
representations of these AI2 blocks to general parser rules is not ideal. How-
ever, it is strongly worth considering whether it would be feasible and useful
to create a table of unique identifiers for AI2 blocks (whether this identifier
is the blocks title or some other information on the block such as number
of arguments etc.) and the components and general structural requirements
of the underlying XML representation of the block (block type attribute,
mutations, etc.).
76
Original TAIL Abbreviation
{{12} + {{5} * {3}}} {12 + 5 * 3}
{42 * { get x }} {42 * x }
77
5.3.7 Improving Error Handling
The TAIL code blocks produce an error icon whenever there is an error in
the TAIL code inside the editable label of the block. When clicked, the error
icon displays an error message generated directly from ANTLR. In the case
of invalid component, component event, component property, or component
field names, I have specified my own error messages.
It is important to make sure that error messages (whether the ones I have
specified or the ones generated by ANTLR) are actually helpful to the user.
It is best to test this with user studies.
Before this work can be integrated into an official AI2 release, it is necessary
to create tutorials and pages of documentation for both AI2 users and devel-
opers detailing all the specifics of TAIL as well as how to use the additional
AI2 code blocks. Of course, all of the code and functionality needs to be very
well tested and will go through iterations of code reviews as well.
78
5.4.1 Venthon, Venti, and More!
Section 2.6.3 describes work I did with a fellow student on Venthon, a textual
programming language for App Inventor with Python-esque syntax. This
work remains incomplete, but would be interesting to finish. It would be
great to offer multiple syntaxes for users to choose from. Venti, (App Inven-
tor + Java) is another possibility. Java, in addition to Python, is another
programming language that is often taught to novices. Venti might prove to
be a helpful stepping stone between App Inventor and the Android SDK for
the users who wish to use some of the features of the Android SDK that are
not available in App Inventor.
Now that I have explored the relationship between blocks programming lan-
guages and textual programming languages by creating a textual language
for an existing blocks language, it might be interesting to approach the rela-
tionship the other way around. What would a blocks language for an existing
textual language such as Python look like? It could be helpful if there were
79
an isomorphism between Python and a new blocks language, and such a
blocks language could be used to introduce novices to programming concepts
in an introductory Computer Science class, with the plans to transition to
the isomorphic textual language a bit later in the course.
This project goes hand-in-hand with the project idea outlined in the previ-
ous section. It would be interesting to have an environment in which two
isomorphic languages (a blocks language and a text language) can be edited
side by side. If the edits on one side were reflected in the code on the oppo-
site side in real time, users of such an environment could potentially benefit
from understanding how exactly the two languages relate. It could also help
novice programmers transition from blocks languages to text languages more
easily.
80
Bibliography
[Ai1a] MIT Center for Mobile Learning, App Inventor Classic home page,
http://explore.appinventor.mit.edu/classic, accessed Feb.
24, 2014.
[Ai2a] MIT Center for Mobile Learning, App Inventor 2 home page, http:
//appinventor.mit.edu, accessed Feb. 24, 2014.
81
[Bri] Java Bridge website, java . appinventor . org, accessed Apr 24,
2014.
[Phia] Philip Guo, Proposal to render Android App Inventor visual code
blocks as pseudo-Python code, http://people.csail.mit.edu/
pgbovine/android_to_python/, accessed Apr 24, 2014.
[Phib] Philip Guo, Online demo of Android App Inventor yail to pseudo-
Python renderer, http : / / people . csail . mit . edu / pgbovine /
android_to_python/yail_to_python_demo.html, accessed Apr
24, 2014.
82
[Pic] The Playful Invention Company, PicoCricket Reference Guide, ver-
sion 1.2a, http : / / www . picocricket . com / pdfs / Reference _
Guide_V1_2a.pdf, accessed Mar. 22, 2012.
[Scrb] Marina Myburgh, Printing the scripts for a Scratch program, http:
//itisgr8.blogspot.com/2012/01/printing- scripts- for-
scratch-program.html, accessed Apr 24, 2014.
[Scrc] Scratch Wiki, Block Plugin, http : / / wiki . scratch . mit . edu /
wiki/Block_Plugin_(2.0), accessed Apr 24, 2014.
[SH] Chazz Sims and Jimmy Hernandez. Code to Block Interface Com-
ponent Extension. Course 6.s063, Final project, https://github.
com/JDub20/CodeBlocks?source=c, accessed Apr 24, 2014.
83
[Stab] StarLogo Nova project, MIT Scheller Teacher Education Program,
http://education.mit.edu/projects/starlogo-nova, accessed
Apr 24, 2014.
[Tan] Charlene Lee and Sonali Sastry, Tanner Connect App, Wellesley
College Computer Science Department introductory CS course, Fall
2011. https://sites.google.com/site/cs117charleneandsonali/,
accessed Apr 24, 2014.
84
TAIL Grammar
grammar TAIL ;
options {
language = JavaScript ;
backtrack = true ;
}
tokens {
LSQUARE = '[ ';
RSQUARE = '] ';
LCURLY = '{ ';
RCURLY = '} ';
LPAREN = '( ';
RPAREN = ') ';
LANGLE = ' < ';
RANGLE = ' > ';
DOT = '. ';
COMMA = ' , ';
// Known Keywords
TRUE = ' true ';
FALSE = ' false ';
WHEN = ' when ';
IF = 'if ';
THEN = ' then : ';
ELSE = ' else : ';
ELSE_IF = ' else_if : ';
FOR_EACH = ' for_each ';
DO = ' do : ';
RESULT = ' result : ';
85
TO = 'to ';
LABEL_TO = ' to : ';
CALL = ' call ';
INIT_GLOBAL_VAR = ' initialize_global ';
INIT_LOCAL_VAR = ' initialize_local ';
GET = 'get ';
SET = 'set ';
GLOBAL = ' global ';
IN = ' in : ';
// operators
NOT = 'not ';
AND = 'and ';
OR = 'or ';
LEQ = ' <= ';
GEQ = ' >= ';
LOGIC_EQ = ' equals ';
LOGIC_NOT_EQ = ' not_equals ';
EQ = '= ';
NOT_EQ = '!= ';
// Trig Ops
SIN = ' sin ';
COS = ' cos ';
TAN = ' tan ';
86
ASIN = ' asin ';
ACOS = ' acos ';
ATAN = ' atan ';
// Colors
COLOR = ' color ';
MAKE_COLOR = ' make_color ';
BLACK = ' black ';
BLUE = ' blue ';
WHITE = ' white ';
MAGENTA = ' magenta ';
RED = 'red ';
LIGHT_GRAY = ' light_gray ';
PINK = ' pink ';
GRAY = ' gray ';
ORANGE = ' orange ';
DARK_GRAY = ' dark_gray ';
YELLOW = ' yellow ';
GREEN = ' green ';
CYAN = ' cyan ';
// lists
MAKE_LIST = ' make_a_list ';
LIST = ' list ';
@lexer :: members {
var errors = [];
TAILLexer . prototype . emitErrorMessage = function ( error ) {
// var hdr = getErrorHeader ( e ) ;
// var msg = getErrorMessage (e , tokenNames ) ;
87
errors . push ( error ) ;
}
TAILLexer . prototype . getErrors = function () {
return errors ;
}
}
@members {
var errors = [];
TAILParser . prototype . emitErrorMessage = function ( error )
{
// var hdr = getErrorHeader ( e ) ;
// var msg = getErrorMessage (e , tokenNames ) ;
errors . push ( error ) ;
};
TAILParser . prototype . getErrors = function () {
return errors ;
};
TAILParser . prototype . r ec ov er F ro mM is m at ch ed T ok en =
function ( input , ttype , follow ) {
throw new
org . antlr . runtime . Mis mat ched Toke nExc ept ion ( ttype ,
input ) ;
}
88
};
TAILParser . prototype . is Va lid Co mpo ne nt Fie ld Na me =
function ( fields , componentType , fieldName ) {
// I am using " field " as a general name for event ,
property or method
// fields should be of the form " events " , " properties " ,
or " methods "
var componentInfo =
Blockly . ComponentTypes [ componentType ]. componentInfo ;
var componentFields = componentInfo [ fields ];
for ( var i = 0; i < componentFields . length ; i ++) {
if ( componentFields [ i ]. name === fieldName ) {
return true ;
}
}
return false ;
};
}
@rulecatch {
catch ( re ) {
throw re ;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* PARSER RULES
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
// program
// : ( eventHandler | procdef | funcdef |
globalvar_decl ) *;
// eventHandler
// : LPAREN WHEN dotted_name ( var_decl ) ? KEYWORD
( statement_block ) * RPAREN ;
// procdef
89
// : LPAREN TO IDENTIFIER DO ( statement_block ) * RPAREN ;
// should a valid program allow empty procdefs and
event handlers ?
// funcdef
// : LPAREN TO IDENTIFIER RESULT expression_block
RPAREN ;
// globalvar_decl
// : LPAREN INIT_VAR var_decl ' to : ' expression_block ;
// var_decl
// : LANGLE IDENTIFIER RANGLE ;
90
var title = document . createElement (" title ") ;
title . setAttribute (" name " ," NAME ") ;
var value = document . createElement (" value ") ;
value . setAttribute (" name " ," VALUE ") ;
}
: INIT_GLOBAL_VAR LANGLE IDENTIFIER RANGLE LABEL_TO
expression_block
{
title . innerHTML = $IDENTIFIER . text ;
value . appendChild ( $expression_block . elt ) ;
$elt . appendChild ( title ) ;
$elt . appendChild ( value ) ;
}
;
91
argsCount ++;
}) *
DO suite {
if ( hasMutations ) {
$elt . appendChild ( mutation ) ;
}
$elt . appendChild ( name ) ;
for ( var i =0; i < var_title_arr . length ; i ++) {
$elt . appendChild ( var_title_arr [ i ]) ;
}
var seq = $suite . elt ;
seq . setAttribute (" name " ," STACK ") ;
$elt . appendChild ( seq ) ;
}
;
92
var var_title = document . createElement (" title ") ;
var_title . setAttribute (" name " ," VAR "+ argsCount ) ;
var_title . innerHTML = $arg_name . text ;
var_title_arr . push ( var_title ) ;
argsCount ++;
}) *
RESULT expression_block {
if ( hasMutations ) {
$elt . appendChild ( mutation ) ;
}
$elt . appendChild ( name ) ;
for ( var i =0; i < var_title_arr . length ; i ++) {
$elt . appendChild ( var_title_arr [ i ]) ;
}
value . appendChild ( $expression_block . elt ) ;
$elt . appendChild ( value ) ;
}
;
93
componentType =
Blockly . Component . instanceNameToTypeName ( componentName ) ;
mutation . setAttribute (" component_type " ,
componentType ) ;
mutation . setAttribute (" instance_name " ,
componentName ) ;
title . innerHTML = componentName ;
} else {
throw new TAILException (" Invalid component name : "
+ componentName ) ;
// this . emitErrorMessage (" Invalid component name : "
+ componentName ) ;
// the parser will continue even after this error
because syntactically this is still correct ...
}
if ( this . is Val id Com po ne ntF ie ld Nam e (" events " ,
componentType , eventName ) ) {
mutation . setAttribute (" event_name " , eventName ) ;
} else {
throw new TAILException (" Invalid event name : " +
eventName ) ;
// this . emitErrorMessage (" Invalid event name : " +
eventName ) ;
} // no need for else case , we ' ve already added an
error to the errors array above
}
( LANGLE arg = IDENTIFIER RANGLE ) * // apparently we don ' t
have to put these in the DOM ....
DO suite
{
var statements = $suite . elt ;
statements . setAttribute (" name " ," DO ") ;
94
expression_start returns [ var xml ]
@init {
$xml = document . createElement (" block ") ;
}
: expression_block
{ $xml . appendChild ( $expression_block . elt ) ;}
;
95
// {
// if ( count === 0) { // this is the very first
statement
// prevStatementBlock = $statement_block . elt ;
// $elt . appendChild ( prevStatementBlock ) ;
// } else { // all of the rest of the statement blocks
// var next = document . createElement (" next ") ;
// var currentStmt = $statement_block . elt ;
// next . appendChild ( currentStmt ) ;
// prevStatementBlock . appendChild ( next ) ;
// prevStatementBlock = currentStmt ;
// }
// count ++;
// }
// TODO : stuff needs to go here ... actiony things ...
// {
// for ( var i = 0; i < stmt_arr . length ; i ++) {
// currentStatementBlock = stmt_arr [ i ];
// if ( i ===0) {
// $elt . appendChild ( currentStatementBlock ) ;
// prevStatementBlock = currentStatementBlock ;
// } else {
// var next = document . createElement (" next ") ;
// next . appendChild ( currentStatementBlock ) ;
//
prevStatementBlock . appendChild ( currentStatementBlock ) ;
// prevStatementBlock = currentStatementBlock ;
// }
// }
// }
; // think about requiring newlines
96
: if_stmt { $elt = $if_stmt . elt ;}
| variable_set_stmt { $elt = $variable_set_stmt . elt ;}
| component_stmt { $elt = $component_stmt . elt ;}
| tail_stmt { $elt = $tail_stmt . elt ;}
;
97
}) * ( ELSE c = suite {
mutations = true ;
else_count ++;
98
title . innerHTML = var_name ;
$elt . appendChild ( title ) ;
var componentType ;
if ( isGeneric ) {
if (! Blockly . ComponentTypes . haveType ( componentName ) ) {
throw new TAILException (" Invalid Generic
Component Name : " + componentName ) ;
} else {
componentType = componentName ;
}
} else {
if (! this . isValidComponentName ( componentName ) ) {
throw new TAILException (" Invalid Component Name :
" + componentName ) ;
} else {
componentType =
Blockly . Component . instanceNameToTypeName ( componentName ) ;
99
}
}
if ( isGeneric ) {
var componentVal =
document . createElement (" value ") ;
componentVal . setAttribute (" name " ," COMPONENT ") ;
componentVal . appendChild ( $of_comp . elt ) ;
$elt . appendChild ( componentVal ) ;
}
100
CALL component = IDENTIFIER DOT method = IDENTIFIER
( FOR_COMPONENT for_comp = expression_block
{ isGeneric = true ;}) ?
( LABEL arg = expression_block {
var val = document . createElement (" value ") ;
val . setAttribute (" name " ," ARG "+ argCount ) ;
val . appendChild ( $arg . elt ) ;
valArr . push ( val ) ;
}) *{
$elt . setAttribute (" type " ," component_method ") ;
var componentType ;
if ( isGeneric ) {
if (! Blockly . ComponentTypes . haveType ( componentName ) ) {
throw new TAILException (" Invalid Generic
Component Name : " + componentName ) ;
} else {
componentType = componentName ;
}
} else {
if (! this . isValidComponentName ( componentName ) ) {
throw new TAILException (" Invalid Component Name :
" + componentName ) ;
} else {
componentType =
Blockly . Component . instanceNameToTypeName ( componentName ) ;
}
}
mutation . setAttribute (" component_type " , componentType ) ;
mutation . setAttribute (" is_generic " , isGeneric ) ;
if (! this . is Va li dCo mp on ent Fi el dNa me (" methods " ,
componentType , methodName ) ) {
throw new TAILException (" Invalid Component Method
Name : " + methodName ) ;
} else {
mutation . setAttribute (" method_name " , methodName ) ;
if (! isGeneric ) {
101
mutation . setAttribute (" instance_name " , componentName ) ;
$elt . appendChild ( mutation ) ;
102
: if_expr { $elt = $if_expr . elt ;}
//| do_result_expr { $elt = $do_result_expr . elt ;}
| logic_expr { $elt = $logic_expr . elt ;}
| not_expr { $elt = $not_expr . elt ;}
| compare_eq_expr { $elt = $compare_eq_expr . elt ;}
| compare_math_eq_expr { $elt =
$compare_math_eq_expr . elt ;}
| compare_math_expr { $elt = $compare_math_expr . elt ;}
| math_expr { $elt = $math_expr . elt ;}
| init_local_expr { $elt = $init_local_expr . elt ;}
| component_expr { $elt = $component_expr . elt ;}
| variable_ref_expr { $elt = $variable_ref_expr . elt ;}
| color_expr { $elt = $color_expr . elt ;}
| list_expr { $elt = $list_expr . elt ;}
| tail_expr { $elt = $tail_expr . elt ;}
| atom { $elt = $atom . elt ;}
;
// assign_stmt
// : SET IDENTIFIER ( ' to : ') ? expression_block ;
// if_stmt
// : IF expression_block THEN ( statement_block ) *
( ELSE_IF ( statement_block ) *) * ELSE ( statement_block ) *;
103
testVal . appendChild ( $a . elt ) ;
104
: a = expression_block
( AND { operation =" AND ";}| OR { operation =" OR ";})
b = expression_block
{
var title = document . createElement (" title ") ;
title . setAttribute (" name " ," OP ") ;
title . innerHTML = operation ;
105
@init {
$elt = document . createElement (" block ") ;
106
var operation = "";
}
: a = expression_block ( EQ { operation = " EQ ";} | NOT_EQ
{ operation = " NEQ ";}) b = expression_block
{
var title = document . createElement (" title ") ;
title . setAttribute (" name " ," OP ") ;
title . innerHTML = operation ;
107
{
var title = document . createElement (" title ") ;
title . setAttribute (" name " ," OP ") ;
title . innerHTML = operation ;
108
var valArr = [];
// var value ;
109
)+
{ $elt . setAttribute (" type " ," math_multiply ") ;}
)
{
mutation . setAttribute (" items " , itemCount ) ;
$elt . appendChild ( mutation ) ;
110
$elt . appendChild ( valA ) ;
$elt . appendChild ( valB ) ;
}
;
special_math_expr returns [ var elt ]
@init {
$elt = document . createElement (" block ") ;
$elt . setAttribute (" type " ," math_divide ") ;
$elt . setAttribute (" inline " ," true ") ;
111
$elt . setAttribute (" inline " ," false ") ;
}
: op =( SQRT { operation = " ROOT ";}
| ABS { operation = " ABS ";}
| SUBTRACT { operation = " NEG ";}
| LOG { operation = " LN ";}
| E_EXP { operation = " EXP ";}
| ROUND { operation = " ROUND ";}
| CEILING { operation = " CEILING ";}
| FLOOR { operation = " FLOOR ";})
expression_block
{
var title = document . createElement (" title ") ;
title . setAttribute (" name " ," OP ") ;
title . innerHTML = operation ;
112
| ACOS { operation =" ACOS ";}
| ATAN { operation =" ATAN ";})
expression_block
{
var title = document . createElement (" title ") ;
title . setAttribute (" name " ," OP ") ;
title . innerHTML = operation ;
113
value = document . createElement (" value ") ;
value . setAttribute (" name " , " DECL "+ count ) ;
value . appendChild ( $a . elt ) ;
valArr . push ( value ) ;
count ++;
}) +
{
$elt . appendChild ( mutation ) ;
titleArr . forEach ( function ( title ) {
$elt . appendChild ( title ) ;
}) ;
valArr . forEach ( function ( value ) {
$elt . appendChild ( value ) ;
}) ;
}
IN b = expression_block
{
var returnVal = document . createElement (" value ") ;
returnVal . setAttribute (" name " ," RETURN ") ;
returnVal . appendChild ( $b . elt ) ;
$elt . appendChild ( returnVal ) ;
}
;
114
title . innerHTML = variable ;
115
)
{
$elt . setAttribute (" type " , type ) ;
$elt . appendChild ( title ) ;
}
| HEX // custom_color
{
title . innerHTML = $HEX . text ;
$elt . setAttribute (" type " ," color_black ") ;
// because there ' s no other default color type
$elt . appendChild ( title ) ;
}
)
| MAKE_COLOR expression_block
{
$elt . setAttribute (" type " ," color_make_color ") ;
$elt . setAttribute (" inline " ," false ") ;
var value = document . createElement (" value ") ;
value . setAttribute (" name " ," COLORLIST ") ;
value . appendChild ( $expression_block . elt ) ;
$elt . appendChild ( value ) ;
}
;
116
val_block . setAttribute (" name " , (" ADD " +
( item_count -1) ) ) ;
val_block . appendChild ( $expression_block . elt ) ;
val_block_arr . push ( val_block ) ;
}
)*
{
mutation . setAttribute (" items " , item_count ) ;
$elt . appendChild ( mutation ) ;
val_block_arr . forEach ( function ( block ) {
$elt . appendChild ( block ) ;
}) ;
}
;
117
throw new TAILException (" Invalid Component Name : "
+ componentName ) ;
} else {
componentType =
Blockly . Component . instanceNameToTypeName ( componentName ) ;
}
118
componentType = componentName ;
}
} else {
if (! this . isValidComponentName ( componentName ) ) {
throw new TAILException (" Invalid Component Name :
" + componentName ) ;
} else {
componentType =
Blockly . Component . instanceNameToTypeName ( componentName ) ;
}
}
mutation . setAttribute (" component_type " , componentType ) ;
if (! isGeneric ) {
mutation . setAttribute (" instance_name " , componentName ) ;
var compSelectorTitle =
document . createElement (" title ") ;
compSelectorTitle . setAttribute (" name " ," COMPONENT_SELECTOR ") ;
compSelectorTitle . innerHTML = componentName ;
$elt . appendChild ( compSelectorTitle ) ;
}
if ( isComponentSetGet ) {
$elt . setAttribute (" type " ," component_set_get ") ;
mutation . setAttribute (" set_or_get " ," get ") ;
if (! this . i sVa li dC omp on ent Fi el dNa me (" properties " ,
componentType , propName ) ) {
throw new TAILException (" Invalid Component
Property Name : " + propName ) ;
} else {
mutation . setAttribute (" property_name " , propName ) ;
var title = document . createElement (" title ") ;
title . setAttribute (" name " ," PROP ") ;
title . innerHTML = propName ;
$elt . appendChild ( title ) ;
}
mutation . setAttribute (" is_generic " , isGeneric ) ;
} else {
$elt . setAttribute (" type " ," c omp on ent _c om pon en t_ blo ck ") ;
}
119
$elt . insertBefore ( mutation , $elt . firstElementChild ) ;
if ( isGeneric ) {
$elt . appendChild ( value ) ;
}
}
;
120
title . setAttribute (" name " ," TEXT ") ;
var text = $STRING . text ;
title . innerHTML = text . substring (1 , text . length -1) ;
$elt . appendChild ( title ) ;
}
| TRUE {
$elt . setAttribute (" type " ," logic_boolean ") ;
dotted_name
: IDENTIFIER '. ' IDENTIFIER ;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* LEXER RULES
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
fragment
ALPHA : ( 'a ' .. 'z ' | 'A ' .. 'Z ') ;
fragment
DIGIT : ( '0 ' .. '9 ') ;
fragment
ALPHA_NUM
: ALPHA | DIGIT
;
121
fragment
ESC
: '\\ ' .
;
STRING
: ( '\ ' ' ( ESC | ~( '\\ ' | '\n ' | '\ ' ') ) * '\ ' ')
| ( '" ' ( ESC | ~( '\\ ' | '\n ' | '" ') ) * '" ')
;
HEX
: '# ' ALPHA_NUM ALPHA_NUM ALPHA_NUM ALPHA_NUM
ALPHA_NUM ALPHA_NUM
;
WS : ( ' '
| '\t '
| '\r '
| '\n '
) { $channel = HIDDEN ;}
;
122
TAIL Code Blocks
/**
* Visual Blocks Language
*
* Copyright 2012 Google Inc .
* http :// code . google . com / p / blockly /
*
* Licensed under the Apache License , Version 2.0 ( the "
License ") ;
* you may not use this file except in compliance with
the License .
* You may obtain a copy of the License at
*
* http :// www . apache . org / licenses / LICENSE -2.0
*
* Unless required by applicable law or agreed to in
writing , software
* distributed under the License is distributed on an " AS
IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either
express or implied .
* See the License for the specific language governing
permissions and
* limitations under the License .
*/
/**
* @fileoverview Blocks for textual languages in App
Inventor
* @author kchadha@wellesley . edu ( Karishma Chadha )
123
*/
124
this . getTitle_ ( ' COLLAPSED_TEXT ') . setText (
textToDisplay , ' COLLAPSED_TEXT ') ;
},
codeCustomContextMenu : function ( options ) {
var myBlock = this ;
var convertToBlocksOption = { text : " Convert to Blocks
"};
if (! this . errorIcon && ! this . disabled ) {
convertToBlocksOption . enabled = true ;
} else {
convertToBlocksOption . enabled = false ;
}
convertToBlocksOption . callback = function () {
Blockly . Language . code_write_tail_exp .
ge ne ra teA IE xpr es si onB lo ck ( myBlock , true ) ;
myBlock . dispose ( true , false ) ;
};
options . push ( convertToBlocksOption ) ;
}
};
/**
* This function generates the new blocks equivalent to
the tail expression block .
* @param {! Sting } tailText The TAIL text that needs to be
parsed . This is not always
* the same as the current tyext in the block ( see
generator for this block ) .
* @param {! Blockly . Block } myBlock The TAIL expression
block to convert into AI blocks .
* @param {! Boolean } keepConnections If true , the new
blocks will replace the old blocks
* and keep the TAIL blocks connections ( if it is
connected to anything ) . If false , the
* connections will not be kept ( this is for use in the
generator , where we essentially
* want to generate invisible blocks , but instead we
generate blocks elsewhere in the
* workspace and dispose of them when we ' re done using
them ) .
125
* @return {! Blockly . Block } Returns the root block of the
generated set of blocks .
*/
Blockly . Language . code_write_tail_exp .
ge ne ra teA IE xpr es si onB lo ck = function ( myBlock ,
keepConnections ) {
try {
var parser = Blockly . Language . code . parser ( myBlock .
getTitleValue ( ' CODE ') ) ;
var newBlockDom = parser . expression_block () ;
// console . log (" New Block Dom : \ n " + Blockly . Xml .
domToPrettyText ( newBlockDom ) ) ;
var newBlock = Blockly . Xml . domToBlock_ ( myBlock .
workspace , newBlockDom ) ;
126
// var xy = myBlock . getRelativeToSurfaceXY () ;
// // if ( Blockly . RTL ) {
// // xy . x -= Blockly . SNAP_RADIUS ;
// // } else {
// // xy . x += Blockly . SNAP_RADIUS ;
// // }
// // xy . y += Blockly . SNAP_RADIUS * 2;
// newBlock . moveBy ( xy .x , xy . y ) ;
// }
127
// var myBlock = this ;
// var convertToBlocksOption = { text : " Convert
Statement Sequence to Blocks "};
// if (! this . errorIcon && ! this . disabled ) {
// convertToBlocksOption . enabled = true ;
// } else {
// convertToBlocksOption . enabled = false ;
// }
// convertToBlocksOption . callback = function () {
// Blockly . Language . code_write_tail_stmt .
ge nera teAI Stat eme ntBl ock ( myBlock , false , true ) ;
// myBlock . dispose ( true , false ) ;
// };
// options . push ( convertToBlocksOption ) ;
// } ,
codeCustomContextMenu : function ( options ) {
var myBlock = this ;
var convertToBlocksOption = { text : " Convert to Blocks
"};
if (! this . errorIcon && ! this . disabled ) {
convertToBlocksOption . enabled = true ;
} else {
convertToBlocksOption . enabled = false ;
}
convertToBlocksOption . callback = function () {
Blockly . Language . code_write_tail_stmt .
ge nera teAI Stat eme ntBl ock ( myBlock , true ) ;
myBlock . dispose ( false , false ) ; // we don ' t want to
use the healstack option here
};
options . push ( convertToBlocksOption ) ;
}
};
/**
* This function generates the new blocks equivalent to
the tail expression block .
* @param {! Sting } tailText The TAIL text that needs to be
parsed . This is not always
128
* the same as the current text in the block ( see
generator for this block ) .
* @param {! Blockly . Block } myBlock The TAIL expression
block to convert into AI blocks .
* @param {! Boolean } keepConnections If true , the new
blocks will replace the old blocks
* and keep the TAIL blocks connections ( if it is
connected to anything ) . If false , the
* connections will not be kept ( this is for use in the
generator , where we essentially
* want to generate invisible blocks , but instead we
generate blocks elsewhere in the
* workspace and dispose of them when we ' re done using
them ) .
* @return {! Blockly . Block } Returns the root block of the
generated set of blocks .
*/
if ( keepConnections ) {
Blockly . BlocklyEditor . r e p o s i t i o n N e w l y G e n er a t e d B l o c k
( myBlock , newBlock ) ;
}
return newBlock ;
} catch ( error ) {
if (!( error instanceof org . antlr . runtime .
RecognitionException ) ) {
throw error ;
}
}
};
129
// var reposition = true ; // variable which indicates
whether the newly generated block ( s ) need to be
repositioned or not
130
// var xy = myBlock . getRelativeToSurfaceXY () ;
// newBlock . moveBy ( xy .x , xy . y ) ;
// }
// // if (! newBlock . previousConnection . targetConnection )
{ // else move new generated blocks to location of
myBlock
// // // this doesn ' t need to happen for a tail stmt
block that has a previous block connected ,
// // // but it does need to happen for a block that
doesn 't , but does have a next block ,
// // // so we should just do this in all cases to be
safe ....
131
textToDisplay = textToDisplay . substring (0 , 5) +
'... ';
this . getTitle_ ( ' COLLAPSED_TEXT ') . setText (
textToDisplay , ' COLLAPSED_TEXT ') ;
},
// c o d e S t a t e m e n t S e q u e n c e C u s t o m C o n t e x t M e n u : function (
options ) {
// var myBlock = this ;
// var convertToBlocksOption = { text : " Convert
Statement Sequence to Blocks "};
// if (! this . errorIcon && ! this . disabled ) {
// convertToBlocksOption . enabled = true ;
// } else {
// convertToBlocksOption . enabled = false ;
// }
// convertToBlocksOption . callback = function () {
// Blockly . Language . code_write_tail_stmt .
ge nera teAI Stat eme ntBl ock ( myBlock , false , true ) ;
// myBlock . dispose ( true , false ) ;
// };
// options . push ( convertToBlocksOption ) ;
// } ,
codeCustomContextMenu : function ( options ) {
var myBlock = this ;
var convertToBlocksOption = { text : " Convert to Blocks
"};
if (! this . errorIcon && ! this . disabled ) {
convertToBlocksOption . enabled = true ;
} else {
convertToBlocksOption . enabled = false ;
}
convertToBlocksOption . callback = function () {
Blockly . Language . code_write_tail_stmt .
genera teAITopLev elBlock ( myBlock , true ) ;
myBlock . dispose ( false , false ) ; // we don ' t want to
use the healstack option here
};
options . push ( convertToBlocksOption ) ;
}
};
132
Blockly . Language . code_write_tail_stmt .
genera teAITopLev elBlock = function ( myBlock ,
keepConnections ) {
try {
var parser = Blockly . Language . code . parser ( myBlock .
getTitleValue ( ' CODE ') ) ;
var newBlockDom = parser . top_level_block () ;
console . log (" DOM : " + Blockly . Xml . domToPrettyText (
newBlockDom ) ) ;
var newBlock = Blockly . Xml . domToBlock_ ( myBlock .
workspace , newBlockDom ) ;
if ( keepConnections ) {
Blockly . BlocklyEditor . r e p o s i t i o n N e w l y G e n er a t e d B l o c k
( myBlock , newBlock ) ;
}
return newBlock ;
} catch ( error ) {
if (!( error instanceof org . antlr . runtime .
RecognitionException ) ) {
throw error ;
}
}
};
133
YAIL Code Generator
134
Blocks to Text Converter
135
procedures_defreturn " ," component_event " , , "
procedures_callreturn " , ];
Blockly . BlocksToTextConverter .
g e t I m m e d i a t e Ch i l d r e n B y T a g N a m e = function ( element ,
tagName ) {
var elementsWithTag = [];
var current = element . firstElementChild ;
while (!! current ) {
if ( current . nodeName . toLowerCase () === tagName .
toLowerCase () ) {
elementsWithTag . push ( current ) ;
}
current = current . nextElementSibling ;
}
return elementsWithTag ;
}
136
Blockly . BlocksToTextConverter . blockToTAIL = function (
block ) {
var root = Blockly . Xml . blockToDom_ ( block ) ;
Blockly . BlocksToTextConverter . tailText = "";
// translate root node
Blockly . BlocksToTextConverter . translateBlock ( root ) ;
137
Blockly . BlocksToTextConverter . type = "
code_write_tail_stmt ";
Blockly . BlocksToTextConverter .
transl ateStatemen tBlock ( element ) ;
}
}
else {
if ( Blockly . BlocksToTextConverter . expressionBlocks .
indexOf ( type ) !== -1) {
// element is an expression block
Blockly . BlocksToTextConverter . type = "
code_write_tail_exp ";
Blockly . BlocksToTextConverter .
tr ansl ateE xpre ssi onBl ock ( element ) ;
} else if ( Blockly . BlocksToTextConverter .
statementBlocks . indexOf ( type ) !== -1) {
// element is a statement block
Blockly . BlocksToTextConverter . type = "
code_write_tail_stmt ";
Blockly . BlocksToTextConverter .
trans lateStateme ntBlock ( element ) ;
} else if ( Blockly . BlocksToTextConverter .
topLevelBlocks . indexOf ( type ) !== -1) {
// element is a top level block
Blockly . BlocksToTextConverter . type = "
code_write_tail_decl ";
Blockly . BlocksToTextConverter .
translateTopLevelBlock ( element ) ;
} else {
// do nothing
console . log (" I ' m getting not getting an expression ,
statement , or top level block .")
}
}
}
}
138
Blockly . BlocksToTextConverter . tra nsla teEx pre ssio nBlo ck =
function ( element ) {
var elementType = element . getAttribute (" type ") ;
// expression block
Blockly . BlocksToTextConverter . tailText += '{ ';
Blockly . BlocksToTextConverter [" translate_ " +
elementType ]. call ( this , element ) ;
Blockly . BlocksToTextConverter . tailText += '} ';
}
139
Blockly . BlocksToTextConverter . tra nsla teEx pre ssio nBlo ck (
aBlock ) ;
Blockly . BlocksToTextConverter . tailText += ' ' + op + '
' ;
Blockly . BlocksToTextConverter . tra nsla teEx pre ssio nBlo ck (
bBlock ) ;
}
140
Blockly . BlocksToTextConverter . translate_math_compare =
function ( element ) {
var children = element . children ;
var aBlock = children . namedItem (" A ") . firstElementChild ;
var bBlock = children . namedItem (" B ") . firstElementChild ;
var op = children . namedItem (" OP ") . innerHTML ;
var mutationBlock ;
for ( var i =0; i < children . length ; i ++) {
var child = children . item ( i ) ;
if ( child . nodeName . toLowerCase () === " mutation ") {
mutationBlock = child ;
}
}
141
var numItems = (!! mutationBlock ) ? parseInt (
mutationBlock . getAttribute (" items ") ) : 0;
var mutationBlock ;
for ( var i =0; i < children . length ; i ++) {
var child = children . item ( i ) ;
if ( child . nodeName . toLowerCase () === " mutation ") {
mutationBlock = child ;
}
}
142
var value = children . namedItem (" NUM "+ i ) .
firstElementChild ;
143
Blockly . BlocksToTextConverter . tra nsla teEx pre ssio nBlo ck (
aBlock ) ;
Blockly . BlocksToTextConverter . tailText += ' ^ ';
Blockly . BlocksToTextConverter . tra nsla teEx pre ssio nBlo ck (
bBlock ) ;
}
144
} else if ( op === " exp ") {
op = 'e ^ ';
} // no else case , op should remain the same for abs ,
round , ceiling , and floor
145
Blockly . BlocksToTextConverter . translate_math_trig =
function ( element ) {
var children = element . children ;
var expr = children . namedItem (" NUM ") . firstElementChild ;
Blockly . BlocksToTextConverter .
t r a n s l a t e _ l o c a l _ d e c l a r a t i o n _ e x p r e s s i o n = function (
element ) {
Blockly . BlocksToTextConverter . tailText += '
initialize_local ';
var children = element . children ;
var mutationBlock ;
for ( var i =0; i < children . length ; i ++) {
var child = children . item ( i ) ;
if ( child . nodeName . toLowerCase () === " mutation ") {
mutationBlock = child ;
}
}
146
var numVars = (!! mutationBlock ) ? mutationBlock .
childElementCount : 0;
Blockly . BlocksToTextConverter .
t r a n s l a t e _ l e x i c a l _ v a r i a b l e _ g e t = function ( element ) {
Blockly . BlocksToTextConverter . tailText += ' get ';
var title = element . firstElementChild ;
Blockly . BlocksToTextConverter . tailText += title .
innerHTML ;
}
147
}
148
Blockly . BlocksToTextConverter . translate_color_red =
function ( element ) {
var hexValue = element . firstElementChild . innerHTML .
toLowerCase () ;
if ( hexValue === "# ff0000 ") {
Blockly . BlocksToTextConverter . tailText += ' color red
';
} else {
Blockly . BlocksToTextConverter . tailText += ' color ' +
hexValue ;
}
}
Blockly . BlocksToTextConverter . t ra ns l at e_ co l or _l ig h t_ gr ay
= function ( element ) {
var hexValue = element . firstElementChild . innerHTML .
toLowerCase () ;
if ( hexValue === "# cccccc ") {
Blockly . BlocksToTextConverter . tailText += ' color
light_gray ';
} else {
Blockly . BlocksToTextConverter . tailText += ' color ' +
hexValue ;
}
}
149
Blockly . BlocksToTextConverter . translate_color_gray =
function ( element ) {
var hexValue = element . firstElementChild . innerHTML .
toLowerCase () ;
if ( hexValue === "#888888") {
Blockly . BlocksToTextConverter . tailText += ' color gray
';
} else {
Blockly . BlocksToTextConverter . tailText += ' color ' +
hexValue ;
}
}
150
Blockly . BlocksToTextConverter . translate_color_yellow =
function ( element ) {
var hexValue = element . firstElementChild . innerHTML .
toLowerCase () ;
if ( hexValue === "# ffff00 ") {
Blockly . BlocksToTextConverter . tailText += ' color
yellow ';
} else {
Blockly . BlocksToTextConverter . tailText += ' color ' +
hexValue ;
}
}
151
Blockly . BlocksToTextConverter . t ra ns l at e_ co l or _m ak e _c ol or
= function ( element ) {
var children = element . children ;
var expr = children . namedItem (" COLORLIST ") .
firstElementChild ;
Blockly . BlocksToTextConverter . t r an s la t e _l i st s _ cr e at e _ wi t h
= function ( element ) {
Blockly . BlocksToTextConverter . tailText += ' list ';
var children = element . children ;
var mutation = parseInt ( element . firstElementChild .
getAttribute (" items ") ) ;
for ( var i =0; i < mutation ; i ++) {
var valBlock = children . namedItem (" ADD " + i ) ;
if ( valBlock ) {
var block = valBlock . firstElementChild ;
Blockly . BlocksToTextConverter . tailText += ' ';
Blockly . BlocksToTextConverter .
tr ansl ateE xpre ssi onBl ock ( block ) ;
}
}
}
152
Blockly . BlocksToTextConverter . translate_ logic_boole an =
function ( element ) {
Blockly . BlocksToTextConverter . tailText += element .
firstElementChild . innerHTML . toLowerCase () ;
}
Blockly . BlocksToTextConverter .
t r a n s l a t e _ c o de _ w r i t e _ t a i l _ e x p = function ( element ) {
Blockly . BlocksToTextConverter . tailText += ' TAIL_exp ';
Blockly . BlocksToTextConverter . t ra ns l at eS ta t em en tS e qu en ce
= function ( element ) {
// var elementType = element . getAttribute (" type ") ;
153
if (!! element ) { // if element is not null ( make sure it ' s
not the empty statement )
// var children = element . children ;
var curBlock = element . firstElementChild ; // this
should be the first statement block
Blockly . BlocksToTextConverter . translateS tatementBlo ck (
curBlock ) ;
var next_arr = Blockly . BlocksToTextConverter .
g e t I m m e d i a t eC h i l d r e n B y T a g N a m e ( curBlock ," next ") ;
if ( next_arr . length === 1) {
Blockly . BlocksToTextConverter .
t ra ns la t eS ta te m en tS eq u en ce ( next_arr [0]) ;
}
}
}
// Blockly . BlocksToTextConverter .
tr an sl ate _c ont ro ls _ch oo se = function ( element ) {
// var children = element . children ;
// var ifBlock = children . namedItem (" TEST ") .
firstElementChild ;
// var thenBlock = children . namedItem (" THENRETURN ") .
firstElementChild ;
// var elseBlock = children . namedItem (" ELSERETURN ") .
firstElementChild ;
154
var mutations = Blockly . BlocksToTextConverter .
g e t I m m e d i a t eC h i l d r e n B y T a g N a m e ( element ," mutation ") ;
155
Blockly . BlocksToTextConverter . tailText += ' else :
';
Blockly . BlocksToTextConverter .
t ra ns la t eS ta te m en tS eq u en ce ( elseBlocks ) ;
}
} else {
console . log (" Blocks To Text Converter -
translate_controls_if : something is wrong with
mutations ") ;
}
}
Blockly . BlocksToTextConverter .
t r a n s l a t e _ l e x i c a l _ v a r i a b l e _ s e t = function ( element ) {
var children = element . children ;
var varName = children . namedItem (" VAR ") . innerHTML ;
var value = children . namedItem (" VALUE ") .
firstElementChild ;
Blockly . BlocksToTextConverter .
t r a n s l a t e _ c o d e _ w r i t e _ t a i l _ s t m t = function ( element ) {
Blockly . BlocksToTextConverter . tailText += ' TAIL_stmt ';
156
Blockly . BlocksToTextConverter . translateTopLevelBlock =
function ( element ) {
var elementType = element . getAttribute (" type ") ;
// expression block
Blockly . BlocksToTextConverter . tailText += '( ';
Blockly . BlocksToTextConverter [" translate_ " +
elementType ]. call ( this , element ) ;
Blockly . BlocksToTextConverter . tailText += ') ';
}
Blockly . BlocksToTextConverter .
t r a ns l a t e _g l o b a l_ d e c l ar a t i o n = function ( element ) {
var children = element . children ;
var varName = children . namedItem (" NAME ") . innerHTML ;
var value = children . namedItem (" VALUE ") .
firstElementChild ;
Blockly . BlocksToTextConverter .
t r a n s l a t e _ p r o c e d u r e s _ d e f n o r e t u r n = function ( element ) {
var children = element . children ;
var procName = children . namedItem (" NAME ") . innerHTML ;
var numParams = 0;
var mutations = Blockly . BlocksToTextConverter .
g e t I m m e d i a t eC h i l d r e n B y T a g N a m e ( element ," mutation ") ;
if ( mutations . length === 1) {
numParams = mutations [0]. childElementCount ;
} else {
console . log (" Blocks To Text Converter -
t r a n s l a t e _ p r o c e d u r e s _ d e f n o r e t u r n : something is
157
wrong with mutations ") ;
}
Blockly . BlocksToTextConverter .
t r a n s l a t e _ p r o c e d u r e s _ d e f r e t u r n = function ( element ) {
var children = element . children ;
var procName = children . namedItem (" NAME ") . innerHTML ;
var numParams = 0;
var mutations = Blockly . BlocksToTextConverter .
g e t I m m e d i a t eC h i l d r e n B y T a g N a m e ( element ," mutation ") ;
if ( mutations . length === 1) {
numParams = mutations [0]. childElementCount ;
} else {
console . log (" Blocks To Text Converter -
t r a n s l a t e _ p r o c e d u r e s _ d e f n o r e t u r n : something is
wrong with mutations ") ;
}
158
var retVal = children . namedItem (" RETURN ") .
firstElementChild ;
Blockly . BlocksToTextConverter . tailText += ' result : ';
Blockly . BlocksToTextConverter . tra nsla teEx pre ssio nBlo ck (
retVal ) ;
}
Blockly . BlocksToTextConverter .
t ra ns la t eS ta te m en tS eq u en ce ( suite ) ;
}
159
var mutations = Blockly . BlocksToTextConverter .
g e t I m m e d i a t eC h i l d r e n B y T a g N a m e ( element ," mutation ") ;
if ( mutations . length !== 1) {
console . log (" BlocksToTextCovereter -
tr an sl ate _c omp on en t_e ve nt : Something is wrong with
this block .")
}
var setGet = mutations [0]. getAttribute (" set_or_get ") ;
160