MAINSAIL Language Manual, Chapter 5

previous   next   top   complete contents   complete index   framed top   this page unframed


5. Statements

A
statement performs an action or directs the flow of control. This chapter describes eleven of the thirteen MAINSAIL statements: assignment, expression, PROCEDURE, RETURN, BEGIN, IF, CASE, iterative, DONE, CONTINUE, and empty. The other two statements are the INIT statement for initializing dynamic ARRAYs (see Section 12.10.2) and the $HANDLE statement for handling exceptions (see Chapter 20).

Semicolons are used to separate (rather than terminate) statements.

5.1. Assignment Statement

An assignment statement gives a value to a variable. The form of an assignment statement is v := e where v is a variable, e is an expression, and := is the assignment operator. The value of the expression e is assigned to the variable v. For example, i := 8 is an assignment statement that assigns the value 8 to the variable i.

_ (the underbar or left arrow character) may be used in place of :=.

v and e must be assignment compatible as explained in Section 4.9.

The order of evaluation of v and e is not specified, so avoid assignment statements for which the order might make a difference. For example:

i := 0; a[i] := i .+ 1

could assign the value 1 to either a[0] or a[1], since the value of i is changed to 1 by i .+ 1, but it is not specified which value of i (the one before or after the change) is used to evaluate a[i]. It is the programmer's responsibility to avoid such unspecified assignment statements.

The precedence of the assignment operator is slightly different in expressions and assignment statements; see Figure 4–3.

5.2. Expression Statement

An expression statement is a dotted expression used as a statement (see Section 4.8.6). It computes a value and assigns that value to the leftmost operand, which must be a variable. For example, if v is a variable, op is one of the operators that may be dotted (see Section 4.8.6), and e is an expression:

v .op e has the same effect as v := v op e
.- v has the same effect as v := - v

An expression statement is undefined if the expression composing it is undefined; see Section 4.8.6.

Some examples of expression statements, assuming i and j are simple variables:

Statement Effect
.- a[i] Negate a[i]. More efficient than a[i] := - a[i].
i .+ j * 2 Same as i := i + j * 2.
i .+ j .* 2 Same as i := i + (j := j * 2).

5.3. PROCEDURE Statement

A PROCEDURE statement is a PROCEDURE call (see Section 7.2). It invokes execution of the body of the called PROCEDURE. PROCEDUREs are described in Chapter 7.

A PROCEDURE used as a statement may be either typed or untyped; if typed, its value is discarded. By contrast, a PROCEDURE used in an expression must be a typed PROCEDURE (see Section 7.3); its return value is used in the expression.

For example, the system PROCEDURE cRead is an INTEGER PROCEDURE that returns the character code (see Section 2.1) of the first character of its STRING argument, and removes that character from the STRING. A sample call to cRead in an expression is:

i := cRead(s)

which removes the first character from s and puts its code into i. If it is desired to remove the first character from s without recording its value, then a PROCEDURE statement may be used:

cRead(s)

5.4. RETURN Statement

A RETURN statement returns from a PROCEDURE (see Chapter 7). The format is:

RETURN

for an untyped PROCEDURE (see Section 7.3), or:

RETURN(e)

for a typed PROCEDURE, where the value of the expression e is returned as the value of the PROCEDURE.

A RETURN statement is not necessary in untyped PROCEDUREs (Section 7.3); an untyped PROCEDURE automatically returns upon completion of the execution of the PROCEDURE body. However, a RETURN statement can be used in an untyped PROCEDURE to provide a convenient “early return”, much as the DONE statement (see Section 5.9) provides termination of an iterative statement (see Section 5.8). A RETURN statement is necessary in typed PROCEDUREs, since it provides the mechanism for returning a value. A runtime error occurs if a typed PROCEDURE reaches its final END without the execution of some RETURN(e). A warning is given at compiletime if a typed PROCEDURE contains no RETURN statement (except during an FLI or RPC compilation).

