At program start-up time, all objects are first initialized to the appropriate initial object. Next, the object expressions are evaluated in a compilation specific order. (Usually, the order that the modules are specified on the link command line.)
The language mandates the order of these clauses in order to improve uniformity between code.
For example:
procedure at_exit
routine_types
procedure exit_routine_type
takes_nothing
returns_nothing
takes
exit_routine exit_routine_type
returns_nothing
#: This procedure will register {exit_routine}
#, to be called when the program is exiting.
defines a procedure which takes as its only argument,
exit_routine, a procedure variable that
will eventually be invoked upon exit. The signature
of the procedure variable is specified by the
routine_types clause to be a routine that
takes no arguments and returns no values.
The routine_types clause can reference any types imported into the module or defined within the module. In addition, for parameterized routines, the routine_types clause can reference any of the routine parameter types.
An example of a routine_types clause that references a parameter type is shown below:
procedure on_expose_set@window1[co_type]
routine_types
procedure expose_routine_type
takes window, region, co_type
returns_nothing
takes
window window
expose_routine expose_routine_type
returns_nothing
#: This procedure stores the procedure variable
#, to call when a region is exposed on {window}.
In the example immediately above, the
routine_types clause defines the
expose_routine_type procedure
variable type whose third argument type is
the type co_type, the parameter
name from window1[co_type].
For example, consider the code fragment that calls the procedure named add,
...
x := add(y + 10, z - 17)
...
where add is the following procedure:
procedure add
takes
a unsigned
b unsigned
returns unsigned
return a + b
The procedure call sequence is equivalent to:
a := y + 10
b := z - 17
where a and b are variables in the add procedure
and x and y are evaluated in the environment at
the call site.
variadic_clause
--> variadic variadic_size_variable { [ variadic_parameter_name, ... ] } ø
¿ { variadic_name signature_type_reference }+
<
{ variadic_needs_clause }
variadic_size_variable
--> identifier
variadic_parameter_name
--> identifier
variadic_name
--> identifier
variadic_needs_clause
--> variadic_needs ¤ need_clause
A variadic routine is one with a variadic clause
(see
Why variadic procedures.) The variadic clause
in conjunction with the optional variadic_needs
clause specifies a template, called a variadic
record. A variadic routine invocation can pass
zero, one, or more variadic records as arguments.
The identifier immediately following the variadic
keyword is called the variadic size variable; it
has a type of unsigned and is assigned the
number of variadic records passed into the variadic
procedure. Following the variadic size variable is
an optional parameter list enclosed in square
brackets ("[...]".) Variadic parameter
lists are discussed shortly. The lines following
the variadic keyword define the fields and types
that make up the variadic record in exactly the
same format as a record type declaration.
A variadic procedure with T arguments in the takes clause and V arguments in the variadic clause must be invoked with T + nV arguments, for some non-negative value of n. n is the number of variadic records and is assigned the to the variadic size variable. The types of the first T arguments must match the corresponding types of the takes clause. The remaining nV arguments must match the corresponding types in the variadic clause in a round-robin fashion.
For example, the following two variadic procedures:
procedure sequence
takes
heap
variadic size
value unsigned
returns vector[integer]
...
procedure wire
takes
heap
variadic count
x integer
y integer
returns wire
...
can be invoked as follows:
primes := sequence(heap1, 1, 2, 3, 5, 7, 11, 13) # size = 7
fibonacci := sequence(heap1, 1, 1, 2, 3, 5, 8, 13) # size = 7
empty := sequence(heap1) # size = 0
error := sequence(heap1, -1, -2, -3) # Type error, must be unsigned
wire1 := wire(heap1, 0, 0, 1, 1) # count = 2
none := wire(heap1) # count = 0
bad := wire(heap1, 1, 2, 3) # Error, no y value for last pair
The values of variadic arguments are accessed
using the variadic_with statement (see
the variadic_with statement.)
The following example is a simple implemention of the out_stream@print variadic procedure, which requires a variadic_needs clause:
procedure print@out_stream
takes
output out_stream
format_string string
variadic count [type]
value type
returns unsigned
variadic_needs
procedure format@type
takes type, out_stream, in_stream
returns unsigned
format_stream:: in_stream :=
format_string~in_stream_convert(heap@standard)
index:: unsigned := 0
width:: unsigned := 0
loop
for chr:: character := format_stream~characters() as format_chrs
if chr = "%"
if !format_stream.empty && format_stream~peek() = "%"
next chr := format_chrs
chr~print(output)
width++
else_if index < count
variadic_with index++
width += value~format(output, format_stream)
else
width += output~print(`')
else
chr~print(output)
width++
if index < count
width += output~print(`<%d arguments not printed>', count - index)
return width
This routine takes an output stream and a format
string followed by a sequence of zero, one, or
more arguments of any type. Each argument is put
into a variadic record along with an associated
routine called format@type. (In addition, the
initial object, type@??, is implicitly passed
in the variadic record.) Errors are printed by
recursively calling itself. The actual
implementation of this routine is more
sophisticated in that it permits the printing
of out-of-order arguments, and fixed width fields.
If a procedure does not return any values, the returns_nothing clause is specified.
Some procedures, like exit@system, have the attribute that they will never return. (This is different from returning no values.) A procedure that never returns specifies the returns_never keywords for the returns clause. Thus,
procedure exit@system
takes
return_code integer
returns_never
...
is the correct definition for the exit@system
procedure.
Signals are raised via the signals statement.
{This is a little too terse.}