gopl-zh.github.com/docs/Limbo/The.Limbo.Programming.Language.htm

5372 lines
138 KiB
HTML
Raw Normal View History

2015-12-22 01:49:45 +00:00
<!-- saved from url=(0050)http://www.vitanuova.com/inferno/papers/limbo.html -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>The Limbo Programming Language</title>
</head><body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#330088" alink="#FF0044">
<h1>The Limbo Programming Language
</h1>
<dl><dd><i>Dennis M. Ritchie<br>
</i></dd></dl>
<p>
Limbo is a programming language intended for applications
running distributed systems on small computers.
It supports modular programming,
strong type checking at compile- and run-time,
interprocess communication over typed channels,
automatic garbage collection,
and simple abstract data types.
It is designed for safe execution even on
small machines without hardware memory protection.
</p>
<p>
In its initial implementation for the Inferno operating system,
object programs generated by the Limbo compiler run
using an interpreter for a fixed virtual machine.
Inferno and its accompanying virtual machine run either stand-alone
on bare hardware
or as an application under conventional operating systems like
Unix, Windows 95, Windows NT, and Plan 9.
For several architectures, including
Intel x86 and MIPS, Limbo object programs
are transformed on-the-fly into instructions for the underlying hardware.
</p>
<h4>1 Overview and introduction
</h4>
<p>
A Limbo application consists of one or more
<i>modules</i>,
each of which supplies an interface declaration and
an implementation part.
A module that uses another module
includes its declaration part.
During
execution, a module dynamically attaches another module by
stating the other module's type identifier and a place from which to load
the object code for its implementation.
</p>
<p>
A module declaration specifies the functions and data it will make visible,
its data types, and constants.
Its implementation part defines the functions and data visible at its interface and
any functions associated with its data types;
it may also contain definitions for functions used only internally and
for data local to the module.
</p>
<p>
Here is a simple module to illustrate the flavour of the language.
</p><dl><dt></dt><dd><tt><pre>1 implement Command;
2 include "sys.m";
3 include "draw.m";
4 sys: Sys;
5 Command: module
{
6 init: fn (ctxt: ref Draw-&gt;Context, argv: list of string);
7 };
</pre></tt></dd></dl>
<dl><dt></dt><dd><tt><pre>8 # The canonical "Hello world" program, enhanced
9 init(ctxt: ref Draw-&gt;Context, argv: list of string)
10 {
11 sys = load Sys Sys-&gt;PATH;
12 sys-&gt;print("hello world\n");
13 for (; argv!=nil; argv = tl argv)
14 sys-&gt;print("%s ", hd argv);
15 sys-&gt;print("\n");
16 }
</pre></tt></dd></dl>
A quick glance at the program reveals that
the syntax of Limbo is influenced by C in its expressions,
statements, and some conventions (for example, look at lines 13-14),
and also by Pascal and its successors (the declarations on lines 4, 6, 9).
When executed in the Inferno environment, the program writes
<tt>hello</tt>
<tt>world</tt>
somewhere, then echoes its arguments.
<p></p>
<p>
Let's look at the program line-by-line.
It begins (line 1) by saying that this is the implementation of module
<tt>Command</tt>.
Line 2 includes a file (found in a way analogous to C's
<tt>#include</tt>
mechanism) named
<tt>sys.m</tt>.
This file defines the interface to module
<tt>Sys</tt>;
it says, in part,
</p><dl><dt></dt><dd><tt><pre>Sys: module {
PATH: con "$Sys";
. . .
print: fn (s: string, *): int;
. . .
};
</pre></tt></dd></dl>
This declares
<tt>Sys</tt>
to be the type name for a module containing among other things a
function named
<tt>print</tt>;
the first argument of
<tt>print</tt>
is a string.
The
<tt>*</tt>
in the argument list specifies that further arguments, of
unspecified type, may be given.
<p></p>
<p>
Line 3 includes
<tt>draw.m</tt>;
only one piece of information, mentioned below,
is used from it.
Line 4 declares the variable
<tt>sys</tt>
to be of type
<tt>Sys</tt>;
its name will be visible throughout the remainder of the file
describing this module.
It will be used later to refer to an instance of the
<tt>Sys</tt>
module.
This declaration initializes it to
<tt>nil</tt>;
it still needs to be set to a useful value.
</p>
<p>
Lines 4-7 constitute the declaration of
<tt>Command</tt>,
the module being implemented.
It contains only a function named
<tt>init</tt>,
with two arguments, a
<tt>ref</tt>
<tt>Draw-&gt;Context</tt>
and a list of strings,
and it doesn't
return any value.
The
<tt>ref</tt>
<tt>Draw-&gt;Context</tt>
argument would be used if the program did any
graphics; it is a data type defined in
<tt>draw.m</tt>
and refers to the display.
Since the program just writes text, it won't be used.
The
<tt>init</tt>
function isn't special to the Limbo language,
but it is conventional in the environment,
like
<tt>main</tt>
in C.
</p>
<p>
In a module designed to be useful
to other modules in an application, it would be wise to
take the module declaration for
<tt>Command</tt>
out, put it in a separate file called
<tt>command.m</tt>
and use
<tt>include</tt>
<tt>command.m</tt>
to allow this module and others to refer to it.
It is called, for example, by the program loader in the Inferno
system to start the execution of applications.
</p>
<p>
Line 8 is a comment; everything from the
<tt>#</tt>
to the end of line is ignored.
</p>
<p>
Line 9 begins the definition for the
<tt>init</tt>
function that was promised in the module's declaration
(line 6).
The argument that is a list of strings is named
<tt>argv</tt>.
</p>
<p>
Line 11 connects the program
being written to the
<tt>Sys</tt>
module.
The first token after
<tt>load</tt>
is the target module's name as defined by its interface
(here found in the
<tt>include</tt>
on line 2)
The next token is the place
where the code for the module can be found; it is a string
that usually names a file.
Conventionally, in the Inferno system,
each module contains a constant declaration for the name
<tt>PATH</tt>
as a string that names the file where
the object module can be found.
Loading the file is performed dynamically during
execution except for a few modules built
into the execution environment.
(These include
<tt>Sys</tt>;
this accounts for the peculiar file name
<tt>$Sys</tt>
as
the value of
<tt>PATH</tt>.)
</p>
<p>
The value of
<tt>load</tt>
is a reference to the
named module; line 11 assigns it
to the variable
<tt>sys</tt>
for later use.
The
<tt>load</tt>
operator dynamically loads the code for the named
module if it is not already present and instantiates
a new instance of it.
</p>
<p>
Line 12 starts the work by printing a familiar message,
using the facilities provided by module
<tt>Sys</tt>
through its handle
<tt>sys</tt>;
the notation
<tt>sys-&gt;print(...)</tt>
means to call the
<tt>print</tt>
function of the module referred to by
<tt>sys</tt>.
The interface of
<tt>Sys</tt>
resembles a binding to some of
the mechanisms of Unix and the ISO/ANSI C library.
</p>
<p>
The loop at lines 13-14 takes the
<tt>list</tt>
<tt>of</tt>
<tt>string</tt>
argument to
<tt>init</tt>
and iterates over it using the
<tt>hd</tt>
(head) and
<tt>tl</tt>
(tail) operators.
When executed, this module combines the
traditional `Hello world' and
<tt>echo</tt>.
</p>
<h4>2 Lexical conventions
</h4>
<p>
There are several kinds of tokens:
keywords, identifiers, constants, strings, expression operators,
and other separators.
White space (blanks, tabs, new-lines) is ignored except that
it serves to separate tokens; sometimes it is required
to separate tokens.
If the input has been parsed into tokens up to a particular
character, the next token is taken to include the longest
string of characters that could constitute a token.
</p>
<p>
The native character set of Limbo is Unicode,
which is identical with the first 16-bit plane of the ISO 10646 standard.
Any Unicode character may be used in comments, or in strings
and character constants.
The implementation assumes that source files use the UTF-8 representation,
in which 16-bit Unicode characters are represented as sequences
of one, two, or three bytes.
</p>
<h4>2.1 Comments
</h4>
<p>
Comments begin with the
<tt>#</tt>
character and extend to the end of the line.
Comments are ignored.
</p>
<h4>2.2 Identifiers
</h4>
<p>
An identifier is a sequence of letters and digits
of which the first is a letter.
Letters are the Unicode characters
<tt>a</tt>
through
<tt>z</tt>
and
<tt>A</tt>
through
<tt>Z</tt>,
together with the underscore character, and
all Unicode characters with encoded values greater than 160
(A0 hexadecimal, the beginning of the range corresponding to Latin-1).
</p>
<p>
Only the first 256 characters in an identifier
are significant.
</p>
<h4>2.3 Keywords
</h4>
<p>
The following identifiers are reserved for use as keywords,
and may not be used otherwise:
</p><dl><dt></dt><dd><tt><pre> adt alt array big
break byte case chan
con continue cyclic do
else exit fn for
hd if implement import
include int len list
load module nil of
or pick real ref
return self spawn string
tagof tl to type
while
</pre></tt></dd></dl>
The word
<tt>union</tt>
is not currently used by the language.
<p></p>
<h4>2.4 Constants
</h4>
<p>
There are several kinds of constants for denoting values of the
basic types.
</p>
<p>
</p>
<h4>2.4.1 Integer constants
</h4>
<p>
Integer constants have type
<tt>int</tt>
or
<tt>big</tt>.
They can be represented in several ways.
</p>
<p>
Decimal integer constants consist of a sequence of decimal
digits.
A constant with an explicit radix
consists of a decimal radix followed by
<tt>R</tt>
or
<tt>r</tt>
followed by the digits of the number.
The radix is between 2 and 36 inclusive;
digits above 10 in the number
are expressed using letters
<tt>A</tt>
to
<tt>Z</tt>
or
<tt>a</tt>
to
<tt>z</tt>.
For example,
<tt>16r20</tt>
has value 32.
</p>
<p>
The type of a decimal or explicit-radix number is
<tt>big</tt>
if its value exceeds
<tt>2<sup>31</sup>-1</tt>,
otherwise it is
<tt>int</tt>.
</p>
<p>
Character constants consist of a single Unicode
character enclosed within single-quote characters
<tt>'</tt>.
Inside the quotes the following escape sequences represent
special characters:
</p><dl><dt></dt><dd><tt><pre><tt>\'</tt> single quote
<tt>\"</tt> double quote
<tt>\\</tt> backslash
<tt>\t</tt> tab
<tt>\n</tt> newline
<tt>\r</tt> carriage return
<tt>\b</tt> backspace
<tt>\a</tt> alert character (bell)
<tt>\v</tt> vertical tab
<tt>\u</tt><i>dddd </i>Unicode character named by 4 hexadecimal digits
<tt>\0</tt> NUL
</pre></tt></dd></dl>
Character constants have type
<tt>int</tt>.
<p></p>
<h4>2.4.2 Real constants
</h4>
<p>
Real constants consist of a sequence of decimal digits
containing one period
<tt>.</tt>
and optionally followed by
<tt>e</tt>
or
<tt>E</tt>
and then by a possibly signed integer.
If there is an explicit exponent, the period is
not required.
Real constants have type
<tt>real</tt>.
</p>
<h4>2.4.3 Strings
</h4>
<p>
String constants are sequences of Unicode characters contained in double
quotes.
They cannot extend across source lines.
The same escape sequences listed above for character
constants are usable within string constants.
Strings have type
<tt>string</tt>.
</p>
<h4>2.4.4 The nil constant
</h4>
<p>
The constant
<tt>nil</tt>
denotes a reference to nothing.
It may be used where an object of a reference
type is expected;
otherwise uninitialized values of reference type
start off with this value, it can be assigned to
reference objects, and reference types can be
tested for equality with it.
(The keyword has other uses as well.)
</p>
<h4>2.5 Operators and other separators
</h4>
<p>
The operators are
</p><dl><dt></dt><dd><tt><pre> + - * / % &amp; | ^
== &lt; &gt; &lt;= &gt;= != &lt;&lt; &gt;&gt;
&amp;&amp; || &lt;- ::
= += -= *= /= %= &amp;= |= ^= &lt;&lt;= &gt;&gt;=
:=
~ ++ -- !
</pre></tt></dd></dl>
The other separators are
<dl><dt></dt><dd><tt><pre> : ; ( ) { } [ ]
, . -&gt; =&gt;
</pre></tt></dd></dl>
<p></p>
<h4>3 Syntax notation
</h4>
<p>
In this manual, Limbo syntax is described by a modified BNF
in which syntactic categories are named in an
<i>italic</i>
font, and literals in
<tt>typewriter</tt>
font.
Alternative productions are listed on separate lines, and
an optional symbol is indicated with
the subscript ``opt.''
</p>
<h4>4 Types and objects
</h4>
<p>
Limbo has three kinds of objects.
<i>Data</i>
objects exist in the storage associated with
a module; they can be manipulated by arithmetic operations,
assignment, selection of component entities, and other concrete
operations.
Each data object has a type that determines what can be stored
in it and what operations are applicable.
</p>
<p>
The second kind of object is the
<i>function</i>.
Functions are characterized by the types of the arguments they
accept and the values they return, and are associated with
the modules in which they are defined.
Their names can be made visible in their module's declaration,
or they can be encapsulated within the
<tt>adt</tt>
(abstract data types) of their modules,
or they can exist privately within their module.
</p>
<p>
Finally, Limbo programs are organized into
<i>modules</i>:
a named collection of constants, abstract data types,
data, and functions made available by that module.
A module declaration displays the
members visible to other modules;
the module's implementation
defines both the publicly visible members and its
private parts, including the data objects it uses.
A module that wishes to use
the facilities of another includes its declaration in order to
understand what it exports, but
before using them it explicitly loads the new module.
</p>
<h4>4.1 Types
</h4>
<p>
Limbo has several basic types, some built-in higher abstractions,
and other ways of composing new types.
In declarations and some other places, constructions naming
a type are used.
The syntax is:
</p><dl><dt></dt><dd><tt><pre><br>
<i>type:
data-type
function-type
<br>
</i></pre></tt></dd></dl><i>
</i>Functions will be discussed in §7 below.
First, data types will be explored.
<p></p>
<h4>4.2 Data types
</h4>
<p>
The syntax of data types is
</p><dl><dt></dt><dd><tt><pre><br>
<i>data-type:
</i><tt>byte</tt><i>
</i><tt>int</tt><i>
</i><tt>big</tt><i>
</i><tt>real</tt><i>
</i><tt>string</tt><i>
tuple-type
</i><tt>array of </tt><i>data-type
</i><tt>list of </tt><i>data-type
</i><tt>chan of </tt><i>data-type
adt-type
</i><tt>ref </tt><i>adt-type
module-type
module-qualified-type
type-name
data-type-list:
data-type
data-type-list </i><tt>,</tt><i> data-type
<br>
</i></pre></tt></dd></dl><i>
</i>Objects of most data types have
<i>value</i>
semantics; when they
are assigned or passed to functions, the destination receives a copy of the
object.
Subsequent changes to the assigned object itself have no effect on
the original object.
The value types are
<tt>byte</tt>,
<tt>int</tt>,
<tt>big</tt>,
<tt>real</tt>,
<tt>string</tt>,
the
<tt>tuple</tt>
types, and
abstract data types or
<tt>adt</tt>.
The rest have
<i>reference</i>
semantics.
When they are assigned, the quantity actually assigned
is a reference to (a pointer to) an underlying object
that is not copied; thus changes or operations on
the assigned value affect the original object.
Reference types include lists, arrays, channels, modules, and
<tt>ref</tt>
<tt>adt</tt>
types.
<p></p>
<h4>4.2.1 Basic types
</h4>
<p>
The five basic data types are denoted by
<tt>byte</tt>,
<tt>int</tt>,
<tt>big</tt>,
<tt>real</tt>,
and
<tt>string</tt>.
</p>
<p>
Bytes are unsigned 8-bit quantities.
</p>
<p>
Integers
(<tt>int</tt>)
are 32-bit signed quantities represented in two's complement
notation.
Large integers
(<tt>big</tt>)
are 64-bit signed quantities represented in two's complement notation.
</p>
<p>
Real numbers
(<tt>real</tt>)
are 64-bit quantities represented in the
IEEE long floating notation.
</p>
<p>
The
<tt>byte</tt>,
<tt>int</tt>,
<tt>big</tt>,
and
<tt>real</tt>
types are collectively called arithmetic types.
</p>
<p>
Strings are rows of Unicode characters.
They may be concatenated and extended character-by-character.
When a string is indexed with a single subscript, it yields an integer
with the Unicode encoding of the character;
when it is indexed by a range, it yields another string.
</p>
<h4>4.2.2 Tuple type
</h4>
<p>
The
<i>tuple</i>
type, denoted
</p><dl><dt></dt><dd><tt><pre><br>
<i>tuple-type:
</i><tt>( </tt><i>data-type-list</i><tt> )</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>is a type consisting of an ordered collection of two or more objects,
each having its own data type.
For each tuple type, the types of the members are
fixed, but need not be identical;
for example, a function might return a tuple containing
an integer and a string.
Each tuple type is characterized solely by the
the order and identity of the types it contains.
Objects of tuple type may be assigned to a list of identifiers (to pick out the
components), and a parenthesized, comma-separated list of expressions
denotes a tuple.
<p></p>
<h4>4.2.3 Array types
</h4>
<p>
The
<i>array</i>
type describes a dynamically-sized row of objects, all of the same
type; it is indexed starting from 0.
An array type is denoted by
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>array of </tt><i>data-type
<br>
</i></pre></tt></dd></dl><i>
</i>The size of an array is not part of its type; instead
it is part of the value.
The
<i>data-type</i>
may itself be an array, to achieve a multidimensional array.
<p></p>
<h4>4.2.4 List types
</h4>
<p>
A
<i>list</i>
is a sequence of like-typed objects; its denotation is
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>list of </tt><i>data-type
<br>
</i></pre></tt></dd></dl><i>
</i>A list is a stack-like object, optimized for
a few operations: get the head (the first object),
get the tail (the rest of the list), place an object at the beginning.
<p></p>
<h4>4.2.5 Channel types
</h4>
<p>
A
<i>channel</i>,
whose type is written
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>chan of </tt><i>data-type
<br>
</i></pre></tt></dd></dl><i>
</i>is a communication mechanism capable of sending and receiving objects of the
specified type to another agent in the system.
Channels may be used to communicate between local processes;
using library procedures, they may be connected
to named destinations.
In either case
<i>send</i>
and
<i>receive</i>
operations may be directed to them.
For example,
<dl><dt></dt><dd><tt><pre> chan of (int, string)
</pre></tt></dd></dl>
is the type of a channel that transmits tuples consisting of
an integer and an string.
Once an instance of such a channel (say
<tt>c</tt>)
has been declared and initialized,
the statement
<dl><dt></dt><dd><tt><pre> c &lt;-= (123, "Hello");
</pre></tt></dd></dl>
sends such a tuple across it.
<p></p>
<h4>4.2.6 Abstract data types
</h4>
<p>
An abstract data type or
<i>adt</i>
is an object that can contain data objects of several
different types and declare
functions that operate on them.
The syntax for declaring an
<tt>adt</tt>
is given later.
Once an
<tt>adt</tt>
has been declared, the identifier associated with it
becomes a data-type name.
</p><dl><dt></dt><dd><tt><pre><br>
<i>adt-type:
identifier
module-qualified-type
<br>
</i></pre></tt></dd></dl><i>
</i><p></p>
<p>
There is also a
<tt>ref</tt>
<tt>adt</tt>
type representing a reference (pointer) to an
<tt>adt</tt>.
It is denoted
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>ref </tt><i>adt-type
<br>
</i></pre></tt></dd></dl><i>
</i>where the identifier is the name of an
<tt>adt</tt>
type.
<p></p>
<h4>4.2.7 Module types
</h4>
<p>
A module type name is an identifier:
</p><dl><dt></dt><dd><tt><pre><br>
<i>module-type:
identifier
<br>
</i></pre></tt></dd></dl><i>
</i>The identifier is declared as a module identifier by a
<i>module-declaration</i>,
as described in §6.5 below.
An object of module type serves as a handle for the
module, and is used to access its functions.
<p></p>
<h4>4.2.8 Module-qualified type
</h4>
<p>
When an
<tt>adt</tt>
is declared within a module declaration, the type name of that
<tt>adt</tt>
is not generally visible to the rest of the program unless a specific
<tt>import</tt>
request is given (see §6.6, §10 below).
Without such a request, when
<tt>adt</tt>
objects implemented by a module are declared by a client
of that module, the
<tt>adt</tt>
type name is qualified:
</p><dl><dt></dt><dd><tt><pre><br>
<i>module-qualified-type:
identifier </i><tt>-&gt;</tt><i> identifier
<br>
</i></pre></tt></dd></dl><i>
</i>Here the first identifier is either the name of a module
or a variable of the module type;
the second is the name of a type
mentioned in the module declaration.
<p></p>
<h4>4.2.9 Named types
</h4>
<p>
Finally, data types may be named, using a
<tt>type</tt>
declaration; this is discussed in §6.4 below.
</p><dl><dt></dt><dd><tt><pre><br>
<i>type-name:
identifier
<br>
</i></pre></tt></dd></dl><i>
</i><p></p>
<h4>4.3 Function types
</h4>
<p>
A function type characterizes the arguments and return value of
a function. The syntax is
</p><dl><dt></dt><dd><tt><pre><br>
<i>function-type:
</i><tt>fn </tt><i>function-arg-ret
function-arg-ret:
</i><tt>( </tt><i>formal-arg-list</i><i>opt</i><tt> )
</tt><tt>( </tt><i>formal-arg-list</i><i>opt</i><tt> ) : </tt><i>data-type
formal-arg-list:
formal-arg
formal-arg-list</i><tt> , </tt><i>formal-arg
formal-arg:
nil-or-</i><i>D-list</i><tt> : </tt><i>type
nil-or-</i><i>D</i><tt> : self ref</tt><i>opt </i><i>identifier
nil-or-</i><i>D</i><tt> : self </tt><i>identifier
</i><tt>*</tt><i>
nil-or-</i><i>D-list:
nil-or-</i><i>D
nil-or-</i><i>D-list </i><tt>, </tt><i>nil-or-</i><i>D
nil-or-</i><i>D:
identifier
</i><tt>nil</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>That is, the denotation of a function type has the keyword
<tt>fn</tt>
followed by a comma-separated list of its arguments
enclosed in parentheses,
and perhaps followed by the type the function returns.
Absence of a return value means that the function returns no
value: it is a procedure.
The names and types of arguments are specified.
However, the name of an argument may be replaced by
<tt>nil</tt>;
in this case it is nameless.
For example,
<dl><dt></dt><dd><tt><pre> fn (nil: int, nil: int): int
fn (radius: int, angle: int): int
fn (radius, angle: int): int
</pre></tt></dd></dl>
all denote exactly the same type,
namely a function of two integers that returns an integer.
As another example,
<dl><dt></dt><dd><tt><pre> fn (nil: string)
</pre></tt></dd></dl>
is the type of a function that takes a string argument
and returns no value.
<p></p>
<p>
The
<tt>self</tt>
keyword has a specialized use within
<tt>adt</tt>
declarations.
It may be used only for the first argument
of a function declared within an
<tt>adt</tt>;
its meaning is discussed in §6.3 below.
</p>
<p>
The star character
<tt>*</tt>
may be given as the last argument in a function type.
It declares that
the function is variadic; during a call, actual arguments at its
position and following are passed in a manner
unspecified by the language.
For example, the type of the
<tt>print</tt>
function of the
<tt>Sys</tt>
module is
</p><dl><dt></dt><dd><tt><pre> fn (s: string, *): int
</pre></tt></dd></dl>
This means that the first argument of
<tt>print</tt>
is a string and that other arguments may be given when the function
is called.
The Limbo language itself has no way of accessing these arguments;
the notation is an artifice for describing facilities
built into the runtime system, such as the
<tt>Sys</tt>
module.
<p></p>
<h4>5 Limbo programs
</h4>
<p>
Limbo source programs that implement modules are stored in files,
conventionally named with the suffix
<tt>.b</tt>.
Each such file begins with a single
<tt>implement</tt>
directive naming the type of the module being implemented,
followed by a sequence of declarations.
Other files, conventionally named with the suffix
<tt>.m</tt>,
contain declarations for things obtainable from other modules.
These files are incorporated by an
<tt>include</tt>
declaration in the implementation modules that need them.
At the top level, a program consists of a sequence
of declarations.
The syntax is
</p><dl><dt></dt><dd><tt><pre><br>
<i>program:
</i><tt>implement </tt><i>identifier</i><tt> ; </tt><i>top-declaration-sequence
top-declaration-sequence:
top-declaration
top-declaration-sequence top-declaration
top-declaration:
declaration
identifier-list</i><tt> := </tt><i>expression</i><tt> ;</tt><i>
identifier-list</i><tt> = </tt><i>expression</i><tt> ;</tt><i>
</i><tt>( </tt><i>identifier-list</i><tt> ) := </tt><i>expression</i><tt> ;</tt><i>
module-declaration
function-definition
adt-declaration
<br>
</i></pre></tt></dd></dl><i>
</i>The
<tt>implement</tt>
declaration at the start identifies the type of the module that
is being implemented.
The rest of the program consists of a sequence of various kinds of
declarations and definitions that announce the names
of data objects, types, and functions, and also create
and initialize them.
It must include a module declaration for the module
being implemented and the objects it announces,
and may also include declarations for the functions, data
objects, types, and constants used privately within the module
as well as declarations for modules used by it.
<p></p>
<p>
Declarations are used both at the top
level (outside of functions) and also inside functions
and module declarations.
Some styles of declaration
are allowed only in certain of these places,
but all will be discussed together.
</p>
<h4>6 Declarations
</h4>
<p>
Declarations take several forms:
</p><dl><dt></dt><dd><tt><pre><br>
<i>declaration:
identifier-list</i><tt> : </tt><i>type</i><tt> ;</tt><i>
identifier-list</i><tt> : </tt><i>type</i><tt> = </tt><i>expression</i><tt> ;</tt><i>
identifier-list</i><tt> : con </tt><i>expression</i><tt> ;</tt><i>
</i><i>identifier-list</i><tt> : import </tt><i>identifier </i><tt>;</tt><i>
identifier-list</i><tt> : type</tt><i> type</i><tt> ;</tt><i>
</i><tt>include </tt><i>string-constant</i><tt> ;</tt><i>
identifier-list:
identifier
identifier-list</i><tt> , </tt><i>identifier
expression-list:
expression
expression-list</i><tt> , </tt><i>expression
<br>
</i></pre></tt></dd></dl><i>
</i><p></p>
<h4>6.1 Data declarations
</h4>
<p>
These forms constitute the basic way to declare and
initialize data:
</p><dl><dt></dt><dd><tt><pre><br>
<i> identifier-list</i><tt> : </tt><i>type</i><tt> ;</tt><i>
identifier-list</i><tt> : </tt><i>type</i><tt> = </tt><i>expression</i><tt> ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>A comma-separated sequence of identifiers is followed by a colon
and then the name of a type.
Each identifier is declared as having that type and denotes a
particular object
for rest of its scope (see §11 below).
If the declaration contains
<tt>=</tt>
and an expression, the type must be a data type, and
all the objects are initialized from
the value of the expression.
In a declaration at the top level
(outside of a function), the expression must be
constant (see §8.5) or an array initialized with constant expressions;
the bound of any array must be a constant expression.
Lists and
<tt>ref</tt>
<tt>adt</tt>
types may not be initialized at the top level.
If an object is not explicitly initialized, then
it is always set to
<tt>nil</tt>
if it has a reference type;
if it has arithmetic type, then it is set to 0
at the top level and is undefined if it occurs
within a function.
<p></p>
<p>
For example,
</p><dl><dt></dt><dd><tt><pre> i, j: int = 1;
r, s: real = 1.0;
</pre></tt></dd></dl>
declares
<tt>i</tt>
and
<tt>j</tt>
as integers,
<tt>r</tt>
and
<tt>s</tt>
as real.
It sets
<tt>i</tt>
and
<tt>j</tt>
to 1,
and
<tt>r</tt>
and
<tt>s</tt>
to 1.0.
<p></p>
<p>
Another kind of declaration is a shorthand.
In either of
</p><dl><dt></dt><dd><tt><pre><br>
<i> identifier</i><tt> := </tt><i>expression</i><tt> ;</tt><i>
</i><tt>( </tt><i>identifier-list</i><tt> ) := </tt><i>expression</i><tt> ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>identifiers on the left are declared using the type of the expression,
and are initialized with the value of the expression.
In the second case, the expression must be a tuple or an
<tt>adt</tt>,
and the types and values attributed to the identifiers
in the list are taken from the members of the tuple, or the
data members of the
<tt>adt</tt>
respectively.
For example,
<dl><dt></dt><dd><tt><pre> x: int = 1;
</pre></tt></dd></dl>
and
<dl><dt></dt><dd><tt><pre> x := 1;
</pre></tt></dd></dl>
are the same.
Similarly,
<dl><dt></dt><dd><tt><pre> (p, q) := (1, 2.1);
</pre></tt></dd></dl>
declares the identifiers on the left as
<tt>int</tt>
and
<tt>real</tt>
and initializes them to 1 and 2.1 respectively.
Declarations with
<tt>:=</tt>
can also be expressions, and are discussed again in §8.4.4 below.
<p></p>
<h4>6.2 Constant declarations
</h4>
<p>
The
<tt>con</tt>
declaration
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><i>identifier-list</i><tt> : con</tt><i> expression</i><tt> ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>declares a name (or names) for constants.
The
<i>expression</i>
must be constant (see §8.5).
After the declaration,
each identifier in the list may be used anywhere a constant
of the appropriate type is needed;
the type is taken from the type of the constant.
For example, after
<dl><dt></dt><dd><tt><pre> Seven: con 3+4;
</pre></tt></dd></dl>
the name
<tt>Seven</tt>
is exactly the same as the constant 7.
<p></p>
<p>
The identifier
<tt>iota</tt>
has a special meaning in the expression in a
<tt>con</tt>
declaration.
It is equivalent to the integer constant
<tt>0</tt>
when evaluating the expression for the first (leftmost) identifier declared,
<tt>1</tt>
for the second, and so on numerically.
For example, the declaration
</p><dl><dt></dt><dd><tt><pre> M0, M1, M2, M3, M4: con (1&lt;&lt;iota);
</pre></tt></dd></dl>
declares several constants
<tt>M0</tt>
through
<tt>M4</tt>
with the values 1, 2, 4, 8, 16 respectively.
<p></p>
<p>
The identifier
<tt>iota</tt>
is not reserved except inside the expression
of the
<tt>con</tt>
declaration.
</p>
<h4>6.3 adt declarations
</h4>
<p>
An
<tt>adt</tt>
or abstract data type contains data objects and functions that
operate on them.
The syntax is
</p><dl><dt></dt><dd><tt><pre><br>
<i>adt-declaration:
</i><i>identifier</i><tt> : adt { </tt><i>adt-member-list</i><i>opt</i><tt> } ;</tt><i>
adt-member-list:
adt-member
adt-member-list adt-member
adt-member:
identifier-list</i><tt> : cyclic</tt><i>opt </i><i>data-type</i><tt> ;</tt><i>
identifier-list</i><tt> : con </tt><i>expression</i><tt> ;</tt><i>
identifier-list</i><tt> : </tt><i>function-type</i><tt> ;</tt><i>
</i><tt>pick { </tt><i>pick-member-list</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>After an
<i>adt-declaration</i>,
the identifier becomes the name of the type of that
<tt>adt</tt>.
For example, after
<dl><dt></dt><dd><tt><pre> Point: adt {
x, y: int;
add: fn (p: Point, q: Point): Point;
eq: fn (p: Point, q: Point): int;
};
</pre></tt></dd></dl>
the name
<tt>Point</tt>
is a type name for an
<tt>adt</tt>
of two integers and two
functions; the fragment
<dl><dt></dt><dd><tt><pre> r, s: Point;
xcoord: int;
...
xcoord = s.x;
r = r.add(r, s);
</pre></tt></dd></dl>
makes sense.
The first assignment selects one of the data members of
<tt>s</tt>;
the second calls one of the function members of
<tt>r</tt>.
<p></p>
<p>
As this example indicates,
<tt>adt</tt>
members are accessed by mentioning an object with the
<tt>adt</tt>
type, a dot, and then the name of the member;
the details will be discussed in §8.13 below.
A special syntactic indulgence is available for functions declared within an
<tt>adt</tt>:
frequently such a function
receives as an argument the same object used to access it
(that is, the object before the dot).
In the example just above,
<tt>r</tt>
was both the object being operated on and the first argument to the
<tt>add</tt>
function.
If the first formal argument of a function declared in an
<tt>adt</tt>
is marked with the
<tt>self</tt>
keyword, then in any calls to the function, the
<tt>adt</tt>
object is implicitly passed to the function, and
is not mentioned explicitly in the actual argument list
at the call site.
For example, in
</p><dl><dt></dt><dd><tt><pre> Rect: adt {
min, max: Point;
contains: fn(r: self Rect, p: Point): int;
};
r1: Rect;
p1: Point;
...
if (r1.contains(p1)) ...
</pre></tt></dd></dl>
because the first argument of the
<tt>contains</tt>
function is declared with
<tt>self</tt>,
the subsequent call to it automatically passes
<tt>r1</tt>
as its first argument. The
<tt>contains</tt>
function itself is defined elsewhere with this first
argument explicit.
(This mechanism is analogous to the
<i>this</i>
construct in C++ and other languages,
but puts the special-casing at the declaration site and makes it explicit.)
<p></p>
<p>
If
<tt>self</tt>
is specified in the declaration of a function, it must also be
specified in the definition as well. For example,
<tt>contains</tt>
would be defined
</p><dl><dt></dt><dd><tt><pre> Rect.contains(r: self Rect, p: Point)
{
. . .
}
</pre></tt></dd></dl>
<p></p>
<p>
The
<tt>adt</tt>
type in Limbo
does not provide control over the visibility
of its individual members; if any are accessible, all are.
</p>
<p>
Constant
<tt>adt</tt>
members follow the same rules as ordinary constants (§6.2).
</p>
<p>
The
<tt>cyclic</tt>
modifier will be discussed in §11.1.
</p>
<h4>6.3.1 pick adts
</h4>
<p>
An
<tt>adt</tt>
which contains a
<tt>pick</tt>
member is known as a
<i>pick</i>
<i>adt</i>.
A
<tt>pick</tt>
<tt>adt</tt>
is Limbo's version of a
<i>discriminated union</i>.
An
<tt>adt</tt>
can only contain one
<tt>pick</tt>
member and it must be the last component of the
<tt>adt</tt>.
Each
<i>identifier</i>
enumerated in the
<i>pick-tag-list</i>
names a variant type of the
<tt>pick</tt>
<tt>adt</tt>.
The syntax is
</p><dl><dt></dt><dd><tt><pre><br>
<i>pick-member-list:
pick-tag-list</i><tt> =&gt;</tt><i>
pick-member-list pick-tag-list</i><tt> =&gt;</tt><i>
pick-member-list identifier-list</i><tt> : cyclic</tt><i>opt </i><i>data-type</i><tt> ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>pick-tag-list:
identifier
pick-tag-list</i><tt> or </tt><i>identifier
<br>
</i></pre></tt></dd></dl><i>
</i><p></p>
<p>
The
<i>pick-member-list</i>
contains a set of data members for each
<i>pick-tag-list</i>.
These data members are specific to those variants of the
<tt>pick</tt>
<tt>adt</tt>
enumerated in the
<i>pick-tag-list</i>.
The
<tt>adt</tt>
data members found outside of the
<tt>pick</tt>
are common to all variants of the
<tt>adt</tt>.
A
<tt>pick</tt>
<tt>adt</tt>
can only be used as a
<tt>ref</tt>
<tt>adt</tt>
and can only be initialized from a value of one of its variants.
For example, if
<tt>Constant</tt>
is a
<tt>pick</tt>
<tt>adt</tt>
and
<tt>Constant.Real</tt>
is one of its variant types then
</p><dl><dt></dt><dd><tt><pre> c : ref Constant = ref Constant.Real("pi", 3.1);
</pre></tt></dd></dl>
will declare
<tt>c</tt>
to have type
<tt>ref</tt>
<tt>Constant</tt>
and initialize it with a value of the variant type
<tt>ref</tt>
<tt>Constant.Real</tt>.
<p></p>
<h4>6.4 Type declarations
</h4>
<p>
The type declaration
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><i>identifier-list</i><tt> : type</tt><i> data-type ;</i><i>
<br>
</i></pre></tt></dd></dl><i>
</i>introduces the identifiers as synonyms for the
given type.
Type declarations are transparent; that is,
an object declared with the newly-named
type has the same type as the one it abbreviates.
<p></p>
<h4>6.5 Module declarations
</h4>
<p>
A module declaration collects and packages declarations of
<tt>adt</tt>,
functions, constants and simple types, and creates an
interface with a name
that serves to identify the type of the module.
The syntax is
</p><dl><dt></dt><dd><tt><pre><br>
<i>module-declaration:
</i><i>identifier</i><tt> : module { </tt><i>mod-member-list</i><i>opt</i><tt> } ;</tt><i>
mod-member-list:
mod-member
mod-member-list mod-member
mod-member:
identifier-list</i><tt> : </tt><i>function-type</i><tt> ;</tt><i>
identifier-list</i><tt> : </tt><i>data-type</i><tt> ;</tt><i>
adt-declaration</i><tt> ;</tt><i>
identifier-list</i><tt> : con </tt><i>expression </i><tt>;</tt><i>
identifier-list</i><tt> : type </tt><i>type </i><tt>;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>After a module declaration, the named
<i>identifier</i>
becomes the name of the type of that module.
For example, the declaration
<dl><dt></dt><dd><tt><pre>Linear: module {
setflags: fn (flag: int);
TRUNCATE: con 1;
Vector: adt {
v: array of real;
add: fn (v1: self Vector, v2: Vector): Vector;
cross: fn (v1: self Vector, v2: Vector): Vector;
dot: fn (v1: self Vector, v2: Vector);
make: fn (a: array of real): Vector;
};
Matrix: adt {
m: array of array of real;
add: fn (m1: self Matrix, m2: Matrix): Matrix;
mul: fn (m1: self Matrix, m2: Matrix): Matrix;
make: fn (a: array of array of real): Matrix;
};
};
</pre></tt></dd></dl>
is a module declaration for a linear algebra package that
implements two
<tt>adt</tt>,
namely
<tt>Vector</tt>
and
<tt>Matrix</tt>,
a constant,
and a function
<tt>setflags</tt>.
The name
<tt>Linear</tt>
is the type name for the module, and it may be used to declare
an object referring to an instance of the module:
<dl><dt></dt><dd><tt><pre> linearmodule: Linear;
</pre></tt></dd></dl>
Before the module can be used, it must be loaded, for example in
the style:
<dl><dt></dt><dd><tt><pre> linearmodule = load Linear "/usr/dmr/limbo/linear.dis";
if (linearmodule == nil) {
sys-&gt;print("Can't load Linear\n");
exit;
}
</pre></tt></dd></dl>
The
<tt>load</tt>
operator is discussed more fully in §8.4.5 below.
<p></p>
<p>
To initialize data declared as part of a module
declaration, an assignment expression may be used at the top level.
For example:
</p><dl><dt></dt><dd><tt><pre> implement testmod;
testmod: module {
num: int;
};
. . .
num = 5;
</pre></tt></dd></dl>
The right side of the assignment must be a constant expression (§8.5).
<p></p>
<h4>6.6 Declarations with
<tt>import</tt>
</h4>
<p>
These declarations take the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><i>identifier-list</i><tt> : import </tt><i>identifier </i><tt>;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>Identifiers for entities
declared within a module declaration are normally
meaningful only in a context that
identifies the module.
The
<tt>import</tt>
declaration lifts the names of specified members of a module
directly into the current scope.
The use of
<tt>import</tt>
will be discussed more fully in §8.1.4 below, after the syntax
for expressions involving modules has been presented.
<p></p>
<h4>6.7 Declarations with
<tt>include</tt>
</h4>
<p>
The string following the
<tt>include</tt>
keyword names
a file, which is inserted into the program's
text at that point.
The included
text is treated like text literally present.
Conventionally, included files declare
module interfaces and are named with the suffix
<tt>.m</tt>.
The directories to be searched for included files
may be specified to the Limbo compiler command.
Include files may be nested.
</p>
<h4>7 Function definitions
</h4>
<p>
All executable code
is supplied as part of a function definition.
The syntax is
</p><dl><dt></dt><dd><tt><pre><br>
<i>function-definition:
function-name-part function-arg-ret</i><tt> { </tt><i>statements</i><tt> }</tt><i>
function-name-part:
identifier
function-name-part</i><tt> . </tt><i>identifier
<br>
</i></pre></tt></dd></dl><i>
</i>The syntax of the statements in a function will be discussed in §9 below.
As a brief example,
<dl><dt></dt><dd><tt><pre> add_one(a: int): int
{
return a+1;
}
</pre></tt></dd></dl>
is a simple function
that might be part of the top level of a module.
<p></p>
<p>
Functions that are declared within an
<tt>adt</tt>
use the qualified form of definition:
</p><dl><dt></dt><dd><tt><pre> Point: adt {
x, y: int;
add: fn (p: Point, q: Point): Point;
eq: fn (p: Point, q: Point): int;
}
. . .
Point.add(p: Point, q: Point): Point
{
return Point(p.x+q.x, p.y+q.y);
}
</pre></tt></dd></dl>
Because an
<tt>adt</tt>
may contain an
<tt>adt</tt>,
more than one qualification is possible.
<p></p>
<h4>8 Expressions
</h4>
<p>
Expressions in Limbo resemble those of C, although some
of the operators are different.
The most salient difference between Limbo's expression
semantics and those of C is that Limbo
has no automatic coercions between types; in Limbo every
type conversion is explicit.
</p>
<h4>8.1 Terms
</h4>
<p>
The basic elements of expressions are terms:
</p><dl><dt></dt><dd><tt><pre><br>
<i>term:
identifier
constant
real-constant
string-constant
</i><tt>nil</tt><i>
</i><tt>( </tt><i>expression-list</i><tt> )</tt><i>
term</i><tt> . </tt><i>identifier
term</i><tt> -&gt; </tt><i>term
term</i><tt> ( </tt><i>expression-list</i><i>opt</i><tt> )</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> ]</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> : </tt><i>expression</i><tt> ]</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> : ]</tt><i>
term</i><tt> ++</tt><i>
term</i><tt> --</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>The operators on terms all associate to the left,
and their order of precedence, with tightest listed first, is as follows:
<dl><dt></dt><dd><tt><pre> .
-&gt;
() [] ++ --
</pre></tt></dd></dl>
<p></p>
<h4>8.1.1 Simple terms
</h4>
<p>
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.
</p>
<h4>8.1.2 Parenthesized terms
</h4>
<p>
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.
</p>
<h4>8.1.3 Selection
</h4>
<p>
A term of the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> term</i><tt> . </tt><i>identifier
<br>
</i></pre></tt></dd></dl><i>
</i>denotes selection of a member of an
<tt>adt</tt>.
The term must be a
type name or yield an object;
its type must be
<tt>adt</tt>
or
<tt>ref</tt>
<tt>adt</tt>;
the identifier must be a member of the
<tt>adt</tt>.
The result denotes the named member (either a data object
or a function).
<p></p>
<h4>8.1.4 Module qualification
</h4>
<p>
A term of the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> term</i><tt> -&gt; </tt><i>term
<br>
</i></pre></tt></dd></dl><i>
</i>denotes module qualification.
The first term identifies a module: either it is a module type name,
or it is an expression of module type.
The second term is a constant name, type, or function specified within
that module's declaration.
Either the module type name or
an object of the module's type suffices to qualify constants and types;
functions directly exported by the module or contained within its
<tt>adt</tt>
must be qualified by an object of the module's type, initialized with
<tt>load</tt>.
<p></p>
<p>
An example using an abridged version of an example above: given
</p><dl><dt></dt><dd><tt><pre> Linear: module {
setflags: fn(flag: int);
TRUNCATE: con 1;
Vector: adt {
make: fn(v: array of real): Vector;
v: array of real;
};
};
</pre></tt></dd></dl>
one might say
<dl><dt></dt><dd><tt><pre> lin := load Linear "/dis/linear.dis";
a: array of real;
v1: lin-&gt;Vector;
v2: Linear-&gt;Vector;
lin-&gt;setflags(Linear-&gt;TRUNCATE);
v1 = lin-&gt;(Linear-&gt;Vector).make(a);
v1 = lin-&gt;v1.make(a);
v1 = lin-&gt;v1.add(v1);
v1.v = nil;
</pre></tt></dd></dl>
Here, the declarations for
<tt>v1</tt>
and
<tt>v2</tt>
are equivalent; either a module type name (here,
<tt>Linear</tt>)
or a handle (here,
<tt>lin</tt>)
suffices to identify the module.
In the call to
<tt>setflags</tt>,
a handle
is required for the call itself;
the type name is sufficient for the constant.
<p></p>
<p>
When calling a function associated with an
<tt>adt</tt>
of another module, it is necessary to identify
both the module and the
<tt>adt</tt>
as well as the function.
The two calls to the
<tt>make</tt>
function illustrate two ways of doing this.
In the first,
</p><dl><dt></dt><dd><tt><pre> v1 = lin-&gt;(Linear-&gt;Vector).make(a);
</pre></tt></dd></dl>
the module handle
<tt>lin</tt>
is specified first, then
the type name of the
<tt>Vector</tt>
<tt>adt</tt>
within it, and then the function.
In the second call
<dl><dt></dt><dd><tt><pre> v1 = lin-&gt;v1.make(a);
</pre></tt></dd></dl>
instead of using a type name to specify the
<tt>adt</tt>,
an instance of an object of the appropriate type is
used instead.
In the first example, the parentheses are required because
the qualification operators associate to the left.
<dl><dt></dt><dd><tt><pre> v1 = lin-&gt;Vector.make(a); # Wrong
v1 = lin-&gt;Linear-&gt;Vector.make(a); # Wrong
</pre></tt></dd></dl>
The first is wrong because the same
<tt>lin</tt>
can't serve as a qualifier for both the type and the call;
the second is wrong because
<tt>lin-&gt;Linear</tt>
is meaningless.
<p></p>
<p>
Using
<tt>import</tt>
makes the code less verbose:
</p><dl><dt></dt><dd><tt><pre> 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;
</pre></tt></dd></dl>
<p></p>
<h4>8.1.5 Function calls
</h4>
<p>
The interpretation of an expression in the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> term</i><tt> ( </tt><i>expression-list</i><i>opt</i><tt> )
<br>
</tt></pre></tt></dd></dl><tt>
</tt>depends on the declaration of the term.
If it is the (perhaps qualified) name of an
<tt>adt</tt>,
then the expression is a cast; this is discussed in §8.2.11 below.
If the term is the (perhaps qualified) name of a function,
the expression means a function call; this is discussed here.
<p></p>
<p>
A plain identifier as the
<i>term</i>
names a function defined
in the current module or imported into it.
A term qualified by using the selection operator
<tt>.</tt>
specifies a function member of an
<tt>adt</tt>;
a term using
<tt>-&gt;</tt>
specifies a function defined in another module.
</p>
<p>
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.
</p>
<p>
Function calls may be directly or indirectly recursive;
objects declared within each function are distinct from
those in their dynamic predecessors.
</p>
<p>
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).
</p>
<h4>8.1.6 Subscripting and slicing
</h4>
<p>
In a term of the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> term</i><tt> [ </tt><i>expression</i><tt> ]</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>the first term must be an array or a string, and the
bracketed expression must have
<tt>int</tt>
type.
The whole term
designates a member of the array or string, indexed by the bracketed expression;
the index origin is 0.
For an array, the type of the whole term is
the type from which the array is constructed;
for a string, the type is an
<tt>int</tt>
whose value is the Unicode character at that position in the string.
<p></p>
<p>
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.)
</p>
<p>
In a term of the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> term</i><tt> [ </tt><i>expression</i><tt> : </tt><i>expression</i><tt> ]</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>the first term must be an array or a string, and the whole term
denotes a slice of it.
The first expression is the lower bound, and the second
is the upper.
If
<tt>e1</tt>
is the first expression and
<tt>e2</tt>
is the second, then in
<tt>a[e1:e2]</tt>
it must be the case that
<tt>0&lt;=e1, e1&lt;=e2, e2&lt;=len a</tt>,
where
<tt>len</tt>
gives the number of elements in the array or string.
When the term is an array, the value is an
array of the same type beginning at the indicated
lower bound and extending to the element just before
the upper bound.
When the term is a string, the value is similarly the substring
whose first character is indexed by the lower bound
and whose last character lies just before the upper bound.
<p></p>
<p>
Thus, for both arrays and strings, the number of elements in
<tt>a[e1:e2]</tt>
is equal to
<tt>e2-e1</tt>.
</p>
<p>
A slice of the form
<tt>a[e:]</tt>
means
<tt>a[e:len a].</tt>
</p>
<p>
When a string slice is assigned to another string or passed as an
argument, a copy of its value is made.
</p>
<p>
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.
</p>
<p>
In general, slice expressions cannot be the subject of
assignments.
However, as a special case, an array slice expression of the
form
<tt>a[e1:]</tt>
may be assigned to.
This is discussed in §8.4.1.
</p>
<p>
The following example shows how slices
can be used to accomplish what would
need to be done with pointer arithmetic in C:
</p><dl><dt></dt><dd><tt><pre> fd := sys-&gt;open( ... );
want := 1024;
buf := array[want] of byte;
b := buf[0:];
while (want&gt;0) {
got := sys-&gt;read(fd, b, want);
if (got&lt;=0)
break;
b = b[got:];
want -= got;
}
</pre></tt></dd></dl>
Here the array
<tt>buf</tt>
is filled by successive calls to
<tt>sys-&gt;read</tt>
that may supply fewer bytes than requested; each call
stores up to
<tt>want</tt>
bytes
starting at
<tt>b[0]</tt>,
and returns the number of bytes stored.
The invariant is that the slice
<tt>b</tt>
always refers to the part of the array still to be stored into.
<p></p>
<h4>8.1.7 Postfix increment and decrement
</h4>
<p>
A term of the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> term</i><tt> ++</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>is called a
<i>post-increment</i>.
The term must be an lvalue (see §8.4 below) and must have an
arithmetic type.
The type and value of the whole term is
that of the incremented term.
After the value is taken, 1 of the appropriate
type is added to the lvalue.
The result is undefined if the same object is changed
more than once in the same expression.
<p></p>
<p>
The term
</p><dl><dt></dt><dd><tt><pre><br>
<i> term</i><tt> --</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>behaves analogously to the increment case except
that 1 is subtracted from the lvalue.
<p></p>
<p>
</p>
<h4>8.2 Monadic expressions
</h4>
<p>
Monadic expressions are expressions with
monadic operators, together with a few more
specialized notations:
</p><dl><dt></dt><dd><tt><pre><br>
<i>monadic-expression:
term
monadic-operator monadic-expression
</i><tt>array [ </tt><i>expression</i><tt> ] of </tt><i>data-type
</i><tt>array [ </tt><i>expression</i><i>opt</i><tt> ] of { </tt><i>init-list</i><tt> }</tt><i>
</i><tt>list of { </tt><i>expression-list</i><tt> }</tt><i>
</i><tt>chan of </tt><i>data-type
data-type monadic-expression
monadic-operator: one of
</i><tt>+ - ! ~ ref * ++ -- &lt;- hd tl len</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><p></p>
<h4>8.2.1 Monadic additive operators
</h4>
<p>
The
<tt>-</tt>
operator produces the negative of its operand, which
must have an arithmetic type.
The type of the result is the same as the type of
its operand.
</p>
<p>
The
<tt>+</tt>
operator has no effect; it is supplied only for
symmetry.
However, its argument must have an arithmetic type
and the type of the result is the same.
</p>
<h4>8.2.2 Logical negation
</h4>
<p>
The
<tt>!</tt>
operator yields the
<tt>int</tt>
value 1 if its operand
has the value 0, and yields 0 otherwise.
The operand must have type
<tt>int</tt>.
</p>
<h4>8.2.3 One's complement
</h4>
<p>
The
<tt>~</tt>
operator yields the 1's complement of its
operand, which must have type
<tt>int</tt>
or
<tt>byte</tt>.
The type of the result is the same as that of its operand.
</p>
<h4>8.2.4 Reference and indirection operators
</h4>
<p>
If
<i>e</i>
is an expression of an
<tt>adt</tt>
type, then
<tt>ref</tt>
<i>e</i>
is an expression of
<tt>ref</tt>
<tt>adt</tt>
type whose value refers to (points to) an anonymous object with value
<i>e</i>.
The
<tt>ref</tt>
operator differs from the unary
<tt>&amp;</tt>
operator of C; it makes a new object and returns a reference
to it, rather than generating a reference to an existing object.
</p>
<p>
If
<i>e</i>
is an expression of type
<tt>ref</tt>
<tt>adt</tt>,
then
<tt>*</tt>
<i>e</i>
is the value
of the
<tt>adt</tt>
itself.
The value of
<i>e</i>
must not be
<tt>nil</tt>.
</p>
<p>
For example, in
</p><dl><dt></dt><dd><tt><pre> Point: adt { ... };
p: Point;
pp: ref Point;
p = Point(1, 2);
pp = ref p; # pp is a new Point; *pp has value (1, 2)
p = Point(3, 4); # This makes *pp differ from p
*pp = Point(4, 5); # This does not affect p
</pre></tt></dd></dl>
the expression
<tt>*pp</tt>
at first refers to a copy of the value stored in
<tt>p</tt>,
so
<tt>*pp == p</tt>
is true; however, when
<tt>p</tt>
is changed later,
<tt>*pp</tt>
does not change.
<p></p>
<h4>8.2.5 Prefix increment and decrement
</h4>
<p>
A monadic expression of the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>++ </tt><i>monadic-expression
<br>
</i></pre></tt></dd></dl><i>
</i>is called a
<i>pre-increment</i>.
The monadic expression must be an lvalue (see §8.4 below) and must have an
arithmetic type.
Before the value is taken, 1 of the appropriate type
is added to the lvalue.
The type and value of the whole expression is
that of the now incremented term.
The result is undefined if the same object is changed
more than once in the same expression.
<p></p>
<p>
The term
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>-- </tt><i>monadic-expression
<br>
</i></pre></tt></dd></dl><i>
</i>behaves analogously to the increment case except
that 1 is subtracted from the lvalue.
<p></p>
<p>
</p>
<h4>8.2.6 Head and tail
</h4>
<p>
The operand of the
<tt>hd</tt>
operator must be a non-empty list.
The value is the first member of the list
and has that member's type.
</p>
<p>
The operand of the
<tt>tl</tt>
operator must be a non-empty list.
The value is the tail of the list,
that is, the part of the list after its
first member.
The tail of a list with one member is
<tt>nil</tt>.
</p>
<h4>8.2.7 Length
</h4>
<p>
The operand of the
<tt>len</tt>
operator is a string, an array, or a list.
The value is an
<tt>int</tt>
giving the number of elements currently in the item.
</p>
<h4>8.2.8 Tagof
</h4>
<p>
The operand of the
<tt>tagof</tt>
operator is a monadic expression of type
<tt>ref</tt>
<tt>adt</tt>
that refers to a
<tt>pick</tt>
<tt>adt</tt>.
or the type name of a
<tt>pick</tt>
<tt>adt</tt>
or one of its variants.
The value is an
<tt>int</tt>
giving a unique value for each of the variants and for the
<tt>pick</tt>
<tt>adt</tt>
type itself.
</p>
<h4>8.2.9 Channel communication
</h4>
<p>
The operand of the communication operator
<tt>&lt;-</tt>
has type
<tt>chan</tt>
<tt>of</tt>
<i>sometype</i>.
The value of the expression
is the first unread object previously sent over that
channel, and has the type associated with the channel.
If the channel is empty, the program delays
until something is sent.
</p>
<p>
As a special case, the operand of
<tt>&lt;-</tt>
may have type
<tt>array</tt>
<tt>of</tt>
<tt>chan</tt>
<tt>of</tt>
<i>sometype</i>.
In this case, all of the channels in the array are tested;
one is fairly selected from those that have data.
The expression yields a tuple of type
<tt>(int,</tt>
<i>sometype</i>
<tt>)</tt>;
its first member gives the index of the channel from
which data was read, and its second member is the
value read from the channel.
If no member of the array has data ready, the expression delays.
</p>
<p>
Communication channels are treated more fully in §9.8 and
§9.13 below with the discussion of the
<tt>alt</tt>
and
<tt>spawn</tt>
statements.
</p>
<h4>8.2.10 Creation of arrays
</h4>
<p>
In the expressions
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>array [ </tt><i>expression</i><tt> ] of </tt><i>data-type
</i><tt>array [ </tt><i>expression</i><i>opt</i><tt> ] of { </tt><i>init-list</i><tt> ,</tt><i>opt</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>the value is a new array of the specified type.
In both forms, the
<i>expression</i>
must be of type
<tt>int</tt>,
and it supplies the size of the array.
In the first form, the type is given,
and the values in the array are initialized as
appropriate to the underlying type.
In the second form, a comma-separated list of values to initialize
the array is given, optionally followed by a trailing comma.
The type of the array is taken from the types of
the initializers, which must all be the same.
The list of initializers has the syntax
<dl><dt></dt><dd><tt><pre><br>
<i>init-list:
element
init-list</i><tt> , </tt><i>element
element:
expression
expression</i><tt> =&gt; </tt><i>expression
</i><tt>* =&gt; </tt><i>expression
<br>
</i></pre></tt></dd></dl><i>
</i>In an
<i>init-list</i>
of plain expressions (without
<tt>=&gt;</tt>),
the members of the array
are successively initialized with the corresponding
elements of the init-list.
An element of the form
<tt>e1=&gt;e2</tt>
initializes the member of the array at subscript
<tt>e1</tt>
with the expression
<tt>e2</tt>.
After such an element has been given, subsequent
simple elements (without
<tt>=&gt;</tt>)
begin initializing at position
<tt>e1+1</tt>
and so on.
Each of the first expressions must be of type
<tt>int</tt>
and must evaluate to a constant (§8.5).
<p></p>
<p>
If an element of the form
<tt>*</tt>
<tt>=&gt;e2</tt>
is present, all members of the array not otherwise
initialized are set to the value
<tt>e2</tt>.
The expression
<tt>e2</tt>
is evaluated for each subscript position,
but in an undefined order.
For example,
</p><dl><dt></dt><dd><tt><pre> arr := array[3] of { * =&gt; array[3] of { * =&gt; 1 } };
</pre></tt></dd></dl>
yields a 2-dimensional array (actually an array of arrays) filled with
<tt>1</tt>'s.
<p></p>
<p>
If the expression giving the size of the array is omitted, its size
is taken from the largest subscript of
a member explicitly initialized.
It is erroneous to initialize a member twice.
</p>
<h4>8.2.11 Creation of lists
</h4>
<p>
The value of an expression
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>list of { </tt><i>expression-list</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>is a list consisting of the expressions given.
The types of the expressions must be identical,
and this type is the underlying type of the list.
The first expression is the head of the list, and the
remaining expressions are a list constituting its tail.
Where a list is expected,
<tt>nil</tt>
specifies an empty list.
<p></p>
<h4>8.2.12 Creation of channels
</h4>
<p>
The value of
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>chan of </tt><i>data-type
<br>
</i></pre></tt></dd></dl><i>
</i>is an initialized channel of the specified type.
Just a declaration of a channel leaves it initialized only to
<tt>nil</tt>;
before it can be used it must be created. For example,
<dl><dt></dt><dd><tt><pre> ch: chan of int; # just declares, sets ch to nil
. . .
ch = chan of int; # creates the channel and assigns it
</pre></tt></dd></dl>
<p></p>
<h4>8.2.13 Casts
</h4>
<p>
An expression of the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> data-type monadic-expression
<br>
</i></pre></tt></dd></dl><i>
</i>in which a type name is followed by an expression
is called a
<i>cast</i>,
and converts the monadic expression to the named type.
Only certain specialized forms are provided for.
<p></p>
<h4>8.2.13.1 Arithmetic casts
</h4>
<p>
In arithmetic casts, the named type must be one of
<tt>byte</tt>,
<tt>int</tt>,
<tt>big</tt>,
or
<tt>real</tt>,
and the monadic-expression must have arithmetic type.
For example,
</p><dl><dt></dt><dd><tt><pre> byte 10
</pre></tt></dd></dl>
is an expression of
<tt>byte</tt>
type and value 10.
When real values are converted to integral ones,
they are rounded to the nearest integer, and away from 0
if there is a tie.
The effect of overflow during conversion is undefined.
<p></p>
<h4>8.2.13.2 Casts to strings
</h4>
<p>
Here the named data type is
<tt>string</tt>.
In a first form, the monadic expression has arithmetic type
(<tt>byte</tt>,
<tt>int</tt>,
<tt>big</tt>,
or
<tt>real</tt>)
and the value is a string containing the decimal representation
of the value, which may be either positive or negative.
A
<tt>real</tt>
operand is converted as if by format
<tt>%g</tt>,
and if the result is converted back to
<tt>real</tt>,
the original value will be recovered exactly.
</p>
<p>
In a second form,
the monadic expression has type
<tt>array</tt>
<tt>of</tt>
<tt>byte</tt>.
The value is a new string containing the Unicode characters
obtained by interpreting the bytes in the array as a UTF-8 representation
of that string.
(UTF-8 is a representation of 16-bit Unicode characters as one,
two, or three bytes.)
The result of the conversion is undefined if the byte array
ends within a multi-byte UTF-8 sequence.
</p>
<h4>8.2.13.3 Casts from strings
</h4>
<p>
In a first form, the monadic expression is a string,
and the named type is an arithmetic type.
The value is obtained by converting the string to
that type. Initial white space is ignored; after a possible
sign, conversion
ceases at the first character not part of a number.
</p>
<p>
In a second form, the named type is
<tt>array</tt>
<tt>of</tt>
<tt>byte</tt>
and the monadic-expression is a string.
The value is a new array of bytes containing the UTF-8 representation
of the Unicode characters in the string.
For example,
</p><dl><dt></dt><dd><tt><pre> s := "Ångström";
a := array of byte s;
s = string a;
</pre></tt></dd></dl>
takes the string
<tt>s</tt>
apart into bytes in the second line,
and puts it back in the third.
The length of
<tt>s</tt>
is 8, because it contains that many characters;
the length of
<tt>a</tt>
is larger, because some of its characters require more than
one byte in the UTF-8 representation.
<p></p>
<h4>8.2.13.4 Casts to
<tt>adt</tt>
and
<tt>ref</tt>
<tt>adt</tt>
</h4>
<p>
Here the named type is that of an
<tt>adt</tt>
or
<tt>ref</tt>
<tt>adt</tt>,
and the monadic expression is a comma-separated list of expressions
within parentheses.
The value of the expression is an instance of an
<tt>adt</tt>
of the named type whose data members are initialized with
the members of the list, or whose single data member
is initialized with the parenthesized expression.
In case the type is
<tt>ref</tt>
<tt>adt</tt>,
the value is a reference to the new
instance of the
<tt>adt</tt>.
</p>
<p>
The expressions in the list, read in order, correspond with the data
members of the
<tt>adt</tt>
read in order; their types and number must agree.
Placement of any function members of the
<tt>adt</tt>
is ignored.
For example,
</p><dl><dt></dt><dd><tt><pre> Point: adt {
x: int;
eq: fn (p: Point): int;
y: int;
};
. . .
p: Point;
p = Point(1, 2);
</pre></tt></dd></dl>
puts in
<tt>p</tt>
a
<tt>Point</tt>
whose
<tt>x</tt>
value is 1 and whose
<tt>y</tt>
value is 2.
The declaration and assignment could also be written
<dl><dt></dt><dd><tt><pre> p := Point(1, 2);
</pre></tt></dd></dl>
<p></p>
<h4>8.3 Binary expressions
</h4>
<p>
Binary expressions are either monadic expressions,
or have two operands and an infix operator;
the syntax is
</p><dl><dt></dt><dd><tt><pre><br>
<i>binary-expression:
monadic-expression
binary-expression binary-operator binary-expression
binary-operator: one of
</i><tt>* / % + - &lt;&lt; &gt;&gt; &lt; &gt; &lt;= &gt;= == != &amp; ^ | :: &amp;&amp; ||</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>All these binary operators are left-associative except for
<tt>::</tt>,
which associates to the right.
Their precedence is as listed here, with tightest first:
<dl><dt></dt><dd><tt><pre> * / %
+ -
&lt;&lt; &gt;&gt;
&lt; &gt; &lt;= &gt;=
== !=
&amp;
^
|
::
&amp;&amp;
||
</pre></tt></dd></dl>
<p></p>
<h4>8.3.1 Multiplicative operators
</h4>
<p>
The
<tt>*</tt>,
<tt>/</tt>,
and
<tt>%</tt>
operators respectively accomplish multiplication, division, and remainder.
The operands must be of identical arithmetic type, and the result has that
same type.
The remainder operator does not apply to type
<tt>real</tt>.
If overflow or division by 0 occurs, the result is undefined.
The absolute value of
<tt>a%b</tt>
is less than the absolute value of
<tt>b</tt>;
<tt>(a/b)*b + a%b</tt>
is always equal to
<tt>a</tt>;
and
<tt>a%b</tt>
is non-negative if
<tt>a</tt>
and
<tt>b</tt>
are.
</p>
<h4>8.3.2 Additive operators
</h4>
<p>
The
<tt>+</tt>
and
<tt>-</tt>
operators respectively accomplish addition and subtraction
of arithmetic operands of identical type;
the result has the same type.
The behavior on overflow or underflow is undefined.
The
<tt>+</tt>
operator may also be applied to strings;
the result is a string that is the concatenation of the operands.
</p>
<h4>8.3.3 Shift operators
</h4>
<p>
The shift operators are
<tt>&lt;&lt;</tt>
and
<tt>&gt;&gt;</tt>.
The left operand may be
<tt>big</tt>,
<tt>int</tt>,
or
<tt>byte</tt>;
the right operand is
<tt>int</tt>.
The type of the value is the same as its left operand.
The value of the right operand must be non-negative
and smaller than the number of bits in the left operand.
For the left-shift operator
<tt>&lt;&lt;</tt>,
the fill bits are 0;
for the right-shift operator
<tt>&gt;&gt;</tt>,
the fill bits are a copy of the sign for the
<tt>int</tt>
case, and 0 for the
<tt>byte</tt>
case.
</p>
<h4>8.3.4 Relational operators
</h4>
<p>
The relational operators are
<tt>&lt;</tt>
(less than),
<tt>&gt;</tt>
(greater than),
<tt>&lt;=</tt>
(less than or equal),
<tt>&gt;=</tt>
(greater than or equal),
<tt>==</tt>
(equal to),
<tt>!=</tt>
(not equal to).
The first four operators, which generate orderings,
apply only to arithmetic types
and to strings; the types of their operands
must be identical, except that a string may be
compared to
<tt>nil</tt>.
Comparison on strings is lexicographic over the
Unicode character set.
</p>
<p>
The equality operators
<tt>==</tt>
and
<tt>!=</tt>
accept operands of arithmetic, string, and reference types.
In general, the operands must have identical type,
but reference types and strings may be compared for identity with
<tt>nil</tt>.
Equality for reference types occurs when the operands
refer to the same object, or when both are
<tt>nil</tt>.
An uninitialized string, or one set to
<tt>nil</tt>,
is identical to the empty string denoted
<tt>""</tt>
for all the relational operators.
</p>
<p>
The value of any comparison is the
<tt>int</tt>
value 1 if the stated
relation is true, 0 if it is false.
</p>
<h4>8.3.5 Bitwise logical operators
</h4>
<p>
The logical operators
<tt>&amp;</tt>
(and),
<tt>^</tt>
(exclusive or) and
<tt>|</tt>
(inclusive or)
require operands of the same type,
which must be
<tt>byte</tt>,
<tt>int</tt>,
or
<tt>big</tt>.
The result has the same type and its
value is obtained by applying the operation
bitwise.
</p>
<h4>8.3.6 List concatenation
</h4>
<p>
The concatenation operator
<tt>::</tt>
takes a object of any data type
as its left operand and a list as its right operand.
The list's underlying type must be the same as
the type of the left operand.
The result is a new list with the left operand
tacked onto the front:
</p><dl><dt></dt><dd><tt><pre> hd (a :: l)
</pre></tt></dd></dl>
is the same as
<tt>a</tt>.
<p></p>
<h4>8.3.7 Logical operators
</h4>
<p>
The logical
<i>and</i>
operator
<tt>&amp;&amp;</tt>
first evaluates its left operand.
If the result is zero, then the value of the
whole expression is the
<tt>int</tt>
value 0.
Otherwise the right operand is evaluated; if
the result is zero, the value of the whole
expression is again 0; otherwise it is 1.
The operands must have the same arithmetic type.
</p>
<p>
The logical
<i>or</i>
operator
<tt>||</tt>
first evaluates its left operand.
If the result is non-zero, then the value of the
whole expression is the
<tt>int</tt>
value 1.
Otherwise the right operand is evaluated; if
the result is non-zero, the value of the whole
expression is again 1; otherwise it is 0.
The operands must have the same arithmetic type.
</p>
<h4>8.4 General Expressions
</h4>
<p>
The remaining syntax for expressions is
</p><dl><dt></dt><dd><tt><pre><br>
<i>expression:
binary-expression
lvalue-expression assignment-operator expression
</i><tt>( </tt><i>lvalue-expression-list</i><tt> ) = </tt><i>expression
send-expression
declare-expression
load-expression
assignment-operator: one of
</i><tt>= &amp;= |= ^= &lt;&lt;= &gt;&gt;= += -= *= /= %=</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>The left operand of an assignment can take only certain forms, called lvalues.
<dl><dt></dt><dd><tt><pre><br>
<i>lvalue-expression:
identifier
</i><tt>nil</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> ]</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> : ]</tt><i>
term</i><tt> . </tt><i>identifier
</i><tt>( </tt><i>lvalue-expression-list</i><tt> )</tt><i>
</i><tt>* </tt><i>monadic-expression
lvalue-expression-list:
lvalue
lvalue-expression-list</i><tt> , </tt><i>lvalue
<br>
</i></pre></tt></dd></dl><i>
</i><p></p>
<h4>8.4.1 Simple assignments with
<tt>=</tt>
</h4>
<p>
In general, the types of the left and right operands
must be the same; this type must be a data type.
The value of an assignment is its new left operand.
All the assignment operators associate right-to-left.
</p>
<p>
In the ordinary assignment with
<tt>=</tt>,
the value of the right side is assigned to the object
on the left.
For simple assignment only, the left operand may be a
parenthesized list of lvalues and the right operand
either a tuple or an
<tt>adt</tt>
whose data members correspond
in number and type to the lvalues in the list.
The members of the tuple, or
the data members of the
<tt>adt</tt>,
are assigned in sequence to
lvalues in the list.
For example,
</p><dl><dt></dt><dd><tt><pre> p: Point;
x, y: int;
(x, y) = p;
</pre></tt></dd></dl>
splits out the coordinates of the point into
<tt>x</tt>
and
<tt>y</tt>.
These rules apply recursively, so that if one of the
components of the left side is a parenthesized list of lvalues,
it is assigned from a corresponding
<tt>adt</tt>
or tuple on the right.
<p></p>
<p>
If the left operand of a simple assignment is an
<tt>adt</tt>
and the right side is a tuple, then the assignment
assigns the members of the tuple to the
<tt>adt</tt>
data members; these must correspond in number and type
with the members of the tuple.
</p>
<p>
The constant
<tt>nil</tt>
may be assigned to an lvalue of any reference type.
This lvalue will compare equal to
<tt>nil</tt>
until it is subsequently reassigned.
In the Inferno implementation of Limbo, such an assignment also
triggers the removal of the object referred to unless other references
to it remain.
</p>
<p>
The left operand of an assignment may be the constant
<tt>nil</tt>
to indicate that a value is discarded.
This applies in particular to any of the lvalues in
a tuple appearing on the left; to extend the examples above,
</p><dl><dt></dt><dd><tt><pre> (x, nil) = p;
</pre></tt></dd></dl>
assigns the
<tt>x</tt>
member of the Point
<tt>p</tt>
to the variable
<tt>x</tt>.
<p></p>
<p>
A special consideration applies to
strings.
If an
<tt>int</tt>
containing a Unicode character is assigned to a subscripted
string, the subscript
is normally required to lie within the string.
As a special case, the subscript's value may be equal to
the length of the string (that is, just beyond its end);
in this case, the character is appended to
the string, and the string's length increases by 1.
</p>
<p>
A final special case applies to array slices in the form
<tt>e1[e2:]</tt>.
Such expressions may lie on the left of
<tt>=</tt>.
The right side must be an array of the same type as
<tt>e1</tt>,
and its length must be less than or equal to
<tt>(len e1)-e2.</tt>
In this case, the
elements in the array on the right replace the elements of
<tt>e1</tt>
starting at position
<tt>e2</tt>.
The length of the array is unchanged.
</p>
<h4>8.4.2 Compound assignments
</h4>
<p>
A compound assignment with
<i>op<tt>=</tt></i>
is interpreted in terms of the plain assignment;
</p><dl><dt></dt><dd><tt><pre> e1 <i>op</i><tt>= e2;
</tt></pre></tt></dd></dl>
is equivalent to
<dl><dt></dt><dd><tt><pre> e1 <tt>= (e1) </tt><i>op </i><tt>(e2);
</tt></pre></tt></dd></dl>
except that
<tt>e1</tt>
is evaluated only once.
<p></p>
<h4>8.4.3 Send expressions
</h4>
<p>
A
<i>send-expression</i>
takes the form
</p><dl><dt></dt><dd><tt><pre><br>
<i>send-expression:
lvalue-expression</i><tt> &lt;- = </tt><i>expression
<br>
</i></pre></tt></dd></dl><i>
</i>In the expression
<dl><dt></dt><dd><tt><pre> e1 &lt;- = e2
</pre></tt></dd></dl>
the lvalue
<tt>e1</tt>
must have type
<tt>chan</tt>
<tt>of</tt>
<i>type</i>,
and
<tt>e2</tt>
must be of that type.
The value of
<tt>e2</tt>
is sent over the channel.
If no task is executing a
channel receive operation on the specified channel, the sender blocks.
Task synchronization is discussed in §9.8 and §9.13 below.
<p></p>
<h4>8.4.4 Declare-expressions
</h4>
<p>
A
<i>declare-expression</i>
is an assignment that also declares identifiers on its left:
</p><dl><dt></dt><dd><tt><pre><br>
<i>declare-expression:
lvalue-expression</i><tt> := </tt><i>expression
<br>
</i></pre></tt></dd></dl><i>
</i>Each of the constituent terms in the
<i>lvalue-expression</i>
must be an identifier or
<tt>nil</tt>.
A plain identifier on the left
is declared as having the type of the expression,
and it is initialized with the expression's value.
When a parenthesized list of identifiers is given, the expression
must be a tuple or an
<tt>adt</tt>,
and the individual identifiers in the list are declared and initialized
with the members of the tuple, or the data members of the
<tt>adt</tt>.
As with ordinary assignments, the keyword
<tt>nil</tt>
may stand for an identifier whose declaration and assignment
are skipped.
<p></p>
<p>
The value and type of a declare-expression are the same as those of the expression.
</p>
<h4>8.4.5 Load expressions
</h4>
<p>
A
<i>load-expression</i>
has the form
</p><dl><dt></dt><dd><tt><pre><br>
<i>load-expression:
</i><tt>load </tt><i>identifier expression
<br>
</i></pre></tt></dd></dl><i>
</i>The identifier is the identifier of a module, that is, the type
name declared in a
<tt>module</tt>
declaration.
The expression following
<tt>load</tt>
has type
<tt>string</tt>
and names a file containing the
compiled form of the module.
The
<tt>load</tt>
expression yields a handle for referring to the functions provided
by a module and its
<tt>adt</tt>.
<p></p>
<p>
Execution of
<tt>load</tt>
brings the file containing the module into local memory and dynamically type-checks
its interface: the run-time system ascertains that
the declarations exported by the module are compatible
with the module declaration visible in the scope of the
<tt>load</tt>
operator (see §11.2).
In the scope of a module declaration, the types and constants
exported by the module may be referred to without a handle, but
the functions and data exported by the module
(directly at its top level, or within its
<tt>adt</tt>)
may be called only using a valid
handle acquired by the
<tt>load</tt>
operator.
</p>
<p>
The value of
<tt>load</tt>
is
<tt>nil</tt>
if the attempt to load fails, either because the file containing
the module can not be found, or because the found module does not
export the specified interface.
</p>
<p>
Each evaluation of
<tt>load</tt>
creates a separate instance of the specified module;
it does not share data with any other instance.
</p>
<h4>8.5 Constant expressions
</h4>
<p>
In several places a constant expression is required.
Such an expression contains operands that are
identifiers previously declared with
<tt>con</tt>,
or
<tt>int</tt>,
<tt>big</tt>,
<tt>real</tt>,
or
<tt>string</tt>
constants.
These may be connected by any of the following operators:
</p><dl><dt></dt><dd><tt><pre> + - * / % &amp; | ^
== &lt; &gt; &lt;= &gt;= != &lt;&lt; &gt;&gt;
&amp;&amp; ||
~ !
</pre></tt></dd></dl>
together with arithmetic and string casts, and parentheses for
grouping.
<p></p>
<h4>8.6 Expression evaluation
</h4>
<p>
Expressions in Limbo are not reordered by the compiler;
values are computed in accordance with the parse of the expression.
However there is no guarantee of temporal evaluation order for expressions
with side effects, except in the following circumstances:
function arguments are fully evaluated before the function
is called; the logical operators
<tt>&amp;&amp;</tt>
and
<tt>||</tt>
have fully defined order of evaluation, as explained above.
All side effects from an expression in one statement are
completed before the next statement is begun.
</p>
<p>
In an expression containing a constant subexpression (in the
sense of §8.5), the constant subexpression is evaluated at
compile-time with all exceptions ignored.
</p>
<p>
Underflow, overflow, and zero-divide conditions during integer
arithmetic produce undefined results.
</p>
<p>
The
<tt>real</tt>
arithmetic of Limbo is all performed in IEEE double precision,
although denormalized numbers may not be supported.
By default,
invalid operations, zero-divide, overflow, and underflow
during real arithmetic are fatal; inexact-result is quiet.
The default rounding mode is round-to-nearest-even.
A set of routines in the
<tt>Math</tt>
library module permits independent control of these modes within each thread.
</p>
<h4>9 Statements
</h4>
<p>
The executable code within a function definition consists
of a sequence of statements and declarations.
As discussed in the Scope section §11 below,
declarations become effective at the place they appear.
Statements are executed in sequence except as discussed below.
In particular, the optional labels on some of the statements are used with
<tt>break</tt>
and
<tt>continue</tt>
to exit from or re-execute the labeled statement.
</p><dl><dt></dt><dd><tt><pre><br>
<i>statements:
(empty)
statements declaration
statements statement
statement:
expression</i><tt> ;</tt><i>
</i><tt>;</tt><i>
</i><tt>{ </tt><i>statements</i><tt> }</tt><i>
</i><tt>if ( </tt><i>expression</i><tt> ) </tt><i>statement
</i><tt>if ( </tt><i>expression</i><tt> ) </tt><i>statement</i><tt> else </tt><i>statement
label</i><i>opt </i><tt>while ( </tt><i>expression</i><i>opt</i><tt> ) </tt><i>statement
label</i><i>opt </i><tt>do </tt><i>statement</i><tt> while ( </tt><i>expression</i><i>opt</i><tt> ) ;</tt><i>
label</i><i>opt </i><tt>for ( </tt><i>expression</i><i>opt</i><tt> ; </tt><i>expression</i><i>opt</i><tt> ; </tt><i>expression</i><i>opt</i><tt> ) </tt><i>statement
label</i><i>opt </i><tt>case </tt><i>expression</i><tt> { </tt><i>qual-statement-sequence</i><tt> }</tt><i>
label</i><i>opt </i><tt>alt { </tt><i>qual-statement-sequence</i><tt> }</tt><i>
label</i><i>opt </i><tt>pick </tt><i>identifier</i><tt> := </tt><i>expression</i><tt> { </tt><i>pqual-statement-sequence</i><tt> }</tt><i>
</i><tt>break </tt><i>identifier</i><i>opt</i><tt> ;</tt><i>
</i><tt>continue </tt><i>identifier</i><i>opt</i><tt> ;</tt><i>
</i><tt>return </tt><i>expression</i><i>opt</i><tt> ;</tt><i>
</i><tt>spawn </tt><i>term</i><tt> ( </tt><i>expression-list</i><i>opt</i><tt> ) ;</tt><i>
</i><tt>exit ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>label:
identifier </i><tt>:</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><p></p>
<h4>9.1 Expression statements
</h4>
<p>
Expression statements consist of an expression followed by
a semicolon:
</p><dl><dt></dt><dd><tt><pre><br>
<i> expression</i><tt> ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>Most often expression statements are assignments, but other expressions
that cause effects are often useful, for example calling a function
or sending or receiving on a channel.
<p></p>
<h4>9.2 Null statement
</h4>
<p>
The null statement consists of a lone semicolon.
It is most useful for supplying an empty body
to a looping statement with internal side effects.
</p>
<h4>9.3 Blocks
</h4>
<p>
Blocks are
<i>statements</i>
enclosed in
<tt>{}</tt>
characters.
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>{ </tt><i>statements</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>A block starts a new scope.
The effect of any declarations within a block disappears
at the end of the block.
<p></p>
<h4>9.4 Conditional statements
</h4>
<p>
The conditional statement takes two forms:
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>if ( </tt><i>expression</i><tt> ) </tt><i>statement
</i><tt>if ( </tt><i>expression</i><tt> ) </tt><i>statement</i><tt> else </tt><i>statement
<br>
</i></pre></tt></dd></dl><i>
</i>The
<i>expression</i>
is evaluated; it must have type
<tt>int</tt>.
If it is non-zero, then the first
<i>statement</i>
is executed.
In the second form, the second
<i>statement</i>
is executed if the
<i>expression</i>
is 0.
The statement after
<tt>else</tt>
is connected to the nearest
<tt>else</tt>-less
<tt>if</tt>.
<p></p>
<h4>9.5 Simple looping statements
</h4>
<p>
The simple looping statements are
</p><dl><dt></dt><dd><tt><pre><br>
<i> label</i><i>opt </i><tt>while ( </tt><i>expression</i><i>opt</i><tt> ) </tt><i>statement
label</i><i>opt </i><tt>do </tt><i>statement</i><tt> while ( </tt><i>expression</i><i>opt</i><tt> ) ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>In both cases the expression must be of type
<tt>int</tt>.
In the first form, the
<i>expression</i>
is first tested against 0;
while it is not equal, the
<i>statement</i>
is repeatedly executed.
In the second form, the
<i>statement</i>
is executed, and then, while the
<i>expression</i>
is not 0, the statement is repeatedly executed.
If the
<i>expression</i>
is missing, it is understood to be non-zero.
<p></p>
<h4>9.6 <tt>for</tt>
statement
</h4>
<p>
The
<tt>for</tt>
statement has the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> label</i><i>opt </i><tt>for ( </tt><i>expression-1</i><i>opt</i><tt> ; </tt><i>expression-2</i><i>opt</i><tt> ; </tt><i>expression-3</i><i>opt</i><tt> ) </tt><i>statement
<br>
</i></pre></tt></dd></dl><i>
</i>It is equivalent to
<dl><dt></dt><dd><tt><pre><br>
<i> expression-1</i><tt> ;</tt><i>
</i><tt>while ( </tt><i>expression-2</i><tt> ) {
</tt><i>statement
expression-3</i><tt> ;
</tt><tt>}</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>in the absence of
<tt>continue</tt>
or
<tt>break</tt>
statements.
Thus (just as in C), the first expression is an initialization,
the second a test for starting and continuing the loop, and the third
a re-initialization for subsequent travels around the loop.
<p></p>
<h4>9.7 <tt>case</tt>
statement
</h4>
<p>
The
<tt>case</tt>
statement transfers control to one of several places
depending on the value of an expression:
</p><dl><dt></dt><dd><tt><pre><br>
<i> label</i><i>opt </i><tt>case </tt><i>expression</i><tt> { </tt><i>qual-statement-sequence</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>The expression must have type
<tt>int</tt>
or
<tt>string</tt>.
The
<tt>case</tt>
statement is followed by sequence of
qualified statements, which are statements labeled by
expressions or expression ranges:
<dl><dt></dt><dd><tt><pre><br>
<i>qual-statement-sequence:
qual-list</i><tt> =&gt;</tt><i>
qual-statement-sequence qual-list</i><tt> =&gt;</tt><i>
qual-statement-sequence statement
qual-statement-sequence declaration
qual-list:
qualifier
qual-list</i><tt> or </tt><i>qualifier
qualifier:
expression
expression</i><tt> to </tt><i>expression
</i><tt>*</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>A
<i>qual-statement-sequence</i>
is a sequence of
statements and declarations, each of which
is preceded by one or more qualifiers.
Syntactically, the qualifiers are
expressions, expression ranges with
<tt>to</tt>,
or
<tt>*</tt>.
If the expression mentioned after
<tt>case</tt>
has
<tt>int</tt>
type,
all the expressions appearing in the qualifiers
must evaluate to integer constants (§8.5).
If the expression has
<tt>string</tt>
type, all the qualifiers must be
string constants.
<p></p>
<p>
The
<tt>case</tt>
statement is executed by comparing
the expression at its head with the constants
in the qualifiers.
The test is for equality in the case
of simple constant qualifiers;
in range qualifiers, the test determines
whether the expression is greater than or
equal to the first constant and less than
or equal to the second.
</p>
<p>
None of the ranges or constants may overlap.
If no qualifier is selected and
there is a
<tt>*</tt>
qualifier,
then that qualifier is selected.
</p>
<p>
Once a qualifier is selected, control passes
to the set of statements headed by that
qualifier.
When control reaches the end of that set
of statements, control passes to the end
of the
<tt>case</tt>
statement.
If no qualifier is selected, the
<tt>case</tt>
statement is skipped.
</p>
<p>
Each qualifier and the statements following it
up to the next qualifier together form a separate
scope, like a block; declarations within this scope
disappear at the next qualifier (or at the end of
the statement.)
</p>
<p>
As an example, this fragment separates small numbers
by the initial letter of their spelling:
</p><dl><dt></dt><dd><tt><pre> case i {
1 or 8 =&gt;
sys-&gt;print("Begins with a vowel\n)";
0 or 2 to 7 or 9 =&gt;
sys-&gt;print("Begins with a consonant\n");
* =&gt;
sys-&gt;print("Sorry, didn't understand\n");
}
</pre></tt></dd></dl>
<p></p>
<h4>9.8 <tt>alt</tt>
statement
</h4>
<p>
The
<tt>alt</tt>
statement transfers control to one of several groups
of statements depending on the readiness of communication
channels.
Its syntax resembles that of
<tt>case</tt>:
</p><dl><dt></dt><dd><tt><pre><br>
<i> label</i><i>opt </i><tt>alt { </tt><i>qual-statement-sequence</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>However, the qualifiers take a form different
from those of
<tt>case</tt>.
In
<tt>alt</tt>,
each qualifier must be a
<tt>*</tt>,
or an expression containing a communication
operator
<tt>&lt;-</tt>
on a channel;
the operator may specify either sending or receiving.
For example,
<dl><dt></dt><dd><tt><pre> outchan := chan of string;
inchan := chan of int;
alt {
i := &lt;-inchan =&gt;
sys-&gt;print("Received %d\n", i);
outchan &lt;- = "message" =&gt;
sys-&gt;print("Sent the message\n");
}
</pre></tt></dd></dl>
The
<tt>alt</tt>
statement is executed by testing each of
the channels mentioned in the
<i>qual-list</i>
expressions for ability to send or receive,
depending on the operator;
if none is ready, the program blocks
until at least one is ready.
Then a random choice from the ready channels is selected
and control passes to the associated set
of statements.
<p></p>
<p>
If a qualifier of the form
<tt>*</tt>
is present, then the statement does not block;
if no channel is ready the statements associated with
<tt>*</tt>
are executed.
</p>
<p>
If two communication operators are present
in the same qualifier expression, only the leftmost one is
tested by
<tt>alt</tt>.
If two or more
<tt>alt</tt>
statements referring to the same receive (or send)
channel are executed in different
threads, the requests are queued;
when the channel becomes unblocked, the thread
that executed
<tt>alt</tt>
first is activated.
</p>
<p>
As with
<tt>case</tt>,
each qualifier and the statements following it
up to the next qualifier together form a separate
scope, like a block; declarations within this scope
disappear at the next qualifier (or at the end of
the statement.)
Thus, in the example above, the scope of
<tt>i</tt>
in the arm
</p><dl><dt></dt><dd><tt><pre> i := &lt;-inchan =&gt;
sys-&gt;print("Received %d\n", i);
</pre></tt></dd></dl>
is restricted to these two lines.
<p></p>
<p>
As mentioned in the specification
of the channel receive operator
<tt>&lt;-</tt>
in §8.2.8, that operator can take an array of channels as an argument.
This notation serves as a kind of simplified
<tt>alt</tt>
in which all the channels have the same type
and are treated similarly.
In this variant,
the value of the communication expression is a tuple
containing the index of the
channel over which a communication was received and
the value received.
For example, in
</p><dl><dt></dt><dd><tt><pre> a: array [2] of chan of string;
a[0] = chan of string;
a[1] = chan of string;
. . .
(i, s) := &lt;- a;
# s has now has the string from channel a[i]
</pre></tt></dd></dl>
the
<tt>&lt;-</tt>
operator waits until at least one of the
members of
<tt>a</tt>
is ready, selects one of them at random,
and returns the index and the transmitted string
as a tuple.
<p></p>
<p>
During execution of an
<tt>alt</tt>,
the expressions in the qualifiers are evaluated in an undefined
order, and in particular subexpressions may be evaluated before
the channels are tested for readiness.
Therefore qualifying expressions should not invoke side effects,
and should avoid subparts that might delay execution.
For example, in the qualifiers
</p><dl><dt></dt><dd><tt><pre> ch &lt;- = getchar() =&gt; # Bad idea
ich &lt;- = next++ =&gt; # Bad idea
</pre></tt></dd></dl>
<tt>getchar()</tt>
may be called early in the elaboration of the
<tt>alt</tt>
statement; if it delays, the entire
<tt>alt</tt>
may wait.
Similarly, the
<tt>next++</tt>
expression may be evaluated before testing the readiness of
<tt>ich</tt>.
<p></p>
<h4>9.9 <tt>pick</tt>
statement
</h4>
<p>
The
<tt>pick</tt>
statement transfers control to one of several groups of statements
depending upon the resulting variant type of a
<tt>pick</tt>
<tt>adt</tt>
expression. The syntax resembles that of
<tt>case</tt>:
</p><dl><dt></dt><dd><tt><pre><br>
<i> label</i><i>opt </i><tt>pick </tt><i>identifier</i><tt> := </tt><i>expression</i><tt> { </tt><i>pqual-statement-sequence</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>The expression must have type
<tt>ref</tt>
<tt>adt</tt>
and the
<tt>adt</tt>
must be a
<tt>pick</tt>
<tt>adt</tt>.
The
<tt>pick</tt>
statement is followed by a sequence of qualified statements, which are
statements labeled by the
<tt>pick</tt>
variant names:
<dl><dt></dt><dd><tt><pre><br>
<i>pqual-statement-sequence:
pqual-list</i><tt> =&gt;</tt><i>
pqual-statement-sequence pqual-list</i><tt> =&gt;</tt><i>
pqual-statement-sequence statement
pqual-statement-sequence declaration
pqual-list:
pqualifier
pqual-list</i><tt> or </tt><i>pqualifier
pqualifier:
identifier
</i><tt>*</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>A
<i>pqual-statement-sequence</i>
is a sequence of statements and declarations, each of which
is preceded by one or more qualifiers.
Syntactically, the qualifiers are identifiers, identifier lists (constructed with
<tt>or</tt>),
or
<tt>*</tt>.
The identifiers must be names of the variant types of the
<tt>pick</tt>
<tt>adt</tt>.
The
<tt>pick</tt>
statement is executed by comparing the variant type of the
<tt>pick</tt>
<tt>adt</tt>
referenced by the expression at its head with the variant type names in the qualifiers.
The matching qualifier is selected.
None of the variant type names may appear more than once.
If no qualifier is selected and there is a
<tt>*</tt>
qualifier, then that qualifier is selected.
<p></p>
<p>
Once a qualifier is selected, control passes
to the set of statements headed by that qualifier.
When control reaches the end of that set of statements,
control passes to the end of the
<tt>pick</tt>
statement.
If no qualifier is selected, the
<tt>pick</tt>
statement is skipped.
</p>
<p>
Each qualifier and the statements following it
up to the next qualifier together form a separate
scope, like a block; declarations within this scope
disappear at the next qualifier (or at the end of
the statement.)
</p>
<p>
The
<i>identifier</i>
and
<i>expression</i>
given in the
<tt>pick</tt>
statement are used to bind a new variable to a
<tt>pick</tt>
<tt>adt</tt>
reference expression, and within the statements associated with the
selected qualifier the variable can be used as if it were of the corresponding
variant type.
</p>
<p>
As an example, given a
<tt>pick</tt>
<tt>adt</tt>
of the following form:
</p><dl><dt></dt><dd><tt><pre> Constant: adt {
name: string;
pick {
Str or Pstring =&gt;
s: string;
Real =&gt;
r: real;
}
};
</pre></tt></dd></dl>
the following function could be used to print out the value of
an expression of type
<tt>ref Constant</tt>:
<dl><dt></dt><dd><tt><pre> printconst(c: ref Constant)
{
sys-&gt;print("%s: ", c.name);
pick x := c {
Str =&gt;
sys-&gt;print("%s\n", x.s);
Pstring =&gt;
sys-&gt;print("[%s]\n", x.s);
Real =&gt;
sys-&gt;print("%f\n", x.r);
};
}
</pre></tt></dd></dl>
<p></p>
<h4>9.10 <tt>break</tt>
statement
</h4>
<p>
The
<tt>break</tt>
statement
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>break </tt><i>identifier</i><i>opt </i><tt>;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>terminates execution of
<tt>while</tt>,
<tt>do</tt>,
<tt>for</tt>,
<tt>case</tt>,
<tt>alt</tt>,
and
<tt>pick</tt>
statements.
Execution of
<tt>break</tt>
with no identifier
transfers control to
the statement after the innermost
<tt>while</tt>,
<tt>do</tt>,
<tt>for</tt>,
<tt>case</tt>,
<tt>alt</tt>,
or
<tt>pick</tt>
statement in which it appears as a substatement.
Execution of
<tt>break</tt>
with an identifier
transfers control to the next statement after the unique enclosing
<tt>while</tt>,
<tt>do</tt>,
<tt>for</tt>,
<tt>case</tt>,
<tt>alt</tt>,
or
<tt>pick</tt>
labeled with that identifier.
<p></p>
<h4>9.11 <tt>continue</tt>
statement
</h4>
<p>
The
<tt>continue</tt>
statement
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>continue </tt><i>identifier</i><i>opt </i><tt>;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>restarts execution of
<tt>while</tt>,
<tt>do</tt>,
and
<tt>for</tt>
statements.
Execution of
<tt>continue</tt>
with no identifier
transfers control to the end of
the innermost
<tt>while</tt>,
<tt>do</tt>,
or
<tt>for</tt>
statement in which the
<tt>continue</tt>
appears as a substatement.
The expression that controls the loop is tested
and if it succeeds, execution continues in the loop.
The initialization portion of
<tt>for</tt>
is not redone.
<p></p>
<p>
Similarly, execution of
<tt>continue</tt>
with an identifier transfers control to the end of the enclosing
<tt>while</tt>,
<tt>do</tt>,
or
<tt>for</tt>
labeled with the same identifier.
</p>
<h4>9.12 <tt>return</tt>
statement
</h4>
<p>
The
<tt>return</tt>
statement,
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>return </tt><i>expression</i><i>opt</i><tt> ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>returns control to the caller of a function.
If the function returns a value (that is, if its definition
and declaration mention a return type),
the expression must be given and it must have the same type that the
function returns.
If the function returns no value, the expression
must generally be omitted.
However, if a function returns no value, and its
last action before returning is to call
another function with no value, then it may
use a special form of
<tt>return</tt>
that names the function being called.
For example,
<dl><dt></dt><dd><tt><pre> f, g: fn(a: int);
f(a: int) {
. . .
return g(a+1);
}
</pre></tt></dd></dl>
is permitted.
Its effect is the same as
<dl><dt></dt><dd><tt><pre> f(a: int) {
. . .
g(a+1);
return;
}
</pre></tt></dd></dl>
This
<i>ad hoc</i>
syntax offers the compiler a cheap opportunity to recognize
tail-recursion.
<p></p>
<p>
Running off the end of a function is equivalent to
<tt>return</tt>
with no expression.
</p>
<h4>9.13 <tt>spawn</tt>
statement
</h4>
<p>
The
<tt>spawn</tt>
statement creates a new thread of control.
It has the form
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>spawn </tt><i>term</i><tt> ( </tt><i>expression-list</i><i>opt</i><tt> ) ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>The term and expression-list are taken to be
a function call.
Execution of
<tt>spawn</tt>
creates an asynchronous, independent thread
of control, which calls the function in the new thread context.
This function may access the accessible objects
in the spawning thread; the two threads share
a common memory space.
These accessible objects include the data global to
the current module and reference data passed to the
spawned function.
Threads are preemptively scheduled, so that
changes to objects used in common between
threads may occur at any time.
The Limbo language provides no explicit synchronization
primitives; §12.3 shows examples of how to use channel
communication to control concurrency.
<p></p>
<h4>9.14 <tt>exit</tt>
statement
</h4>
<p>
The
<tt>exit</tt>
statement
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><tt>exit ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>terminates a thread and frees any resources
belonging exclusively to it.
<p></p>
<h4>10 Referring to modules;
<tt>import</tt>
</h4>
<p>
As discussed above, modules present
constants, functions, and types
in their interface.
Their names may be the same as names
in other modules or of local objects or types within
a module that uses another.
Name clashes are avoided because references
to the entities presented by a module are
qualified by the module type name or an object
of that module type.
</p>
<p>
For example,
after the module and variable declarations
</p><dl><dt></dt><dd><tt><pre> M: module {
One: con 1;
Thing: adt {
t: int;
f: fn();
};
g: fn();
};
m: M;
</pre></tt></dd></dl>
the name
<tt>One</tt>
refers to the constant defined in
module
<tt>M</tt>
only in the contexts
<tt>M-&gt;One</tt>
or
<tt>m-&gt;One</tt>;
the name
<tt>Thing</tt>
as the particular data type
associated with the
<tt>M</tt>
module can be referred to only in contexts
like
<dl><dt></dt><dd><tt><pre> th1: M-&gt;Thing;
th2: m-&gt;Thing;
</pre></tt></dd></dl>
Finally, to call a function defined either as a top-level
member of the module, or as a member of one of its
<tt>adt</tt>,
it is necessary to declare, and also dynamically initialize using
<tt>load</tt>,
a handle for the module.
Then calls of the form
<dl><dt></dt><dd><tt><pre> m-&gt;g();
m-&gt;th1.f();
</pre></tt></dd></dl>
become appropriate.
It is possible to use just the type name of a module to qualify
its constants and types because constants and types can be understood
without having the code and data present.
Calling a function declared by a module or one of its
<tt>adt</tt>
requires loading the module.
<p></p>
<p>
The
<tt>import</tt>
declaration
</p><dl><dt></dt><dd><tt><pre><br>
<i> </i><i>identifier-list</i><tt> : import </tt><i>identifier </i><tt>;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i>lifts the identifiers in the
<i>identifier-list</i>
into the scope in which
<tt>import</tt>
appears, so that they are usable without a qualifier.
The identifier after the
<tt>import</tt>
keyword is either
a module identifier, or an identifier declared as having
that type.
The initial list of identifiers specifies those
constants,
types,
and functions of the module whose names are promoted.
In the case of constants and types,
<tt>import</tt>
merely makes their names accessible without using a qualifier.
In the example above, if the
<tt>module</tt>
declaration above had been followed by
<dl><dt></dt><dd><tt><pre> One, Thing: import M;
</pre></tt></dd></dl>
then one could refer to just
<tt>One</tt>
instead of
<tt>M-&gt;One</tt>;
similarly an object could be declared like
<dl><dt></dt><dd><tt><pre> th: Thing;
</pre></tt></dd></dl>
For functions, and also
<tt>adt</tt>
with functions as members,
<tt>import</tt>
must specify a module
variable (as opposed to a module identifier).
Each imported name is associated with the specified module
variable, and the current value of this module variable
controls which instance of the module will
be called.
For example, after
<dl><dt></dt><dd><tt><pre> g, Thing: import m;
</pre></tt></dd></dl>
then
<dl><dt></dt><dd><tt><pre> g();
</pre></tt></dd></dl>
is equivalent to
<dl><dt></dt><dd><tt><pre> m-&gt;g();
</pre></tt></dd></dl>
and
<dl><dt></dt><dd><tt><pre> th: Thing;
th.f();
</pre></tt></dd></dl>
is equivalent to
<dl><dt></dt><dd><tt><pre> th: M.Thing;
m-&gt;th.f();
</pre></tt></dd></dl>
When the module declaration for the module being
implemented is encountered, an implicit
<tt>import</tt>
of all the names of the module is executed.
That is, given
<dl><dt></dt><dd><tt><pre> implement Mod;
. . .
Mod: module {
. . .
};
</pre></tt></dd></dl>
the constants and types of
<tt>Mod</tt>
are accessed as if they had been imported;
the functions declared in
<tt>Mod</tt>
are imported as well, and refer dynamically to the
current instance of the module being implemented.
<p></p>
<h4>11 Scope
</h4>
<p>
The scope of an identifier is the lexical range of
a program throughout which the identifier means a particular
type of, or instance of, an object.
The same identifier may be associated with several
different objects in different parts of the same program.
</p>
<p>
The names of members of an
<tt>adt</tt>
occupy a separate, nonconflicting space from other identifiers;
they are declared in a syntactically distinct position,
and are always used in a distinguishable way, namely
after the
<tt>.</tt>
selection operator.
Although the same scope rules apply to
<tt>adt</tt>
members as to other identifiers, their names may
coincide with other entities in the same scope.
</p>
<p>
Similarly, the names of constants, functions, and
<tt>adt</tt>
appearing
within a
<tt>module</tt>
declaration are ordinarily qualified either with
the name of the module or with a module variable
using the
<tt>-&gt;</tt>
notation.
As discussed above, the
<tt>import</tt>
declaration lifts these names into the current scope.
</p>
<p>
Identifiers declared in a top-declaration
(§5) have scope that lasts from the
declaration throughout the remainder of the
file in which it occurs, unless it is overridden
by a redeclaration of that name within an inner
scope.
Each function definition, and each block
within a function,
introduces a new scope.
A name declared within the block or function
(including a formal argument name of a function)
has a scope that begins
at the completion of its declaration and lasts until
the end of the block or function.
If an already-declared identifier is redeclared within
such an inner scope, the declaration previously in
force is used in any initialization expression
that is part of the new declaration.
</p>
<p>
As discussed above, within
<tt>case</tt>
<tt>alt</tt>
and
<tt>pick</tt>,
each qualifier
and the statements following it form an inner
scope just like a block.
</p>
<p>
The scope of a label is restricted to the
labeled statement,
and label names may coincide with those of other
entities in the same scope.
</p>
<h4>11.1 Forward referencing
</h4>
<p>
In general, names must be declared before they are used.
</p>
<p>
The first exception to this rule is that a
function local to a module need not have a
declaration at all; it is sufficient to give
its definition, and that definition may appear anywhere
in the module.
</p>
<p>
The general rule implies that no
<tt>adt</tt>
may contain, as a member, an
<tt>adt</tt>
not previously declared (including an instance of itself).
A second exception to this rule applies to
<tt>ref</tt>
<tt>adt</tt>
types.
An
<tt>adt</tt>
may contain a member whose type is a
<tt>ref</tt>
to itself, or to another
<tt>adt</tt>
even if the second
<tt>adt</tt>
has not yet been declared.
Unless a special notation is used, such
references are restricted:
all mutual or self references among
<tt>adt</tt>
are checked statically throughout all the
<tt>adt</tt>
visible in a module to determine which
members refer to other
<tt>adt</tt>.
Any member of an
<tt>adt</tt>
of
<tt>ref</tt>
<tt>adt</tt>
type that refers directly, or indirectly through a chain of references,
back to its own underlying type may not be assigned to individually;
it can gain a value only by an assignment to the
<tt>adt</tt>
as a whole.
For example, in
</p><dl><dt></dt><dd><tt><pre> Tree: adt {
l: ref Tree;
r: ref Tree;
t: ref Ntree;
};
Ntree: adt {
t: ref Tree;
};
t1 := Tree(nil, nil, nil); # OK
t2 := Tree(ref t1, ref t1, nil); # OK
t1 = Tree(ref t1, ref t2, nil); # OK
t1.l = ... ; # not OK
nt := ref Ntree(nil); # OK
nt.t = ... # not OK
</pre></tt></dd></dl>
the first three assignments are correct, but
any assignment to
<tt>t1.l</tt>
is forbidden, because it is self-referential.
The situation is the same with the mutually
referential fields of the
<tt>Tree</tt>
and
<tt>Ntree</tt>
<tt>adt</tt>.
<p></p>
<p>
These restrictions suffice
to prevent the creation of circular data structures.
Limbo implementations guarantee to
destroy all data objects not involved in such circularity
immediately after they become non-referenced by active
tasks, whether because
their names go out of scope or because they are assigned new values.
This property has visible effect because certain system resources,
like windows and file descriptors, can be seen outside the program.
In particular, if a reference to such a resource is held only within an
<tt>adt</tt>,
then that resource too is destroyed when the
<tt>adt</tt>
is.
</p>
<p>
The default rules are burdensome because they impede the construction even
of harmless structures like trees.
Therefore an escape is provided: using the word
<tt>cyclic</tt>
before the type in an
<tt>adt</tt>
member removes the circular-reference restriction for that member.
For example,
</p><dl><dt></dt><dd><tt><pre> Tree: adt {
l: cyclic ref Tree;
r: cyclic ref Tree;
t: ref Ntree;
};
Ntree: adt {
t: cyclic ref Tree;
};
t1 := Tree(nil, nil, nil); # OK
t2 := Tree(ref t1, ref t1, nil); # OK
t1 = Tree(ref t1, ref t2, nil); # OK
t1.l = ... ; # OK now
nt := ref Ntree(nil); # OK
nt.t = ... # OK now
</pre></tt></dd></dl>
With the use of
<tt>cyclic</tt>,
circular data structures can be created.
When they become unreferenced except by themselves, they will
be garbage-collected eventually, but not instantly.
<p></p>
<h4>11.2 Type equality and compatibility
</h4>
<p>
In an assignment and in passing an actual argument to a function,
the types of the target and the expression being assigned or
passed must be equal (with certain exceptions, e.g. assignment of
<tt>nil</tt>
to a reference type).
When a function is defined, its type must be equal to the type
of a function with the same name if one is in scope.
Type equality is determined as follows.
</p>
<p>
Two basic types are equal if and only if they are identical.
</p>
<p>
Two tuple types are equal if and only if they are composed
of equal types in the same order.
</p>
<p>
Two array types are equal if and only if they are arrays
of equal types.
The size of an array is not part of its type.
</p>
<p>
Two list types are equal if and only if they are composed
of equal types.
</p>
<p>
Two channel types are equal if and only if they transmit
equal types.
</p>
<p>
Two
<tt>adt</tt>
types are equal if and only if their data members
have the same names and correspondingly
equal types, including any
<tt>cyclic</tt>
attribute.
The order of member declaration is insignificant, and
constant and function members of an
<tt>adt</tt>
do not enter into the comparison,
nor does the name of the
<tt>adt</tt>
type itself.
In particular, with the declarations
</p><dl><dt></dt><dd><tt><pre> A: adt { x: ref B; };
B: adt { x: ref A; };
</pre></tt></dd></dl>
the types
<tt>A</tt>
and
<tt>B</tt>
are equal.
<p></p>
<p>
Two
<tt>ref</tt>
<tt>adt</tt>
types are equal if and only if they are references to equal
<tt>adt</tt>
types.
</p>
<p>
Two module types are equal if and only if their data and function members
have the same names and correspondingly equal types; the order
of their mention is insignificant.
Constant members and type members do not enter into the comparison.
</p>
<p>
Two function types are equal if and only if their return
values have the same type
and their argument lists have correspondingly equal types.
Any
<tt>self</tt>
attributes given to arguments much match.
Names given to arguments do not enter into the comparison.
</p>
<p>
A type name has the same type as the type from
which it was constructed.
</p>
<p>
When a module is loaded, the module stored
in the file system must have a type that is
<i>compatible</i>
with the type mentioned in the
<tt>load</tt>
expression.
The type of the stored module
type is compatible with the mentioned type if and only if
all data members of the two types are equal in name and type,
and all
<tt>adt</tt>
or functions actually mentioned by the program executing
<tt>load</tt>
have names and types equal to corresponding members of
the stored module.
</p>
<h4>12 Examples
</h4>
<p>
Because Limbo was designed for the Inferno environment, several
of these examples consist of simplified versions of already simple
Inferno applications in a prototype Inferno implementation.
Some appreciation for the resources available in this environment
should become evident, but its full description is available
elsewhere;
the discussion here will focus on language features.
However, several of the programs use facilities
from the module
<tt>Sys</tt>,
which provides an interface to a file system and its methods
resembling those of Unix or Plan 9,
as well as other useful library facilities.
</p>
<p>
Some of the programs are annotated with line numbers;
they are there only for descriptive purposes.
</p>
<h4>12.1 A simple command interpreter module
</h4>
<p>
This version of a shell program reads from a keyboard and
executes `commands' typed by the user.
Its own interface has the type of a
<tt>Command</tt>
module, and that is the type of the things it executes.
In particular, it can call modules like the
<tt>hello</tt>
example at the beginning of the paper.
</p><dl><dt></dt><dd><tt><pre>1 implement Command;
2 include "sys.m";
3 include "draw.m";
4 sys: Sys;
5 stdin: ref Sys-&gt;FD;
6 Command: module
7 {
8 init: fn(nil: ref Draw-&gt;Context, nil: list of string);
9 };
</pre></tt></dd></dl>
After the boilerplate on lines 1-3, the variables
<tt>sys</tt>
and
<tt>stdin</tt>
are declared on lines 4 and 5.
The I/O operations of the
<tt>Sys</tt>
module use the
<tt>ref</tt>
<tt>FD</tt>
type to refer to open files.
<dl><dt></dt><dd><tt><pre>10 init(ctx: ref Draw-&gt;Context, nil: list of string)
11 {
12
13
14 buf := array[256] of byte;
15 sys = load Sys Sys-&gt;PATH;
16 stdin = sys-&gt;fildes(0);
17 for(;;) {
18 sys-&gt;print("$ ");
19 n := sys-&gt;read(stdin, buf, len buf);
20 if(n &lt;= 0)
21 break;
22 (nw, arg) :=
sys-&gt;tokenize(string buf[0:n], " \t\n");
23 if(nw != 0)
24 exec(ctx, arg);
25 }
26 }
</pre></tt></dd></dl>
Line 10: conventionally, stand-alone modules are started
by calling their
<tt>init</tt>
functions.
The
<tt>Command</tt>
module follows this convention.
The arguments are presented as a list of strings.
In this simple example, the command interpreter itself
ignores its argument, so it need not be given a name.
<p></p>
<p>
Local variables are declared on lines 12-14; line 15
loads the
<tt>Sys</tt>
module and stores a handle for it in the variable
<tt>sys</tt>.
Line 16 creates an
<tt>FD</tt>
for the standard input by calling the
<tt>fildes</tt>
function of the
<tt>Sys</tt>
module using the
<tt>-&gt;</tt>
operator; the notation
<tt>modhandle-&gt;func(...)</tt>
specifies a call to the function named
<tt>func</tt>
in the module currently referred to by
<tt>modhandle</tt>.
(In general there can be several modules of the same type and name
active, and there can also be unrelated modules containing identically
named functions.
The
<tt>import</tt>
declaration, described in §6.6 above, can be used to abbreviate
the references when names do not clash.)
</p>
<p>
The loop on lines 17-25 prints a prompt (line 18), reads a line from
the standard input (line 19), parses it into tokens (line 22), and
executes the command.
</p>
<p>
The function call
<tt>sys-&gt;tokenize</tt>
is worth discussing as an example of style.
It takes two strings as arguments.
The characters in the second string are interpreted as separators
of tokens in the first string.
It returns a tuple whose first member is the number of
tokens found, and whose second is a list of strings
containing the tokens found: its declaration is
</p><dl><dt></dt><dd><tt><pre> tokenize: fn (s: string, sep: string): (int, list of string);
</pre></tt></dd></dl>
In the example, the second argument is
<tt>" \t\n"</tt>,
so that the routine returns the number of, and a list of,
`words' separated by blanks, tabs, and new-lines.
The free use of strings, lists, and tuple-returning
functions is common in Limbo.
<p></p>
<p>
The
<tt>sys-&gt;read</tt>
routine gathers an array of bytes into
<tt>buf</tt>.
Thus the expression for the first argument of
<tt>sys-&gt;tokenize</tt>
converts this array to a string by slicing the
array with
<tt>[0:n]</tt>,
using the actual number of bytes
gathered by the
<tt>read</tt>,
and using a cast.
</p>
<p>
At lines 23-24, if there were any words found,
<tt>exec</tt>
is called:
</p><dl><dt></dt><dd><tt><pre>27 exec(ctx: ref Draw-&gt;Context, args: list of string)
28 {
29 c: Command;
30 cmd, file: string;
31 cmd = hd args;
32 file = cmd + ".dis";
33 c = load Command file;
34 if(c == nil)
35 c = load Command "/dis/"+file;
36 if(c == nil) {
37 sys-&gt;print("%s: not found\n", cmd);
38 return;
39 }
40 c-&gt;init(ctx, args);
41 }
</pre></tt></dd></dl>
On lines 31 and 32 of
<tt>exec</tt>,
<tt>cmd</tt>
is set to the first of the words in the argument list,
and the string
<tt>.dis</tt>
is concatenated to it (to account for the fact that Limbo
object program files are conventionally named using this suffix).
On line 33 an attempt is made to load the named module
from the derived file name; it will fail if the file
does not exist.
The attempt will succeed,
and a non-nil handle assigned to
<tt>c</tt>,
if the file is found, and if
the module stored in that file does in fact implement the
<tt>Command</tt>
module type.
In case this fails, lines 34-35 make another attempt, after prefixing
<tt>/dis/</tt>
to the file name.
<p></p>
<p>
If either attempt to get a handle to the named module
succeeds,
<tt>c</tt>
will contain a valid handle to it; line 40 calls its
<tt>init</tt>
function, passing it the whole argument list.
When it returns, the
<tt>exec</tt>
function returns, and the main loop resumes.
</p>
<h4>12.2 Infrared remote control
</h4>
<p>
This example shows two instances of a module
for interfacing to a TV remote control; one
is for the real remote, which in this case
is connected to a serial port on a set-top
box, and the other is simulated for testing
programs running on a regular operating
system.
The techniques of special interest are the
dynamic use of modules and the communication
using a channel.
</p>
<p>
The module is used by creating a channel and passing
it to the module's
<tt>init</tt>
function,
which returns a success/error indicator and starts an
asynchronous process to read the remote control.
The user of the module executes a receive
on the channel whenever it wishes to accept
a button-push.
</p>
<p>
The (abridged) module declaration is
</p><dl><dt></dt><dd><tt><pre>Ir: module
{
# Codes buttons on IR remote control
Zero: con 0;
One: con 1;
. . .
Mute: con 23;
Error: con 9999;
init: fn(chan of int): int;
PATH: con "/dis/ir.dis";
SIMPATH: con "/dis/irsim.h";
};
</pre></tt></dd></dl>
The implementation for the `real' remote control is
<dl><dt></dt><dd><tt><pre>implement Ir;
include "ir.m";
include "sys.m";
FD, Dir: import Sys;
sys: Sys;
init(keys: chan of int): int
{
cfd, dfd: ref FD;
sys = load Sys Sys-&gt;PATH;
cfd = sys-&gt;open("/dev/eia1ctl", sys-&gt;OWRITE);
if(cfd == nil)
return -1;
sys-&gt;fprint(cfd, "b9600");
dfd = sys-&gt;open("/dev/eia1", sys-&gt;OREAD);
cfd = nil;
spawn reader(keys, dfd);
return 0;
}
</pre></tt></dd></dl>
The
<tt>init</tt>
routine accepts a
<tt>chan</tt>
argument; it will be used by the module to
send codes for the buttons pressed by the user.
In this routine, the calls to
<tt>sys-&gt;open</tt>
and
<tt>sys-&gt;fprint</tt>
open and set up the device data and control files
<tt>/dev/eia1</tt>
and
<tt>/dev/eia1ctl</tt>
used to communicate with the device itself.
The important step is at the end: the
<tt>spawn</tt>
statement creates a new,
asynchronous task to read the device, using a routine
that is passed the communications channel and the
FD for the device:
<dl><dt></dt><dd><tt><pre>reader(keys: chan of int, dfd: ref FD)
{
n, ta, tb: int;
dir: Dir;
b1:= array[1] of byte;
b2:= array[1] of byte;
# find the number of bytes already
# queued and flush that many
(n, dir) = sys-&gt;fstat(dfd);
if(n &gt;= 0 &amp;&amp; dir.length &gt; 0) {
while(dir.length) {
n = sys-&gt;read(dfd,
array[dir.length] of byte,
dir.length);
if(n &lt; 0)
break;
dir.length -= n;
}
}
</pre></tt></dd></dl>
<dl><dt></dt><dd><tt><pre>loop: for(;;) {
n = sys-&gt;read(dfd, b1, len b1);
if(n &lt;= 0)
break;
ta = sys-&gt;millisec();
# Button pushes are pairs of characters
# that arrive closer together than
# 200 ms. Longer than that is likely
# to be noise.
for(;;) {
n = sys-&gt;read(dfd, b2, 1);
if(n &lt;= 0)
break loop;
tb = sys-&gt;millisec();
if(tb - ta &lt;= 200)
break;
ta = tb;
b1[0] = b2[0];
}
# map the character pair; the significant
# bits are the lowest 5.
case ((int b1[0]&amp;16r1f)&lt;&lt;5) | (int b2[0]&amp;16r1f) {
975 =&gt; n = Ir-&gt;Zero;
479 =&gt; n = Ir-&gt;One;
. . .
791 =&gt; n = Ir-&gt;Mute;
* =&gt; n = Ir-&gt;Error;
}
# found a button-push; send the value
keys &lt;-= n;
}
keys &lt;-= Ir-&gt;Error;
}
</pre></tt></dd></dl>
The code in the middle is related to noise-filtering
and is uninteresting in detail except as it illustrates
some of the methods provided by the
<tt>Sys</tt>
module; the crucial actions are found at the bottom,
where the routine sends either
a true button-push or an error code over the channel to
the module's client.
<p></p>
<p>
Here is another implementation of the same interface.
Its
<tt>init</tt>
function performs the same kind of initialization
as the other version, but using the operating system's
keyboard files
<tt>/dev/cons</tt>
and
<tt>/dev/consctl</tt>.
In the Inferno environment, operations corresponding to the Unix
`stty' primitive are accomplished by writing messages to
a control file associated with the file that handles the data.
</p><dl><dt></dt><dd><tt><pre>implement Ir;
include "ir.m";
include "sys.m";
FD: import Sys;
sys: Sys;
cctlfd: ref FD;
init(keys: chan of int): int
{
dfd: ref FD;
sys = load Sys Sys-&gt;PATH;
cctlfd = sys-&gt;open("/dev/consctl", sys-&gt;OWRITE);
if(cctlfd == nil)
return -1;
sys-&gt;write(cctlfd, array of byte "rawon", 5);
dfd = sys-&gt;open("/dev/cons", sys-&gt;OREAD);
if(dfd == nil)
return -1;
spawn reader(keys, dfd);
return 0;
}
</pre></tt></dd></dl>
A fine point: the variable
<tt>cctlfd</tt>
that contains the FD for the control device is
declared external to the
init function, even though it appears to be used
only inside it.
Programming cleanliness suggests that
its declaration be moved inside, but here that
won't work;
device control files
in Inferno retain settings like `raw mode' only
while they remain open.
If
<tt>cctlfd</tt>
were declared inside
<tt>init</tt>,
then returning from
<tt>init</tt>
would destroy the last reference to the FD for the control file,
and the device would be closed automatically.
<p></p>
<p>
The reader function for this module has the same structure as the first
example, but doesn't have to worry about a noisy infrared detector:
</p><dl><dt></dt><dd><tt><pre>reader(keys: chan of int, dfd: ref FD)
{
n: int;
b:= array[1] of byte;
for(;;) {
n = sys-&gt;read(dfd, b, 1);
if(n != 1)
break;
case int b[0] {
'0' =&gt; n = Ir-&gt;Zero;
'1' =&gt; n = Ir-&gt;One;
. . .
16r7f =&gt; n = Ir-&gt;Mute;
* =&gt; n = Ir-&gt;Error;
}
keys &lt;-= n;
}
keys &lt;-= Ir-&gt;Error;
}
</pre></tt></dd></dl>
The following module can be used to test the above code.
It simply prints the name of the button that was pressed.
<dl><dt></dt><dd><tt><pre>implement Irtest;
include "sys.m";
include "draw.m";
FD: import Sys;
include "ir.m";
Irtest: module
{
init: fn(nil: ref Draw-&gt;Context, nil: list of string);
};
ir: Ir;
sys: Sys;
</pre></tt></dd></dl>
<dl><dt></dt><dd><tt><pre>init(nil: ref Draw-&gt;Context, nil: list of string)
{
c: int;
stderr: ref FD;
irchan := chan of int;
sys = load Sys Sys-&gt;PATH;
stderr = sys-&gt;fildes(2);
# If the real IR remote application can
# be found, use it, otherwise use the simulator:
ir = load Ir Ir-&gt;PATH;
if(ir == nil)
ir = load Ir Ir-&gt;SIMPATH;
if(ir == nil) {
# %r format code means the last system error string
sys-&gt;fprint(stderr, "load ir: %r\n");
return;
}
if(ir-&gt;init(irchan) != 0) {
sys-&gt;fprint(stderr, "Ir.init: %r\n");
return;
}
names := array[] of {
"Zero",
"One",
. . .
"Mute",
};
for(;;) {
c = &lt;-irchan;
if(c == ir-&gt;Error)
sys-&gt;print("Error %d\n", c);
else
sys-&gt;print("%s\n", names[c]);
}
}
</pre></tt></dd></dl>
Finally, here is a snippet from a movie application that
uses the IR module; it demonstrates how
<tt>alt</tt>
is useful for dealing with multiple events.
This is only one of the functions of the
movie module, so not everything is defined.
It uses the
<tt>Mpeg</tt>
module, which actually
copies the MPEG data stream to the screen
asynchronously.
Its
<tt>play</tt>
function takes, as one of its arguments,
a channel;
before starting to play it writes a
string on the channel.
An empty string indicates success at
locating the movie; a non-empty
string contains an error message.
When it finishes, it writes another string.
<dl><dt></dt><dd><tt><pre>movie(entry: ref Dbinfo, cc: chan of int)
{
i: int;
m: Mpeg;
b: ref Image;
m = load Mpeg Mpeg-&gt;PATH;
if (m == nil)
return;
# make a place on the screen
w := screen.window(screen.image.r);
mr := chan of string;
s := m-&gt;play(w, 1, w.r, entry.movie, mr);
if(s != "")
return;
# wait for the end of the movie
# while watching for button pushes
for(;;) {
alt {
&lt;-mr =&gt;
return;
i = &lt;-cc =&gt;
case i {
Ir-&gt;Select =&gt;
m-&gt;ctl("stop");
Ir-&gt;Up or Ir-&gt;Dn =&gt;
m-&gt;ctl("pause");
}
}
}
}
</pre></tt></dd></dl>
<p></p>
<h4>12.3 Monitors
</h4>
<p>
Statically allocated storage within a module is accessible to
all the functions of that module,
and there is no explicit mechanism in Limbo for synchronizing
concurrent updates to this storage from several tasks.
However, it is straightforward to build a variety of concurrency-control
mechanisms by using channel communications.
</p>
<p>
An example is a module that implements a
<tt>Monitor</tt>
abstract data type.
Each instance of
<tt>Monitor</tt>
has a
<tt>lock</tt>
and an
<tt>unlock</tt>
operation;
calling
<tt>lock</tt>
delays if another task holds the lock; calling
<tt>unlock</tt>
releases the lock and enables any other task attempting
to execute
<tt>lock</tt>.
</p><dl><dt></dt><dd><tt><pre>implement Mon;
Mon: module
{
Monitor: adt {
create: fn(): Monitor;
lock: fn(m: self Monitor);
unlock: fn(m: self Monitor);
ch: chan of int;
};
};
</pre></tt></dd></dl>
<dl><dt></dt><dd><tt><pre>Monitor.create(): Monitor
{
m := Monitor(chan of int);
spawn lockproc(m.ch);
return m;
}
</pre></tt></dd></dl>
<dl><dt></dt><dd><tt><pre>Monitor.lock(m: self Monitor)
{
m.ch &lt;- = 0;
}
</pre></tt></dd></dl>
<dl><dt></dt><dd><tt><pre>Monitor.unlock(m: self Monitor)
{
&lt;- m.ch;
}
</pre></tt></dd></dl>
<dl><dt></dt><dd><tt><pre>lockproc(ch: chan of int)
{
for (;;) {
&lt;- ch; # wait for someone to lock
ch &lt;- = 0; # wait for someone to unlock
}
}
</pre></tt></dd></dl>
It would be used like this:
<dl><dt></dt><dd><tt><pre>mp: Mon;
Monitor: import mp;
mp = load Mon "...";
l := Monitor.create();
l.lock();
# region of code to be protected;
# only one thread can execute here at once.
l.unlock();
</pre></tt></dd></dl>
The
<tt>create</tt>
method of
<tt>Monitor</tt>
allocates an instance of a
<tt>Monitor</tt>
containing an initialized channel.
It also creates a thread executed in the
<tt>lockproc</tt>
routine, which repeatedly reads from the channel,
then writes on it.
The values transmitted over the channel are of no
interest; it is the pure fact of communication that is put to use.
The
<tt>lock</tt>
routine sends a message; in the idle state, the
<tt>lockproc</tt>
thread reads it and the sender proceeds.
Meanwhile,
<tt>lockproc</tt>
tries to send a message over the same channel.
If another thread attempts to
<tt>lock</tt>,
there is no reader for the channel, and so its transmission will block.
At some point, the thread that gained the lock
calls
<tt>unlock</tt>,
which receives from the channel.
Depending on timing, this reception enables execution of either
<tt>lockproc</tt>
or one of the threads attempting to send via
<tt>lock</tt>.
<p></p>
<h4>12.4 Buffered channels
</h4>
<p>
Limbo channels are unbuffered; a sender blocks until there
is a receiver.
This example shows a way to make a buffered
channel of strings from an unbuffered channel.
It is written as a module whose
<tt>bufchan</tt>
function takes a
<tt>chan</tt>
<tt>of</tt>
<tt>string</tt>
and a size as argument, and returns a new channel;
it creates an asynchronous task that accepts input from the argument
channel and saves up to
<tt>size</tt>
strings, meanwhile trying to send them to its user.
</p><dl><dt></dt><dd><tt><pre>implement Bufchan;
Bufchan: module {
bufchan: fn(c: chan of string, size: int): chan of string;
};
xfer(oldchan, newchan: chan of string, size: int)
{
temp := array[size] of string;
fp := 0; # first string in buffer
n := 0; # number of strings in buffer
dummy := chan of string;
sendch, recvch: chan of string;
s: string;
for (;;) {
sendch = recvch = dummy;
if (n &gt; 0)
sendch = newchan;
if (n &lt; size)
recvch = oldchan;
alt {
s = &lt;-recvch =&gt;
temp[(fp+n)%size] = s;
n++;
sendch &lt;- = temp[fp] =&gt;
temp[fp++] = nil;
n--;
if (fp&gt;=size)
fp -= size;
}
}
}
</pre></tt></dd></dl>
<dl><dt></dt><dd><tt><pre>bufchan(oldchan: chan of string, size: int): chan of string
{
newchan := chan of string;
spawn xfer(oldchan, newchan, size);
return newchan;
}
</pre></tt></dd></dl>
The module is somewhat specialized, but it illustrates
useful programming techniques.
The most interesting occurs in
<tt>xfer</tt>,
which does the work.
The problem
<tt>xfer</tt>
faces is that it doesn't want to receive input when its
buffer is full, nor to try to send when it has nothing to
transmit.
The solution here is to use a dummy channel
on which nothing is ever sent or received; in the
<tt>alt</tt>
statement,
that channel substitutes for the real input channel
when the buffer is full, and for the output channel
when the buffer is empty.
<p></p>
<p>
The module could be used in the following way:
</p><dl><dt></dt><dd><tt><pre>Bufchan: module {
PATH: con "/appl/lib/bufchan.dis";
bufchan: fn(c: chan of string, size: int): chan of string;
};
bufc := load Bufchan Bufchan-&gt;PATH;
sourcech := chan of string;
# ... (here, hand off sourcech to a process that
# reads strings from it and copies them to ch)
ch: chan of string = bufc-&gt;bufchan(sourcech, 10);
s := &lt;- ch;
</pre></tt></dd></dl>
<p></p>
<h4>13 Syntax summary
</h4>
<p>
This section summarizes the grammar of Limbo
above the lexical level; constants and identifiers
are left undefined.
</p>
<p>
</p><dl><dt></dt><dd><tt><pre><br>
<i>program:
</i><tt>implement </tt><i>identifier</i><tt> ; </tt><i>top-declaration-sequence
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>top-declaration-sequence:
top-declaration
top-declaration-sequence top-declaration
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>top-declaration:
declaration
identifier-list</i><tt> := </tt><i>expression</i><tt> ;</tt><i>
identifier-list</i><tt> = </tt><i>expression</i><tt> ;</tt><i>
</i><tt>( </tt><i>identifier-list</i><tt> ) := </tt><i>expression</i><tt> ;</tt><i>
module-declaration
function-definition
adt-declaration
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>declaration:
identifier-list</i><tt> : </tt><i>type</i><tt> ;</tt><i>
identifier-list</i><tt> : </tt><i>type</i><tt> = </tt><i>expression</i><tt> ;</tt><i>
identifier-list</i><tt> : con </tt><i>expression</i><tt> ;</tt><i>
</i><i>identifier-list</i><tt> : import </tt><i>identifier </i><tt>;</tt><i>
identifier-list</i><tt> : type</tt><i> type</i><tt> ;</tt><i>
</i><tt>include </tt><i>string-constant</i><tt> ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>identifier-list:
identifier
identifier-list</i><tt> , </tt><i>identifier
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>expression-list:
expression
expression-list</i><tt> , </tt><i>expression
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>type:
data-type
function-type
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>data-type:
</i><tt>byte</tt><i>
</i><tt>int</tt><i>
</i><tt>big</tt><i>
</i><tt>real</tt><i>
</i><tt>string</tt><i>
tuple-type
</i><tt>array of </tt><i>data-type
</i><tt>list of </tt><i>data-type
</i><tt>chan of </tt><i>data-type
adt-type
</i><tt>ref </tt><i>adt-type
module-type
module-qualified-type
type-name
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>tuple-type:
</i><tt>( </tt><i>data-type-list</i><tt> )</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>data-type-list:
data-type
data-type-list </i><tt>,</tt><i> data-type
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>adt-type:
identifier
module-qualified-type
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>module-type:
identifier
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>module-qualified-type:
identifier </i><tt>-&gt;</tt><i> identifier
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>type-name:
identifier
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>function-type:
</i><tt>fn </tt><i>function-arg-ret
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>function-arg-ret:
</i><tt>( </tt><i>formal-arg-list</i><i>opt</i><tt> )
</tt><tt>( </tt><i>formal-arg-list</i><i>opt</i><tt> ) : </tt><i>data-type
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>formal-arg-list:
formal-arg
formal-arg-list</i><tt> , </tt><i>formal-arg
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>formal-arg:
nil-or-</i><i>D-list</i><tt> : </tt><i>type
nil-or-</i><i>D</i><tt> : self ref</tt><i>opt </i><i>identifier
nil-or-</i><i>D</i><tt> : self </tt><i>identifier
</i><tt>*</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>nil-or-</i><i>D-list:
nil-or-</i><i>D
nil-or-</i><i>D-list </i><tt>, </tt><i>nil-or-</i><i>D
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>nil-or-</i><i>D:
identifier
</i><tt>nil</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>module-declaration:
</i><i>identifier</i><tt> : module { </tt><i>mod-member-list</i><i>opt</i><tt> } ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>mod-member-list:
mod-member
mod-member-list mod-member
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>mod-member:
identifier-list</i><tt> : </tt><i>function-type</i><tt> ;</tt><i>
identifier-list</i><tt> : </tt><i>data-type</i><tt> ;</tt><i>
adt-declaration</i><tt> ;</tt><i>
identifier-list</i><tt> : con </tt><i>expression </i><tt>;</tt><i>
identifier-list</i><tt> : type </tt><i>type </i><tt>;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>adt-declaration:
</i><i>identifier</i><tt> : adt { </tt><i>adt-member-list</i><i>opt</i><tt> } ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>adt-member-list:
adt-member
adt-member-list adt-member
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>adt-member:
identifier-list</i><tt> : cyclic</tt><i>opt </i><i>data-type</i><tt> ;</tt><i>
identifier-list</i><tt> : con </tt><i>expression</i><tt> ;</tt><i>
identifier-list</i><tt> : </tt><i>function-type</i><tt> ;</tt><i>
</i><tt>pick { </tt><i>pick-member-list</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>pick-member-list:
pick-tag-list</i><tt> =&gt;</tt><i>
pick-member-list pick-tag-list</i><tt> =&gt;</tt><i>
pick-member-list identifier-list</i><tt> : cyclic</tt><i>opt </i><i>data-type</i><tt> ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>pick-tag-list:
identifier
pick-tag-list</i><tt> or </tt><i>identifier
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>function-definition:
function-name-part function-arg-ret</i><tt> { </tt><i>statements</i><tt> }</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>function-name-part:
identifier
function-name-part</i><tt> . </tt><i>identifier
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>statements:
(empty)
statements declaration
statements statement
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>statement:
expression</i><tt> ;</tt><i>
</i><tt>;</tt><i>
</i><tt>{ </tt><i>statements</i><tt> }</tt><i>
</i><tt>if ( </tt><i>expression</i><tt> ) </tt><i>statement
</i><tt>if ( </tt><i>expression</i><tt> ) </tt><i>statement</i><tt> else </tt><i>statement
label</i><i>opt </i><tt>while ( </tt><i>expression</i><i>opt</i><tt> ) </tt><i>statement
label</i><i>opt </i><tt>do </tt><i>statement</i><tt> while ( </tt><i>expression</i><i>opt</i><tt> ) ;</tt><i>
label</i><i>opt </i><tt>for ( </tt><i>expression</i><i>opt</i><tt> ; </tt><i>expression</i><i>opt</i><tt> ; </tt><i>expression</i><i>opt</i><tt> ) </tt><i>statement
label</i><i>opt </i><tt>case </tt><i>expression</i><tt> { </tt><i>qual-statement-sequence</i><tt> }</tt><i>
label</i><i>opt </i><tt>alt { </tt><i>qual-statement-sequence</i><tt> }</tt><i>
label</i><i>opt </i><tt>pick </tt><i>identifier</i><tt> := </tt><i>expression</i><tt> { </tt><i>pqual-statement-sequence</i><tt> }</tt><i>
</i><tt>break </tt><i>identifier</i><i>opt</i><tt> ;</tt><i>
</i><tt>continue </tt><i>identifier</i><i>opt</i><tt> ;</tt><i>
</i><tt>return </tt><i>expression</i><i>opt</i><tt> ;</tt><i>
</i><tt>spawn </tt><i>term</i><tt> ( </tt><i>expression-list</i><i>opt</i><tt> ) ;</tt><i>
</i><tt>exit ;</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>label:
identifier </i><tt>:</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>qual-statement-sequence:
qual-list</i><tt> =&gt;</tt><i>
qual-statement-sequence qual-list</i><tt> =&gt;</tt><i>
qual-statement-sequence statement
qual-statement-sequence declaration
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>qual-list:
qualifier
qual-list</i><tt> or </tt><i>qualifier
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>qualifier:
expression
expression</i><tt> to </tt><i>expression
</i><tt>*</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>pqual-statement-sequence:
pqual-list</i><tt> =&gt;</tt><i>
pqual-statement-sequence pqual-list</i><tt> =&gt;</tt><i>
pqual-statement-sequence statement
pqual-statement-sequence declaration
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>pqual-list:
pqualifier
pqual-list</i><tt> or </tt><i>pqualifier
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>pqualifier:
identifier
</i><tt>*</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>expression:
binary-expression
lvalue-expression assignment-operator expression
</i><tt>( </tt><i>lvalue-expression-list</i><tt> ) = </tt><i>expression
send-expression
declare-expression
load-expression
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>binary-expression:
monadic-expression
binary-expression binary-operator binary-expression
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>binary-operator: one of
</i><tt>* / % + - &lt;&lt; &gt;&gt; &lt; &gt; &lt;= &gt;= == != &amp; ^ | :: &amp;&amp; ||</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>assignment-operator: one of
</i><tt>= &amp;= |= ^= &lt;&lt;= &gt;&gt;= += -= *= /= %=</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>lvalue-expression:
identifier
</i><tt>nil</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> ]</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> : ]</tt><i>
term</i><tt> . </tt><i>identifier
</i><tt>( </tt><i>lvalue-expression-list</i><tt> )</tt><i>
</i><tt>* </tt><i>monadic-expression
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>lvalue-expression-list:
lvalue
lvalue-expression-list</i><tt> , </tt><i>lvalue
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>expression:
term
monadic-operator monadic-expression
</i><tt>array [ </tt><i>expression</i><tt> ] of </tt><i>data-type
</i><tt>array [ </tt><i>expression</i><i>opt</i><tt> ] of { </tt><i>init-list</i><tt> }</tt><i>
</i><tt>list of { </tt><i>expression-list</i><tt> }</tt><i>
</i><tt>chan of </tt><i>data-type
data-type monadic-expression
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>term:
identifier
constant
real-constant
string-constant
</i><tt>nil</tt><i>
</i><tt>( </tt><i>expression-list</i><tt> )</tt><i>
term</i><tt> . </tt><i>identifier
term</i><tt> -&gt; </tt><i>term
term</i><tt> ( </tt><i>expression-list</i><i>opt</i><tt> )</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> ]</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> : </tt><i>expression</i><tt> ]</tt><i>
term</i><tt> [ </tt><i>expression</i><tt> : ]</tt><i>
term</i><tt> ++</tt><i>
term</i><tt> --</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>monadic-operator: one of
</i><tt>+ - ! ~ ref * ++ -- &lt;- hd tl len tagof</tt><i>
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>init-list:
element
init-list</i><tt> , </tt><i>element
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>element:
expression
expression</i><tt> =&gt; </tt><i>expression
</i><tt>* =&gt; </tt><i>expression
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>send-expression:
lvalue-expression</i><tt> &lt;- = </tt><i>expression
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>declare-expression:
lvalue-expression</i><tt> := </tt><i>expression
<br>
</i></pre></tt></dd></dl><i>
</i><dl><dt></dt><dd><tt><pre><br>
<i>load-expression:
</i><tt>load </tt><i>identifier expression
<br>
</i></pre></tt></dd></dl><i>
</i><p></p>
<br> <br>
Portions copyright © 1995-1999 Lucent Technologies Inc. All rights reserved.<br>
Portions copyright © 2000 Vita Nuova Holdings Limited. All rights reserved.
</body><style type="text/css">embed[type*="application/x-shockwave-flash"],embed[src*=".swf"],object[type*="application/x-shockwave-flash"],object[codetype*="application/x-shockwave-flash"],object[src*=".swf"],object[codebase*="swflash.cab"],object[classid*="D27CDB6E-AE6D-11cf-96B8-444553540000"],object[classid*="d27cdb6e-ae6d-11cf-96b8-444553540000"],object[classid*="D27CDB6E-AE6D-11cf-96B8-444553540000"]{ display: none !important;}</style></html>