The expression returned as the value of a typed PROCEDURE must be assignment compatible (see Section 4.9) with the data type of the PROCEDURE.

An example of an untyped PROCEDURE with a RETURN statement:

PROCEDURE p;
BEGIN
INTEGER i;
...
IF i > 0 THEN RETURN;
...
END;

A typed PROCEDURE with RETURN statements:

INTEGER PROCEDURE p;
BEGIN
INTEGER i,j;
...
IF i > 0 THEN RETURN(0);
...
RETURN(j);
END;

5.5. BEGIN Statement

A BEGIN statement allows a group of statements to be treated as a single statement.

The format of a BEGIN statement is the word BEGIN followed by a sequence of statements (separated with semicolons) followed by the word END:

BEGIN s1; ...; sn END

Declarations are not allowed in BEGIN statements.

A STRING constant may follow BEGIN to give a name to the BEGIN statement, in which case the same STRING constant must also follow the END:

BEGIN "names1; ...; sn END "name"

The compiler issues a warning if it finds different STRING constants (ignoring upper and lower case distinctions) after a BEGIN and its matching END. This check helps catch mismatched BEGIN-END pairs.

A name may also appear after any of the keywords that are abbreviations for some other keyword followed by BEGIN, i.e., EB or THENB (see Section 5.6), OFB (see Section 5.7), DOB (see Section 5.8), or $HANDLEB or $WITHB (see Section 20.1). As with the corresponding unabbreviated form, the matching END must be followed by the same name.

5.5.1. Special Names for Named BEGIN Statements

There is a restriction on the STRING constants that can be used to name a BEGIN statement: they must not begin with a # character followed by a valid MAINSAIL identifier followed by another # (e.g., #foo#). XIDAK reserves BEGIN statement names of this form for internal purposes; the appearance of such a name in a user program may have undefined effects.

5.6. IF Statement

An IF statement selects one of several statements for execution depending on the values of specified expressions.

The simplest form of IF statement is IF e THEN s where e is an expression and s is a statement. s can be a BEGIN statement, that is, a list of statements enclosed in a BEGIN-END pair. If e evaluates to a non-Zero value, the statement s is executed. For example, IF i THEN j := 2 assigns j the value 2 if and only if i is not zero. Similarly, IF i = 1 THEN j := 2 assigns j the value 2 if and only if i is equal to one (for in that case the expression i = 1 is TRUE, which is the BOOLEAN non-Zero).

The other form of IF statement is IF e THEN s1 ELSE s2. If e evaluates to a non-Zero value, then s1 is executed. Otherwise (if e evaluates to Zero), s2 is executed. For example, the statement IF i = 1 THEN j := 2 ELSE k := 3 assigns j the value 2 if i has the value one, in which case the statement k := 3 is not executed. Otherwise (if i does not equal one), k is assigned the value 3, and the statement j := 2 is not executed.

Any statement in an IF statement can be an IF statement. Thus, an IF statement may look like:

IF e1 THEN s1
ELSE IF e2 THEN s2
ELSE IF e3 THEN s3
ELSE s4

The expressions e1, e2, and e3 are evaluated one by one until one of them evaluates to a non-Zero value; its associated statement (s1, s2, or s3, respectively) is then executed, and no further expressions are evaluated. If all the expressions evaluate to Zero, the statement following the final ELSE (s4) is executed.

MAINSAIL provides the abbreviations EF for ELSE IF, EL for ELSE, EB for ELSE BEGIN, and THENB for THEN BEGIN. The first three abbreviations allow alignment of conditions in IF statements for clarity, as in:

IF e1 THEN s1
EF e2 THEN s2
EF e3 THEN s3
EL s4

There is never a semicolon before an ELSE, since semicolons are used to separate statements, and ELSE is not the beginning of a statement.

An ELSE (EL) or EF is matched with the innermost unmatched IF or EF. In the following statement, the ELSE is matched with the second IF, and the EL is matched with the first IF:

