Expressions¶
- L-values
The l-values (whose value can be read or changed) are: elements of arrays, fields of records, instances of classes, arguments and variables.
- 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.- Nil
The reserved word
nilrefers to a value from a record or a class type. Do not usenilwhere 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
- 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.
0is 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
- 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.
- Class instantiation
An object is created with
new. There are no constructors in Tiger, sonewtakes 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
0or1. For instance, because&and|can be implemented as syntactic sugar, one could easily make123 | 456return1or123: 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 valuenilcan only be compared against a value which type is that of a record or a class. As suchnil = nilis 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
ais assigned another variablebof the same type, then changes on b will affectaand 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 itnherits 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
- 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
- Parentheses
Parentheses enclosing a single expression enforce syntactic grouping.
- 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
- If then else
In an if then else expression:
if exp1 then exp2 else exp3
exp1is typed as an integer,exp2andexp3must 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
exp1is typed as an integer,exp2must have no value. The whole expression has no value either.- While
In a while expression:
while exp1 do exp2
exp1is typed as an integer,exp2must 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 ofexp1to that ofexp2, inclusive, by steps of 1.The scope ofidis restricted toexp3. In particular,idcannot appear inexp1norexp2. The variableidcannot be assigned to.The type of bothexp1andexp2is integer, they can range from the minimal to the maximal integer values. The bodyexp3and 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
letandin), except if it is enclosed by a loop, of course.- Let in end
In the let in end expression:
let decs in exps end
decsis a sequence of declaration andexpsis a sequence of expressions separated by a semi-colon. The whole expression has the value ofexps.