Expressions
- Integers
An integer literal is a series of decimal digits (therefore it is non-negative). Since the compiler targets 32-bit architectures, since it needs to handle signed integers, a literal integer value must fit in a signed 32-bit integer. Any other integer value is a scanner error.
- Booleans
There is no Boolean type in Tiger: they are encoded as integers, with the same semantics as in C, i.e.
0
is the only value standing for false, anything else stands for true.- Strings
A string constant is a possibly empty series of printable characters, spaces or escapes sequences (see Lexical Specifications) enclosed between double quotes.
let var s := "\t\124\111\107\105\122\n" in print(s) end
- Let in end
In the let in end expression:
let chunks in exps end
chunks
is a sequence of declaration andexps
is a sequence of expressions separated by a semi-colon. The whole expression has the value ofexps
.- Sequences
A sequence is a possibly empty series of expressions separated by semicolons and enclosed by parenthesis. By convention, there are no sequences of a single expression (see the following item). The sequence is evaluated from the left to the right. The value of the whole sequence is that of its last expression.
let var a := 1 in a := ( print("first exp to display\n"); print("second exp to display\n"); a := a + 1; a ) + 42; print("the last value of a is : "); print_int(a); print("\n") end
- If then else
In an if then else expression:
if exp1 then exp2 else exp3
exp1
is typed as an integer,exp2
andexp3
must have the same type which will be the type of the entire structure. The resulting type cannot be that ofnil
.- If then
In an if then expression:
if exp1 then exp2
exp1
is typed as an integer,exp2
must have no value. The whole expression has no value either.- While
In a while expression:
while exp1 do exp2
exp1
is typed as an integer,exp2
must have no value. The whole expression has no value either.- For
The following for loop:
for id := exp1 to exp2 do exp3
introduces a fresh variable,id
, which ranges from the value ofexp1
to that ofexp2
, inclusive, by steps of 1.The scope ofid
is restricted toexp3
. In particular,id
cannot appear inexp1
norexp2
. The variableid
cannot be assigned to.The type of bothexp1
andexp2
is integer, they can range from the minimal to the maximal integer values. The bodyexp3
and the whole loop have no value.- Break
A break terminates the nearest enclosing while or for loop. A break must be enclosed by a loop. A break cannot appear inside a definition (e.g. between
let
andin
), except if it is enclosed by a loop, of course.- L-values
The l-values (whose value can be read or changed) can be of multiple types. They can be: - a simple variable, i.e. just the variable identifier - an element of an array - a field of a record - a class instance and a class attribute - a function or a method’s argument
/* Array subscript */ var x := arr[2] /* Field access */ var y := record.field /* Simple variable */ var z := x /* Function argument (actually, it is a simple variable) */ function identity(s: string) : string = s
- Valueless expressions
Some expressions have no value: procedure calls, assignments, ifs with no else clause, loops and breaks. Empty sequences
()
and lets with an empty body are also valueless.- Record instantiation
A record instantiation must define the value of all the fields and in the same order as in the definition of the record type.
- Nil
The reserved word
nil
refers to a value from a record or a class type. Do not usenil
where its type cannot be determined.let type any_record = {any : int} var nil_var : any_record := nil function nil_test(parameter : any_record) : int = ... var invalid := nil /* no type, invalid */ in if nil <> nil_var then ... if nil_test(nil_var) then ... if nil = nil then ... /* no type, invalid */ end
- Class instantiation
An object is created with
new
. There are no constructors in Tiger, sonew
takes only one operand, the name of the type to instantiate.- Function call
Function arguments are evaluated from the left to the right. Arrays and records arguments are passed by reference, strings and integer are passed by value.
The following example:
let type my_record = {value : int} function reference(parameter : my_record) = parameter.value := 42 function value(parameter : string) = parameter := "Tiger is the best language\n" var rec1 := my_record{value = 1} var str := "C++ rulez" in reference(rec1); print_int(rec1.value); print("\n"); value(str); print(str); print("\n") end
results in:
42 C++ rulez
- Boolean operators
Tiger Boolean operators normalize their result to
0
or1
. For instance, because&
and|
can be implemented as syntactic sugar, one could easily make123 | 456
return1
or123
: make them return1
. Andrew Appel does not enforce this for&
and|
; we do, so that the following program has a well defined behavior:print_int("0" < "9" | 42)
- Arithmetic
Arithmetic expressions only apply on integers and return integers. Available operators in Tiger are:
+
,-
,*
and/
.- Comparisons
Comparison operators (
=
,<>
,<=
,<
,>=
and>
) return a Boolean value.- Integer and string comparison
All the comparison operators apply to pairs of strings and pairs of integers, with obvious semantics.
- String comparison
Comparison of strings is based on the lexicographic order.
- Array and record comparison
Pairs of arrays and pairs of records of the same type can be compared for equality (
=
) and inequality (<>
). Identity equality applies, i.e. an array or a record is only equal to itself (shallow equality), regardless of the contents equality (deep equality). The valuenil
can only be compared against a value which type is that of a record or a class. As suchnil = nil
is invalid.Arrays, records and objects cannot be ordered:
<
,>
,<=
and>=
are valid only for pairs of strings or integers.- Void comparison
In conformance with Andrew Appel’s specifications, any two void entities are equal.
- Assignments
Assignments yield no value. The following code is syntactically correct, but type incorrect:
let var foo := 1 var bar := 1 in foo := (bar := 2) + 1 end
Note that the following code is valid:
let var void1 := () var void2 := () var void3 := () in void1 := void2 := void3 := () end
- Array and record assignment
Array and record assignments are shallow, not deep, copies. Therefore aliasing effects arise: if an array or a record variable
a
is assigned another variableb
of the same type, then changes on b will affecta
and vice versa.let type bar = {foo : int} var rec1 := bar{foo = 1} var rec2 := bar{foo = 2} in print_int(rec1.foo); print(" is the value of rec1\n"); print_int(rec2.foo); print(" is the value of rec2\n"); rec1 := rec2; rec2.foo = 42; print_int(rec1.foo); print(" is the new value of rec1\n") end
- Polymorphic (object) assignments
Upcasts are valid for objects because of inclusion polymorphism.
let class A {} class B extends A {} var a := new A var b := new B in a := b end
Upcasts can be performed when defining a new object variable, by forcing the type of the declared variable to a super class of the actual object.
let class C {} class D extends C {} var c : C := new D in end
Tiger does not provide a downcast feature performing run time type identification (RTTI), like C++’s dynamic_cast.
let class E {} class F extends E {} var e : E := new F var f := new F in /* Invalid: downcast. */ f := e end
- Polymorphic (object) branching
- Upcasts are performed when branching between two class instantiations.Since every class inherits from
Object
, you will always find a common root.let class A {} class B extends A {} in if 1 then new A else new B end
- Lifetime
Records and arrays have infinite lifetime: their values lasts forever even if the scope of their creation is left.
let type bar = {foo : int} var rec1 := bar{foo = 1} in rec1 := let var rec2 := bar{foo = 42} in rec2 end; print_int(rec1.foo); print("\n") end
- Parenthesis
Parenthesis enclosing a single expression enforce syntactic grouping.
(2 + 3) * 4