IF e1 THEN
    
IF e2 THEN s1 ELSE s2
EL ...

If there were no ELSE s2 above, the EL would instead be matched with the second IF. A BEGIN statement could be used as shown below to match the EL with the first IF:

IF e1 THEN
    
BEGIN IF e2 THEN s1 END
EL ...

This might also be written as follows, using THENB:

IF e1 THENB IF e2 THEN s1 END
EL ...

5.7. CASE Statement

A CASE statement uses an INTEGER index to select one of several statements for execution. The simplest form of a CASE statement is shown in Example 5–1, where e (the index) is an INTEGER expression, the si are statements and the ci (the selectors) are INTEGER constant expressions. A semicolon separates a statement from the bracketed selector for the next statement. A semicolon may appear between the last statement sn and the END, but it is not necessary.

Example 5–1. Sample CASE Statement
CASE e OF BEGIN
    [
c1]    s1;
    [
c2]    s2;
    ...
    [
cn]    sn # semicolon optional here
    
END

Each statement is preceded by one or more selectors that specify what values of the index are to select that statement. A statement is selected if any of its selectors is satisfied. There are three forms for the selectors (Example 5–1 shows only the simplest form of selector):

Selector Corresponding Statement Is Selected If
[c] index = c.
[c1 TO c2] c1 LEQ index LEQ c2; i.e., the index is between c1 and c2. The compiler gives an error message if c1 exceeds c2.
[ ] no other statement would otherwise be selected (catchall selector).

The CASE statement shown in Example 5–1 has the same effect as (but is usually more efficient than) the following assignment and IF statements:

t := e;      # t is an INTEGER variable

IF t = c1 THEN s1
EF t = c2 THEN s2
...
EF t = cn THEN sn

MAINSAIL provides the abbreviation OFB for OF BEGIN.

The first statement with a satisfied selector is selected for execution; see Figure 5–2.

Figure 5–2. Choice of a Selector Is Affected by Ordering of Selectors
CASE num OFB "ex1"
    [3]         
s1;
    [1 
TO 7]    s2;   # s1 (and not s2is executed
    [8]         
s3    # when num has the value 3.
    
END "ex1"


CASE num OFB "ex2"
    [1 
TO 7]    s1;
    [3]         
s2;   # s2 could never be selected
    [8]         
s3    # since num = 3 would select s1.
    
END "ex2"

A runtime error occurs if the index selects no statement and there is no [] catchall selector. A runtime error would result if num had the value 9 in the CASE statement ex1 in Figure 5–2. All expected values of the index must be specified in some selector, which can be the catchall selector []. An empty statement can be used for those cases in which no action should be taken. If ex1 should do nothing whenever num has a value outside of the range 1 through 8, for example, it could be written as:

CASE num OFB
    [3]         
s1;
    [1 
TO 7]    s2;
    [8]         
s3;
    []          # 
catchall selectordo nothing
    
END

There can be no more than one catchall selector in a CASE statement; the catchall selector matches the same values no matter where it is placed in the CASE statement.

For each CASE statement, the compiler typically creates a branch table with m entries, where:

m = maximum ci - minimum ci + 1

(where ci ranges over all the selector bounds). If m is much greater than the number of cases with specified statements (that is, if the cases are spread sparsely over a wide range), the table results in a significant space overhead. The CASE statement shown below would produce a table with 3000 entries. In this case, it would be better to use an IF statement:

    CASE num OFB
        [1] [20]    
j := 3;
        [980]       
k := 8;
        [3000]      
BEGIN j := 7; k := 9 END
        
END

5.8. Iterative Statement

An iterative statement specifies a statement that is to be repeatedly executed until some condition terminates the iteration. The form of an iterative statement is:

FOR i := e1 UPTO e2 WHILE e3 DO s UNTIL e4
FOR-clause WHILE-clause   UNTIL-clause

