mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-27 07:16:22 +00:00
5372 lines
138 KiB
HTML
5372 lines
138 KiB
HTML
|
|
<!-- 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->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->Context, argv: list of string)
|
|
10 {
|
|
11 sys = load Sys Sys->PATH;
|
|
12 sys->print("hello world\n");
|
|
13 for (; argv!=nil; argv = tl argv)
|
|
14 sys->print("%s ", hd argv);
|
|
15 sys->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->Context</tt>
|
|
and a list of strings,
|
|
and it doesn't
|
|
return any value.
|
|
The
|
|
<tt>ref</tt>
|
|
<tt>Draw->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->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> + - * / % & | ^
|
|
== < > <= >= != << >>
|
|
&& || <- ::
|
|
= += -= *= /= %= &= |= ^= <<= >>=
|
|
:=
|
|
~ ++ -- !
|
|
</pre></tt></dd></dl>
|
|
The other separators are
|
|
<dl><dt></dt><dd><tt><pre> : ; ( ) { } [ ]
|
|
, . -> =>
|
|
</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 <-= (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>-></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<<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> =></tt><i>
|
|
pick-member-list pick-tag-list</i><tt> =></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->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> -> </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> .
|
|
->
|
|
() [] ++ --
|
|
</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> -> </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->Vector;
|
|
v2: Linear->Vector;
|
|
lin->setflags(Linear->TRUNCATE);
|
|
v1 = lin->(Linear->Vector).make(a);
|
|
v1 = lin->v1.make(a);
|
|
v1 = lin->v1.add(v1);
|
|
v1.v = nil;
|
|
</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->(Linear->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->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->Vector.make(a); # Wrong
|
|
v1 = lin->Linear->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->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>-></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<=e1, e1<=e2, e2<=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->open( ... );
|
|
want := 1024;
|
|
buf := array[want] of byte;
|
|
b := buf[0:];
|
|
while (want>0) {
|
|
got := sys->read(fd, b, want);
|
|
if (got<=0)
|
|
break;
|
|
b = b[got:];
|
|
want -= got;
|
|
}
|
|
</pre></tt></dd></dl>
|
|
Here the array
|
|
<tt>buf</tt>
|
|
is filled by successive calls to
|
|
<tt>sys->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 * ++ -- <- 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>&</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><-</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><-</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> => </tt><i>expression
|
|
</i><tt>* => </tt><i>expression
|
|
<br>
|
|
</i></pre></tt></dd></dl><i>
|
|
</i>In an
|
|
<i>init-list</i>
|
|
of plain expressions (without
|
|
<tt>=></tt>),
|
|
the members of the array
|
|
are successively initialized with the corresponding
|
|
elements of the init-list.
|
|
An element of the form
|
|
<tt>e1=>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>=></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>=>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 { * => array[3] of { * => 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>* / % + - << >> < > <= >= == != & ^ | :: && ||</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> * / %
|
|
+ -
|
|
<< >>
|
|
< > <= >=
|
|
== !=
|
|
&
|
|
^
|
|
|
|
|
::
|
|
&&
|
|
||
|
|
</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><<</tt>
|
|
and
|
|
<tt>>></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><<</tt>,
|
|
the fill bits are 0;
|
|
for the right-shift operator
|
|
<tt>>></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><</tt>
|
|
(less than),
|
|
<tt>></tt>
|
|
(greater than),
|
|
<tt><=</tt>
|
|
(less than or equal),
|
|
<tt>>=</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>&</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>&&</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>= &= |= ^= <<= >>= += -= *= /= %=</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> <- = </tt><i>expression
|
|
<br>
|
|
</i></pre></tt></dd></dl><i>
|
|
</i>In the expression
|
|
<dl><dt></dt><dd><tt><pre> e1 <- = 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> + - * / % & | ^
|
|
== < > <= >= != << >>
|
|
&& ||
|
|
~ !
|
|
</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>&&</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> =></tt><i>
|
|
qual-statement-sequence qual-list</i><tt> =></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 =>
|
|
sys->print("Begins with a vowel\n)";
|
|
0 or 2 to 7 or 9 =>
|
|
sys->print("Begins with a consonant\n");
|
|
* =>
|
|
sys->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><-</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 := <-inchan =>
|
|
sys->print("Received %d\n", i);
|
|
outchan <- = "message" =>
|
|
sys->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 := <-inchan =>
|
|
sys->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><-</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) := <- a;
|
|
# s has now has the string from channel a[i]
|
|
</pre></tt></dd></dl>
|
|
the
|
|
<tt><-</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 <- = getchar() => # Bad idea
|
|
ich <- = next++ => # 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> =></tt><i>
|
|
pqual-statement-sequence pqual-list</i><tt> =></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 =>
|
|
s: string;
|
|
Real =>
|
|
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->print("%s: ", c.name);
|
|
pick x := c {
|
|
Str =>
|
|
sys->print("%s\n", x.s);
|
|
Pstring =>
|
|
sys->print("[%s]\n", x.s);
|
|
Real =>
|
|
sys->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->One</tt>
|
|
or
|
|
<tt>m->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->Thing;
|
|
th2: m->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->g();
|
|
m->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->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->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->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>-></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->FD;
|
|
|
|
6 Command: module
|
|
7 {
|
|
8 init: fn(nil: ref Draw->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->Context, nil: list of string)
|
|
11 {
|
|
12
|
|
13
|
|
14 buf := array[256] of byte;
|
|
|
|
15 sys = load Sys Sys->PATH;
|
|
16 stdin = sys->fildes(0);
|
|
|
|
17 for(;;) {
|
|
18 sys->print("$ ");
|
|
19 n := sys->read(stdin, buf, len buf);
|
|
20 if(n <= 0)
|
|
21 break;
|
|
22 (nw, arg) :=
|
|
sys->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>-></tt>
|
|
operator; the notation
|
|
<tt>modhandle->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->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->read</tt>
|
|
routine gathers an array of bytes into
|
|
<tt>buf</tt>.
|
|
Thus the expression for the first argument of
|
|
<tt>sys->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->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->print("%s: not found\n", cmd);
|
|
38 return;
|
|
39 }
|
|
40 c->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->PATH;
|
|
|
|
cfd = sys->open("/dev/eia1ctl", sys->OWRITE);
|
|
if(cfd == nil)
|
|
return -1;
|
|
sys->fprint(cfd, "b9600");
|
|
|
|
dfd = sys->open("/dev/eia1", sys->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->open</tt>
|
|
and
|
|
<tt>sys->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->fstat(dfd);
|
|
if(n >= 0 && dir.length > 0) {
|
|
while(dir.length) {
|
|
n = sys->read(dfd,
|
|
array[dir.length] of byte,
|
|
dir.length);
|
|
if(n < 0)
|
|
break;
|
|
dir.length -= n;
|
|
}
|
|
}
|
|
</pre></tt></dd></dl>
|
|
<dl><dt></dt><dd><tt><pre>loop: for(;;) {
|
|
n = sys->read(dfd, b1, len b1);
|
|
if(n <= 0)
|
|
break;
|
|
ta = sys->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->read(dfd, b2, 1);
|
|
if(n <= 0)
|
|
break loop;
|
|
tb = sys->millisec();
|
|
if(tb - ta <= 200)
|
|
break;
|
|
ta = tb;
|
|
b1[0] = b2[0];
|
|
}
|
|
# map the character pair; the significant
|
|
# bits are the lowest 5.
|
|
case ((int b1[0]&16r1f)<<5) | (int b2[0]&16r1f) {
|
|
975 => n = Ir->Zero;
|
|
479 => n = Ir->One;
|
|
. . .
|
|
791 => n = Ir->Mute;
|
|
* => n = Ir->Error;
|
|
}
|
|
# found a button-push; send the value
|
|
keys <-= n;
|
|
}
|
|
keys <-= Ir->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->PATH;
|
|
|
|
cctlfd = sys->open("/dev/consctl", sys->OWRITE);
|
|
if(cctlfd == nil)
|
|
return -1;
|
|
sys->write(cctlfd, array of byte "rawon", 5);
|
|
|
|
dfd = sys->open("/dev/cons", sys->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->read(dfd, b, 1);
|
|
if(n != 1)
|
|
break;
|
|
case int b[0] {
|
|
'0' => n = Ir->Zero;
|
|
'1' => n = Ir->One;
|
|
. . .
|
|
16r7f => n = Ir->Mute;
|
|
* => n = Ir->Error;
|
|
}
|
|
keys <-= n;
|
|
}
|
|
keys <-= Ir->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->Context, nil: list of string);
|
|
};
|
|
ir: Ir;
|
|
sys: Sys;
|
|
</pre></tt></dd></dl>
|
|
<dl><dt></dt><dd><tt><pre>init(nil: ref Draw->Context, nil: list of string)
|
|
{
|
|
c: int;
|
|
stderr: ref FD;
|
|
irchan := chan of int;
|
|
|
|
sys = load Sys Sys->PATH;
|
|
stderr = sys->fildes(2);
|
|
|
|
# If the real IR remote application can
|
|
# be found, use it, otherwise use the simulator:
|
|
ir = load Ir Ir->PATH;
|
|
if(ir == nil)
|
|
ir = load Ir Ir->SIMPATH;
|
|
if(ir == nil) {
|
|
# %r format code means the last system error string
|
|
sys->fprint(stderr, "load ir: %r\n");
|
|
return;
|
|
}
|
|
if(ir->init(irchan) != 0) {
|
|
sys->fprint(stderr, "Ir.init: %r\n");
|
|
return;
|
|
}
|
|
names := array[] of {
|
|
"Zero",
|
|
"One",
|
|
. . .
|
|
"Mute",
|
|
};
|
|
for(;;) {
|
|
c = <-irchan;
|
|
if(c == ir->Error)
|
|
sys->print("Error %d\n", c);
|
|
else
|
|
sys->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->PATH;
|
|
if (m == nil)
|
|
return;
|
|
# make a place on the screen
|
|
w := screen.window(screen.image.r);
|
|
|
|
mr := chan of string;
|
|
s := m->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 {
|
|
<-mr =>
|
|
return;
|
|
i = <-cc =>
|
|
case i {
|
|
Ir->Select =>
|
|
m->ctl("stop");
|
|
Ir->Up or Ir->Dn =>
|
|
m->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 <- = 0;
|
|
}
|
|
</pre></tt></dd></dl>
|
|
<dl><dt></dt><dd><tt><pre>Monitor.unlock(m: self Monitor)
|
|
{
|
|
<- m.ch;
|
|
}
|
|
</pre></tt></dd></dl>
|
|
<dl><dt></dt><dd><tt><pre>lockproc(ch: chan of int)
|
|
{
|
|
for (;;) {
|
|
<- ch; # wait for someone to lock
|
|
ch <- = 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 > 0)
|
|
sendch = newchan;
|
|
if (n < size)
|
|
recvch = oldchan;
|
|
alt {
|
|
s = <-recvch =>
|
|
temp[(fp+n)%size] = s;
|
|
n++;
|
|
|
|
sendch <- = temp[fp] =>
|
|
temp[fp++] = nil;
|
|
n--;
|
|
if (fp>=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->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->bufchan(sourcech, 10);
|
|
s := <- 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>-></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> =></tt><i>
|
|
pick-member-list pick-tag-list</i><tt> =></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> =></tt><i>
|
|
qual-statement-sequence qual-list</i><tt> =></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> =></tt><i>
|
|
pqual-statement-sequence pqual-list</i><tt> =></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>* / % + - << >> < > <= >= == != & ^ | :: && ||</tt><i>
|
|
<br>
|
|
</i></pre></tt></dd></dl><i>
|
|
</i><dl><dt></dt><dd><tt><pre><br>
|
|
<i>assignment-operator: one of
|
|
</i><tt>= &= |= ^= <<= >>= += -= *= /= %=</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> -> </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 * ++ -- <- 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> => </tt><i>expression
|
|
</i><tt>* => </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> <- = </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> |