Tool Resource Page
Tool stands for Toy Object-Oriented Language, and it is the programming language for which you will write a compiler in this course.
Some basic information for Tool:
- Tool is object oriented. It supports classes with inheritance and method overriding (but not overloading).
- Tool is imperative. All class fields and local variables are mutable, and the while-loop is the main control structure.
- Tool supports the following types: Ints, Booleans, Strings, Int arrays and class types.
- The entry point to the program is signified by the
program
keyword, and is only allowed to invoke a series of statements.
See also the Tool Compiler Project page.
BNF
The syntax of Tool is given by the following BNF grammar:
Program | ::= | MainObject ( ClassDeclaration )* <EOF> |
MainObject | ::= | program Identifier { ( Statement )* } |
ClassDeclaration | ::= | class Identifier ( extends Identifier )? { ( VarDeclaration )* ( MethodDeclaration )* } |
VarDeclaration | ::= | var Identifier : Type ; |
MethodDeclaration | ::= | def Identifier ( ( Identifier : Type ( , Identifier : Type )* )? ) : Type = { ( VarDeclaration )* ( Statement )* return Expression ; } |
Type | ::= | Int |
Bool | ||
String | ||
Int [ ] | ||
Identifier | ||
Statement | ::= | { ( Statement )* } |
if ( Expression ) Statement ( else Statement )? | ||
while ( Expression ) Statement | ||
println ( Expression ) ; | ||
Identifier = Expression ; | ||
Identifier [ Expression ] = Expression ; | ||
do ( Expression ) ; | ||
Expression | ::= | Expression ( && | || | == | < | + | - | * | / ) Expression |
Expression [ Expression ] | ||
Expression . length | ||
Expression . Identifier ( ( Expression ( , Expression )* )? ) | ||
<INTEGER_LITERAL> | ||
" <STRING_LITERAL> " | ||
true | ||
false | ||
Identifier | ||
this | ||
new Int [ Expression ] | ||
new Identifier ( ) | ||
! Expression | ||
( Expression ) | ||
Identifier | ::= | <IDENTIFIER> |
- <IDENTIFIER> represents a sequence of letters, digits and underscores, starting with a letter and which is not a keyword. Identifiers are case-sensitive.
- <INTEGER_LITERAL> represents a sequence of digits
- <STRING_LITERAL> represents a sequence of arbitrary characters, except new lines and ". We don't ask you to support escape characters such as \n.
- <EOF> represents the special end-of-file character
Language Semantics
+
The +
operator can be applied on Int
and String
operands. Unless being applied on two integers (in which case the result is an Int), the result is the concatenation of the string representations.
3 + 3 => 6 "foo" + "bar" => "foobar" "foo" + 3 => "foo3" 3 + "foo" => "3foo"
<, -, *, /
These are the usual arithmetic operators and are applied on Int
operands only.
==
Equality works on all pairs of operands that
- either both belong to class types (even different ones), or
- are of the same type (
Int
,String
,Boolean
,Int[]
).
The semantics of equality is
- value-equality for ints and booleans
- reference-equality for strings, objects and arrays
42 == 42 => true new A() == new A() => false new A() == new B() => false "foo" == 42 => // Type Error... "f" + "oo" == "fo" + "o" => false
println
println
can be used on Integers
, Strings
and Booleans
.
do
The do
keyword is used to call an expression (normally, a method call) just for its side-effect, and discards the result.
e.g. if in a class we define
def hello(): Boolean = { println("Hello world!"); return true; }
then do(this.hello());
will print Hello, world!
on the standard output and discard the result. On the other hand, do(42);
will do nothing.
&&
The &&
(boolean AND) operator is short-circuitting!
def printFoo(): Boolean = { println("foo"); return false; } def printBar(): Boolean = { println("bar"); return true; }
printFoo() && printBar() // "foo"
||
The ||
(boolean OR) operator is short-circuitting!
def printFoo(): Boolean = { println("foo"); return false; } def printBar(): Boolean = { println("bar"); return true; }
printBar() || printFoo() // "bar"
!
!
is the boolean negation.
Arrays
new Int[size]
, where size: Int
returns a new array of size size
.
a[i]
, where a: Int[]
and i: Int
, returns the value stored in the i
th position of a
.
a.length
, where a: Int[]
, returns the length of a
.
a[i] = j
, where a: Int[]
, i: Int
and j: Int
, sets the value in the i
th position of a
to j
.
Miscellaneous
- Comments in Tool can be marked using the end-of-line notation (//) or blocks ( /* … */ ). Nested blocks are not allowed.
- Inheritance works as in Java, except that we don't have the notions of interfaces, abstract members or abstract classes.
- Note that the else branch of the if construct is optional.
- The names of the classes must be distinct from the name of the program object.
- No, you cannot define fields within
program
. - No, you cannot use non-default constructors (constructors that take parameters).
- Method overloading is not allowed, but method overriding is. If you are unclear about the difference between the two, see for instance this page. The only overloading in Tool is on the
+
operator, which can be used with integers, strings, and between the two types. - You cannot define two fields with the same name in a class, or two classes that inherit each other (field overriding is not allowed).
- You cannot define two variables with the same name in a method (including parameters and locally defined variables). You can, however, define a variable in a method whose enclosing object has a field of the same name; in that case, the method variable takes precedence.
- Accessing a field that has not been initialized results in undefined behavior (an implementation of Tool is free to crash or return a default value in this case).
- Only constant strings (strings given as literals) are allowed, and although the grammar specifies that expressions such as
“my string”.method()
are legal, they have no meaning in Tool (they would result in a type error). The only operations allowed are: concatenating strings with other strings or with integers, printing strings, passing strings as argument and returning strings. - The operator precedence is the same as in Java and Scala. From highest priority to lowest: !, then * and /, then + and -, then < and ==, then &&, then ||. Also,
new
binds tighter than.
(method call or.length
), which binds tighter than[]
(array read), which binds tighter than any operator. So1 + new Foo().bar().baz()[42]
means1 + ( ( ( (new Foo()).bar()).baz())[42])
. - All binary operators are left-associative. E.g.
1-2+3
means(1-2)+3
and not1-(2+3)
. (Of course this does not matter for operators of different precedence).
Example Programs
Here are some demonstration programs:
- Factorial.tool - Computes factorials
- BinarySeach.tool - Binary search (dichotomy) in an array
- QuickSort.tool - Implementation of quick sort in Tool
- Pi.tool - Computes an approximation of pi in two different ways
- Maze.tool - Generates and prints random mazes (labyrinths)