-
Notifications
You must be signed in to change notification settings - Fork 2
Language Specification (6) : Statements
A statement represents a computation that causes a transformation of the computational state of a program. Statements are used for their effects only, they do not return values and they may not occur within expressions.
The assignment statement is a value-mutator statement. It assigns a given value to a mutable value.
assignment := targetDesignator ’:=’ expression ;
The designator is called the L-value and must be mutable. The expression is called the R-value and must be assignment compatible to the L-value. If these conditions are not met, a compile time error will occur. Assignment of constants NIL
, 0u0
or 0
and calls to procedures containing such assignments shall be considered intentional and shall not be subjected to dead store elimination!
The INC/DEC statement is a value-mutator statement. It consists of a designator followed by a mutator symbol, where ++
increments and --
decrements the designator by one. The designator is an L-value. It must be mutable and of a whole number type. If these conditions are not met, a compile time error will occur.
incOrDecStatement := designator ( ’++’ | ’--’ );
The COPY statement is a value-mutator statement. It copies values across type boundaries where types have a common or compatible value type.
copyStatement := COPY targetDesignator ’:=’ expression ;
The designator is called the L-value, the expression is called the R-value. The L-value must be mutable. The R-value must be copy compatible to the L-value. Both must be non-elementary types. If these conditions are not met, a compile time error will occur.
Variables of different set types are not assignment compatible but they are copy compatible if their value types match or one is an extension or subtype of the other.
Given the declarations
TYPE BaseColour = ( Red, Green, Blue ); ExtColour = ( +BaseColour, Orange, Magenta, Cyan );
TYPE BaseColours = SET OF BaseColour; ExtColours = SET OF ExtColour;
VAR baseColours : BaseColours; extColours : ExtColours;
a statement of the form
COPY extColours := baseColours
copies set baseColours
to set extColours
Variables of different array types are not assignment compatible but they are copy compatible if their value types match or one is an extension or subtype of the other.
Given the declarations
TYPE PosInt = [0..TMAX(INTEGER)] OF INTEGER;
TYPE ArrayA = ARRAY 20 OF INTEGER; ArrayB = ARRAY 10 OF PosInt;
VAR arrayA : ArrayA; arrayB : ArrayB;
a statement of the form
COPY arrayA := arrayB
copies array arrayB
to array arrayA
a statement of the form
COPY arrayB := arrayA
copies array arrayA
to array arrayB
Variables of array and set types are not assignment compatible with each other but they are copy compatible if their value types are copy compatible.
Given the declarations
TYPE Token = ( Unknown, Alias, And, Arglist, Array, Begin ... );
TYPE TokenSet = SET OF Token; TokenArray = ARRAY 100 OF Token;
VAR set : TokenSet; array : TokenArray;
a statement of the form
COPY set := array
copies the tokens stored in array
to set
a statement of the form
COPY array := set
copies the tokens stored in set
to array
A procedure call statement is used to invoke a procedure. It consists of a designator that designates the called procedure, optionally followed by a list of parameters enclosed in parentheses to be passed to the procedure. Parameters passed are called arguments or actual parameters, those defined in the procedure’s header are called formal parameters.
In every procedure call, the types of actual and formal parameters must match. If they don’t, a compile time error will occur. Procedure calls may be recursive, that is, a procedure may call itself within its body. Recursive calls shall be optimised by tail call elimination (TCE), except when generating source code in a target language that does not support TCE.
The RETURN
statement is a flow-control statement used within a procedure body to return control to its caller
and in the main body of the program to return control to the operating environment that activated the program.
Depending on the type of procedure in which a RETURN
statement is used, it may or may not return a value. When returning from a regular procedure, no value may be returned. When returning from a function procedure a value of the procedure’s return type must be returned. Non-compliance will cause a compile time error.
The NEW
statement is a memory management statement. It allocates storage for a new instance of the target
type referenced by the type of its argument. Its syntax is
newStmt := NEW designator ( ’:=’ initValue | CAPACITY expression )? ;
where designator
must be an L-value and initValue
must be an R-value.
A statement of the form
NEW p
allocates a new instance of the target type of pointer p
and passes a reference to the new instance in p
By default, a newly allocated instance is not initialised. To initialise a new instance during its allocation, an initialisation value may be specified within a NEW
statement.
A statement of the form
NEW array := { a, b, c }
allocates a new instance, initialises it with values a
, b
and c
, and passes a reference to it in array
A capacity value may be specified to initialise a new instance of an indeterminate type.
A statement of the form
NEW buffer CAPACITY 1000
allocates a new instance of an indeterminate type with a capacity limit of 1000 values of the type’s indeterminate field and passes a reference to the new instance in buffer
The RETAIN
statement is a memory management statement. It prevents premature deallocation of the target
referenced by its argument. It is only supported with data types that provide a library binding to RETAIN
.
The RELEASE
statement is a memory management statement. It deallocates the target unless there are any
prior invocations of RETAIN
outstanding for the target. If so, RELEASE
cancels one prior invocation of RETAIN
.
The IF
statement is a flow-control statement that passes control to one of a number of blocks within its body depending on the value of a boolean expression. It evaluates a condition in form of a boolean expression. If the condition is TRUE
then program control passes to its block. If the condition is FALSE
and an ELSIF
branch follows, then program control passes to the branch to evaluate that branch’s condition.
Again, if the condition is TRUE
then program control passes to the block of the branch. If there are no ELSIF
branches, or if the conditions of all ELSIF
branches are FALSE
, and if an ELSE
branch follows, then program control passes to the block. At most one block in the statement is executed. IF
statements must always be terminated with an END
.
The CASE
statement is a flow-control statement that passes control to one of a number of labeled statements
or statement sequences depending on the value of an ordinal expression.
caseStatement := CASE expression OF ( ’|’ case )+ ( ELSE statementSequence )? END ;
case := caseLabels ( ’,’ caseLabels )* : StatementSequence ;
caseLabels := constExpression ( .. constExpression )? ;
Control is passed to the first statement following the case label that matches the ordinal expression. Case labels must be unique. There is no “fall through”. At the end of a label, control is passed to the first statement after the CASE
statement. If no case label matches, control is passed to the ELSE
block, or, if there is no ELSE
block,
to the first statement after the CASE
statement.
The statement is also used as a type-guard to ensure type safety when addressing fields of a type extension of an extensible record whose exact type is unknown. In this case, the fields of a specific type extension are only addressable within a case label matching the specific extension type. Otherwise a compile time fault occurs.
CASE record OF (* record may be of any extension of the root type or the root type itself *)
| ExtnType : record^.extnField := value; (* addressing a field of a specific type extension *)
The LOOP
statement is a flow-control statement used to repeat a statement or statement sequence within its
body indefinitely unless explicitly terminated by an EXIT
statement within its body.
The WHILE
statement is a flow-control statement used to repeat a statement or statement sequence depending on a condition in form of a boolean expression. The expression is evaluated each time before the DO
block is executed. The DO
block is repeated as long as the expression evaluates to TRUE
unless the statement is explicitly terminated by an EXIT
statement within its body.
The REPEAT
statement is is a flow-control statement used to repeat a statement or statement sequence depending on a condition in form of a boolean expression. The expression is evaluated each time after the REPEAT
block has executed. The REPEAT
block is repeated as long as the expression evaluates to TRUE
unless the statement is explicitly terminated by an EXIT
statement within its body.
The FOR
statement is a flow-control statement that iterates over an iterable entity, executing a statement or statement sequence during each iteration cycle. It consists of a loop header and a loop body. The header consists of one or two loop variants, an optional descender, and an iterable expression. The body consists of a statement or statement sequence.
forStatement := FOR forLoopVariants IN iterableExpr DO statementSequence END ;
The loop variant section contains one or two identifiers through which the accessor of the iterated entity and its value are referenced within the loop.
forLoopVariants := accessor ( ’--’ )? ( ’,’ value )? ;
alias accessor, value = ident ;
The loop variant identifiers are declared by the loop header and they are only in scope within the header and body. Once a FOR
statement has terminated, its loop variants are no longer in scope. The number of loop variants depends on the loop’s iterable expression.
- If the iterable expression is an ordinal type, a subrange of an ordinal type or the designator of a set, then the accessor is also its value. The header thus contains only one loop variant, immutable within the loop body.
- If the iterable expression is the designator of an array
a
, the header contains an accessor representing the iteration indexi
and an optional second loop variantv
representing its valuea[i]
. Indexi
is always immutable within the loop body. The value is mutable ifa
is mutable, otherwise immutable. - If the iterable expression is the designator of a key-value dictionary
d
, the header contains an accessor representing the iteration keyk
and an optional second loop variantv
representing the key’s valued[k]
. Keyk
is always immutable within the loop body. The value is mutable ifd
is mutable, otherwise immutable.
During the first iteration cycle the loop variants reference that accessor, value or accessor/value pair which is first for the prevailing iteration order. Before each subsequent iteration cycle the loop variants are advanced to their successor for the prevailing iteration order. Iteration continues until all accessors, values or pairs have been visited unless the FOR
statement is explicitly terminated by an EXIT
statement within its body.
The actual order of values is determined by the type of the iterable. The default iteration order is ascending. For descending order, the first loop variant may be suffixed with a descender, denoted by the --
symbol.
The iterable expression — or simply iterable — is denoted by (1) the identifier of an ordinal or subrange type, (2) an anonymous subrange thereof, or (3) the identifier of a set, array or key-value dictionary, or an array slice.
iterableExpr := qualident ordinalRange? ;
If the iterable is an ordinal type or a subrange thereof, only one loop variant may be given. The loop variant is immutable. Its type is the ordinal type or subrange given and the loop iterates over all values of the iterable.
A statement of the form
FOR char IN CHAR DO
WRITE char
END (* FOR *)
iterates over all values of type CHAR
.
Given declaration
TYPE Colour = ( Red, Green, Blue )
a statement of the form
FOR colour IN Colour DO
...
END (* FOR *)
iterates over all enumerated values of type Colour
.
The current value is referenced as colour
within the loop.
A statement of the form
FOR value-- IN CARDINAL[1..99] DO
WRITE value, " bottles of beer, take one down and pass it around.\n"
END (* FOR *)
iterates over subrange [1..99]
of type CARDINAL
in descending order.
If the iterable is the designator of a set, only one loop variant may be given. The loop variant is immutable. Its type is the element type of the set and the loop iterates over all values in the set.
A statement of the form
FOR elem IN set DO...END
iterates over all elements stored in set
.
If the iterable is the designator of an array or array slice, one or two loop variants may be given. The first is immutable and of type LONGCARD
. The optional second is mutable if the array is mutable, otherwise immutable, and its type is the value type of the array. The loop iterates over all values stored in the array.
A statement of the form
FOR index, value IN array DO array[index] := value END;
iterates over all values of array
.
A statement of the form
FOR index, value IN source[n..m] DO WRITE "source[", index, "] = ", value, "\n" END;
iterates over all values in array slice source[n..m]
.
If the iterable is the designator of a key-value dictionary, one or two loop variants may be given. The first is immutable and of the dictionary’s key type. The optional second is mutable if the dictionary is mutable, otherwise immutable, its type is the value type of the dictionary. The loop iterates over all key-value pairs stored in the dictionary.
A statement of the form
FOR key, value IN dict DO WRITE "dict[", key, "] = ", value, "\n" END;
iterates over all key-value pairs of dict
.
The EXIT statement is a control-flow statement used within the body of a LOOP
, WHILE
, REPEAT
or FOR
statement to terminate execution of the loop and transfer control to the first statement after its body. The EXIT
statement may only occur within the body of loop statements. Non-compliance will cause a compile time error.
The READ
statement reads one or more values from a communications channel and transfers the values in a
non-empty variadic list of designators. Its syntax is
readStmt := READ ( ’@’ chan ’:’ )? NEW? designator ( ’,’ designator )*
where chan
is the designator of a communications channel.
A statement of the form
READ @file : foo, bar, baz;
reads three values from channel file
and passes them in foo
, bar
and baz
.
The communications channel may be omitted in which case a default input channel is used.
A statement of the form READ foo
reads a value from the default input channel and passes it in variable foo
.
The list of designators may be prefixed by reserved word NEW
to allocate new memory for each value read. In
this case every designator must designate a value of a pointer type and its value must be NIL
.
A statement of the form
READ @file : NEW ptr;
reads a value from channel file
, allocates a new instance of ptr
and passes the read value in ptr^
.
The WRITE
Statement statement writes one or more values to a communications channel. Its syntax is
writeStmt := WRITE ( ’@’ chan ’:’ )? outputArgs ( ’,’ outputArgs )* ;
outputArgs := formattedArgs | unformattedArgs ;
formattedArgs := ’#’ ’(’ fmtStr, expressionList ’)’ ;
alias unformattedArgs = expressionList ;
where chan
is the designator of a communications channel and fmtStr
is a format specifier string.
A statement of the form
WRITE @file : foo, bar, baz;
writes the values foo
, bar
and baz
to channel file
.
The communications channel may be omitted in which case a default output channel is used.
A statement of the form WRITE foo
writes the value of foo
to the default output channel.
The list of output values may include formatted and unformatted values. A formatted value or value list is preceded by a format specifier, enclosed in parentheses and preceded by #
.
A statement of the form
WRITE @file : "Price: ", #("5:3;2", price), "incl. VAT\n";
writes three values to channel file
applying format specifier "5:3;2" to value price
.
Format specifiers are library defined but language specified for all built-in types, their interpretation takes place within the IO library. The syntax of format specifiers for built-in types is described in the Appendix.
The NOP
statement represents an explicit empty statement.
The grammar forbids empty loops and blocks. When intended, the NOP
statement must be used instead.
WHILE stream.nextChar() = ’ ’ DO (* skip all whitespace *)
NOP
END; (* WHILE *)
Copyright © 2015-2018 Modula-2 Software Foundation