The basic elements of expressions are terms:
term: identifier constant real-constant string-constant nil ( expression-list ) term . identifier term -> term term ( expression-listopt ) term [ expression ] term [ expression : expression ] term [ expression : ] term ++ term --
. -> () [] ++ --
The first five kinds of term are constants and identifiers. Constants have a type indicated by their syntax. An identifier used in an expression is often a previously declared data object with a particular data type; when used as a term in an expression it denotes the value stored in the object, and the term has the declared object's type. Sometimes, as discussed below, identifiers used in expressions are type names, function names, or module identifiers.
A comma-separated list of expressions enclosed in parentheses is a term. If a single expression is present in the list, the type and value are those of the expression; the parentheses affect only the binding of operators in the expression of which the term is a part. If there is more than one expression in the list, the value is a tuple. The member types and values are taken from those of the expressions.
A term of the form
term . identifier
A term of the form
term -> term
An example using an abridged version of an example above: given
Linear: module {
setflags: fn(flag: int);
TRUNCATE: con 1;
Vector: adt {
make: fn(v: array of real): Vector;
v: array of real;
};
};
lin := load Linear "/dis/linear.dis"; a: array of real; v1: lin->Vector; v2: Linear->Vector; lin->setflags(Linear->TRUNCATE); v1 = lin->(Linear->Vector).make(a); v1 = lin->v1.make(a); v1 = lin->v1.add(v1); v1.v = nil;
When calling a function associated with an adt of another module, it is necessary to identify both the module and the adt as well as the function. The two calls to the make function illustrate two ways of doing this. In the first,
v1 = lin->(Linear->Vector).make(a);
v1 = lin->v1.make(a);
v1 = lin->Vector.make(a); # Wrong v1 = lin->Linear->Vector.make(a); # Wrong
Using import makes the code less verbose:
lin := load Linear "/usr/dmr/limbo/linear.dis"; Vector, TRUNCATE, setflags: import lin; a: array of real; v1: Vector; v2: Vector; setflags(TRUNCATE); v1 = Vector.make(a); v1 = v1.make(a); v1 = v1.add(v1); v1.v = nil;
The interpretation of an expression in the form
term ( expression-listopt )
A plain identifier as the term names a function defined in the current module or imported into it. A term qualified by using the selection operator . specifies a function member of an adt; a term using -> specifies a function defined in another module.
Function calls in Limbo create a copy of each argument of value type, and the execution of a function cannot affect the value of the corresponding actual argument. For arguments of reference type, execution of the function may affect the value of the object to which the reference refers, although it cannot change the argument itself. The actual arguments to a function are evaluated in an unspecified order, although any side effects caused by argument evaluation occur before the function is called.
Function calls may be directly or indirectly recursive; objects declared within each function are distinct from those in their dynamic predecessors.
Functions (§4.3, §7) may either return a value of a specified type, or return no value. If a function returns a value, it has the specified type. A call to a function that returns no value may appear only as the sole expression in a statement (§9.1).
In a term of the form
term [ expression ]
It is erroneous to refer to a nonexisting part of an array or string. (A single exception to this rule, discussed in §8.4.1 below, allows extending a string by assigning a character at its end.)
In a term of the form
term [ expression : expression ]
Thus, for both arrays and strings, the number of elements in a[e1:e2] is equal to e2-e1.
A slice of the form a[e:] means a[e:len a].
When a string slice is assigned to another string or passed as an argument, a copy of its value is made.
A slice of an array produces a reference to the designated subarray; a change to an element of either the original array or the slice is reflected in the other.
In general, slice expressions cannot be the subject of assignments. However, as a special case, an array slice expression of the form a[e1:] may be assigned to. This is discussed in §8.4.1.
The following example shows how slices can be used to accomplish what would need to be done with pointer arithmetic in C:
fd := sys->open( ... );
want := 1024;
buf := array[want] of byte;
b := buf[0:];
while (want>0) {
got := sys->read(fd, b, want);
if (got<=0)
break;
b = b[got:];
want -= got;
}
A term of the form
term ++
The term
term --