where i is a simple local (LONG) INTEGER variable, e1 and e2 are (LONG) INTEGER expressions, e3 and e4 are any expressions, s is any statement, and UPTO may be replaced with DOWNTO. i, e1, and e2 must all be of the same data type, either INTEGER or LONG INTEGER.

The FOR-clause, WHILE-clause, and UNTIL-clause are optional clauses surrounding the required part DO s. Thus, there are eight possible forms (ignoring the distinction between UPTO and DOWNTO) depending on whether each clause is included or not, as shown:

DO s
DO s UNTIL e4
WHILE e3 DO s
WHILE e3 DO s UNTIL e4
FOR i := e1 UPTO e2 DO s
FOR i := e1 UPTO e2 DO s UNTIL e4
FOR i := e1 UPTO e2 WHILE e3 DO s
FOR i := e1 UPTO e2 WHILE e3 DO s UNTIL e4

DO s alone repeatedly executes the statement s until something in s terminates the iterative statement, such as a DONE statement (see Section 5.9), a RETURN statement (see Section 5.4), or an exception (see Chapter 20). The other forms are explained in Table 5–3. To get the equivalent forms for DOWNTO, replace LEQ with GEQ, .+ with .- and UPTO with DOWNTO on the righthand side of Table 5–3.

DOB is equivalent to DO BEGIN.

Table 5–3. Equivalent Forms of Iterative Statement
Form Equivalent Form
DO s UNTIL e4 DOB s; IF e4 THEN DONE END
WHILE e3 DO s DOB IF NOT e3 THEN DONE; s END
WHILE e3 DO s UNTIL e4 WHILE e3 DOB s; IF e4 THEN DONE END
FOR i := e1 UPTO e2 DO s i := e1; t := e2; WHILE i LEQ t DOB s; i .+ 1(L) END
FOR i := e1 UPTO e2 DO s UNTIL e4 FOR i := e1 UPTO e2 DOB s; IF e4 THEN DONE END
FOR i := e1 UPTO e2 WHILE e3 DO s FOR i := e1 UPTO e2 DOB IF NOT e3 THEN DONE; s END
FOR i := e1 UPTO e2 WHILE e3 DO s UNTIL e4 FOR i := e1 UPTO e2 WHILE e3 DOB s; IF e4 THEN DONE END

In accordance with Table 5–3, the second expression (e2) in a FOR-clause is evaluated just once, before the iterations begin. Furthermore, the use of the largest (LONG) INTEGER as e2 in an UPTO FOR-clause or of the most negative (LONG) INTEGER in a DOWNTO FOR-clause is undefined. Such forms may result in (possibly undetected) arithmetic overflow.

In accordance with Table 5–3, the value of the iterative variable after the iterative statement terminates is one greater (for UPTO) or one less (for DOWNTO) than e2, unless the iterative statement is terminated early (e.g., by means of a DONE statement), or unless the iterative variable is explicitly modified within the loop.

FOR-clause increments or decrements other than 1 or 1L are not provided. To get the effect of some other increment e, use the equivalent form shown in Table 5–3 with .+ e in place of .+ 1(L), where e is the desired increment.

The following iterative statement with a FOR-clause:

FOR i := 1 UPTO 3 DO
    
write(logFile,
        "
i is ",i,"; i squared is ",i * i,"." & eol)

writes to logFile:

i is 1; i squared is 1.
i is 2; i squared is 4.
i is 3; i squared is 9.

A STRING constant may follow DO to give a name to the iterative statement. This name may then be used in a DONE statement (see Section 5.9) or a CONTINUE statement (see Section 5.10) within s. If an iterative statement is not given a name in this manner, but s (the iterated statement) is a named BEGIN statement or CASE statement, then s's name is used as the name of the iterative statement, as in:

DOB "outer"
    ...
    
DOB "inner"
        ...
        
IF ... THEN CONTINUE "outer"; # Restart outer loop
        ...
        
IF ... THEN DONE "outer"; # Terminate outer loop
        ...
        
END "inner";
    ...
    
END "outer";

An UNTIL is matched with the innermost unmatched DO. In the following, the UNTIL in UNTIL e1 is matched with the DO in DO s1, and the last UNTIL is matched with the first DO:

DO
    
DO s1 UNTIL e1
UNTIL ...

If there were no UNTIL e1 above, a BEGIN statement (see Section 5.5) could be used as shown below to match UNTIL ... with the first DO:

DOB
    
DO s1 END
UNTIL ...

5.9. DONE Statement

A DONE statement terminates an iterative statement and must occur within an iterative statement. The form of a DONE statement is:

DONE

which terminates the innermost enclosing iterative statement, regardless of its name (if any), or:

DONE c

which terminates the innermost iterative statement with name c (see Section 5.8), where c is a STRING constant expression. To terminate an iterative statement means that the iterations are stopped, and execution continues with the statement following the iterative statement (if any; if the iterative statement is the last thing in an untyped PROCEDURE, the PROCEDURE returns).

A sample use of DONE:

STRING s;
POINTER(textFilef;
...
DOB read(f,s);
    
IF NOT $gotValue(fTHEN DONE;
    ... 
process s...
    
END;

This iterative statement keeps typing Next integer (type 0 to stop): to the terminal, adding all the numbers input from the terminal. When the end of the list is signified by an input of 0, the DONE statement terminates the iterative statement, and the sum is written to the terminal.

5.10. CONTINUE Statement

A CONTINUE statement continues an iterative statement. This means that the current iteration is stopped as if the statement being iterated had completed, and then the usual increments, decrements and tests are applied before the next iteration (if any). For example, if the iterative statement has an UNTIL-clause, execution continues with the UNTIL-clause test (which may or may not terminate the iterative statement).

The form of a CONTINUE statement is:

CONTINUE

which continues the innermost enclosing iterative statement, regardless of its name (if any), or:

CONTINUE c

which continues the innermost iterative statement with name c, where c is a STRING constant expression.

An iterative statement with a CONTINUE in it can look like the one in Example 5–4, where s1 and s2 are statements and e is an expression. Whenever the IF statement finds e to be non-Zero, the current iteration terminates (s2 is not executed) and a new iteration is begun.

Example 5–4. Iterative Statement with a CONTINUE Statement
DOB s1; IF e THEN CONTINUE; s2 END

The IF statement may be a more convenient means of controlling execution than the CONTINUE statement; for example, the iterative statement with a CONTINUE statement of Example 5–4 could also be written as:

DOB s1IF NOT e THEN s2 END

5.11. Empty Statement

The empty statement consists of nothing at all.

An empty statement is allowed wherever any statement may occur.

For example, a semicolon between the final statement and the END of a BEGIN statement is not required, since semicolons are used to separate statements, not to terminate them. Nevertheless, a semicolon is accepted there by the compiler; the semicolon indicates that an empty statement follows the semicolon. Therefore:

BEGIN s1s2s3END

has the same effect as:

BEGIN s1s2s3 END

The empty statement can also be used, for example, for those cases in which no action is to be taken in a CASE statement, as in:

CASE i OFB
    [0]             
j := i;
    [1 
TO 4]        BEGIN j := i + kk .MAX i END;
    [5]             
j := (i + kDIV j;
    []              # 
empty statementdo nothing
    
END

where an empty statement follows the [] catchall selector.

5.12. Parenthesized Statements

MAINSAIL expressions that can appear as statements (i.e., assignments, dotted operations, and PROCEDURE calls) can be enclosed in parentheses when they appear as statements. For example:

(n .+ 1);

is a valid MAINSAIL statement.

This makes it possible for invocations of macros consisting of expressions that can appear as statements to be used either as statements or within larger expressions without having to worry about the precedence of any operators that might surround the macro invocation.


previous   next   top   complete contents   complete index   framed top   this page unframed

MAINSAIL Language Manual, Chapter 5