Sather - A Language Manual - Statement and Expression Catalogue

[back] [Abstract] [Copyright Notice] [Contents]

# Sather - A Language Manual - Chapter 15
Statement and Expression Catalogue

This chapter presents a catalogue of statements and expressions in Sather and descriptions of them that originated in the specification. In some cases, these definitions are duplicated elsewhwere in the text. However, they have been included here, sometimes with more elaborate examples, as a convenient reference.

## 15.1 Statements

### 15.1.1 Assignment statements

Examples:

    a := 5;
    b(7).c := 5;
    A::d := 5;
    [3] := 4;
    e[7,8] := 5;
    g:INT := 5;
    h ::= 5;

Assignment statements are used to assign objects to variables or attributes. The expression on the right hand side must have a return type which is a subtype of the declared type of the destination specified by the left hand side. When a reference object is assigned to a location, only a reference to the object is assigned. This means that later changes to the state of the object will be observable from the assigned location. Since immutable and closure objects cannot be modified once constructed, this issue is not relevant to them.

An assignment can also declare new local variables using the `::=` syntax.

The operation of assignment statements on attributes is described in the section on Attribute Accessor Routines. They are often syntactic sugar for function calls with one argument, which is the right hand side.

- Type inference in assignment statements: Static Type Inference, section 2.7

- Attribute assignment sugar: Attribute assignment, subsection 2.6.1

- Array element assignment: Array Access, subsection 6.2.1

- Immutable class attribute assignment on Attribute access routines, subsection 8.1.4

### 15.1.2 `case` statements

Example:

    case i
    when 5,6 then ...
    when j then ...
    else ...
    end;

Multi-way branches are implemented by case statements. There may be an arbitrary number of when clauses and an optional else clause. The initial expression construct is evaluated first and may have a return value of any type. This type must define one or more routines named '`is_eq`' with a single argument and a boolean return value.The expressions tested in the branches of the if statement are the expressions of successive when lists. The first one of these calls to returns true causes the corresponding statement list to be executed and control passed to the statement following the case statement. If none of the when expressions matches and an else clause is present, then the statement list following the else clause is executed

There is one difference between the `case` statement and the equivalent `if` statement. If none of the branches of an `if` statement match and no `else` clause is present, then execution just continues onto the next statement after the `if` statement. However, if none of the branches of the `case` statement matches and there is no `else` clause, then a fatal run-time error will result.

Points to note

- It is a fatal error if no branch matches and there is no `else` clause.

See case statements, subsection 2.5.2 for a statement description.

### 15.1.3 `if` statements

Example:

    if a>5 then
       foo;
    elsif a>2 then
       bar;
    else
       error;
    end;

if statements are used to conditionally execute statement lists according to the value of a boolean expression. Each expression that is tested must return a boolean value. The first expression is evaluated and if it is true, the following statement list is executed. If it is false, then the expressions of successive `elsif` clauses are evaluated in order. The statement list following the first of these to return `true` is executed. If none of the expressions return true and there is an `else` clause, then its statement list is executed. Note that the else clause is not compulsory.

See if statements, subsection 2.5.1 for a statement description.

### 15.1.4 `protect` statements

Example:

    protect
       < some statements >
    when $STR  then
       #ERR + exception.str;
    when FOO then
       #ERR + exception.foobar;
    else
       < some statements >
    end;

Exceptions may be explicitly raised by a program or generated by the system. Each exception is represented by an exception object whose type is used to select a handler from a protect statement. Execution of a `protect` statement begins with the statement list following the '`protect`' keyword. These statements are executed to completion unless an exception is raised which is not caught by some nested `protect`.

When there is an uncaught exception in a protect statement, the system finds the first type specifier listed in the '`when`' lists which is a supertype of the exception object type. The statement list following this specifier is executed and then control passes to the statement following the `protect` statement. An `exception` expression may be used to access the exception object in these handler statements. If none of the specified types are supertypes, then the statements in an '`else`' clause are executed if it is present. If it is not present, the same exception object is raised to the next most recently entered `protect` statement which is still in progress. It is a fatal error to raise an exception which is not handled by some `protect` statement. `protect` statements may only contain iterator calls if they also contain the surrounding `loop` statement. `protect` statements without an `else` clause must have at least one `when`.

See Exceptions, chapter 10 for more information.

### 15.1.5 `loop` statements

Example:

    f: INT:=4; -- Compute b factorial
    res: INT := 1;
    i :INT := 1;
    loop until!(i > f);
       res := res * i;
       i := i + 1;
    end;

Iteration is done with loop statements, used in conjunction with iterator calls. An execution state is maintained for each textual iterator call. When a loop is entered, the execution state of all enclosed iterator calls is initialized. When an iterator is first called in a loop, the expressions for `self` and for each `once` argument are evaluated left to right. Then the expressions for arguments which are not `once` (`in` or `inout` before the call, `out` or `inout` after the call; are evaluated left to right. On subsequent calls, only the expressions for arguments which are not `once` are re-evaluated. `self` and any `once` arguments retain their earlier values. The expressions for `self` and for `once` arguments may not themselves contain iterator calls (such iters would only execute their first iteration.) .

When an iterator is called, it executes the statements in its body in order. If it executes a `yield` statement, control is returned to the caller. Subsequent calls on the iterator resume execution with the statement following the `yield` statement. If an iterator executes `quit` or reaches the end of its body, control passes immediately to the end of the innermost enclosing loop statement in the caller and no value is returned.

See also: Loops and Iterators, chapter 3

### 15.1.6 `return` statements

Examples:

    foo(a: INT): INT is
       return a*10;
    end;

return statements are used to return from routine calls. No other statements may follow a `return` statement in a statement list because they could never be executed. If a routine doesn't have a return value then it may return either by executing a `return` statement without an expression portion or by executing the last statement in the routine body.

If a routine has a return value, then its `return` statements must specify expressions whose types are subtypes of the routine's declared return type (see the chapter on Abstract Classes and Subtyping). Execution of the `return` statement causes the expression to be evaluated and its value to be returned. It is a fatal error if the final statement executed in a routine with a return type is not a `return` or `raise` statement.

### 15.1.7 `typecase` statements

Example:

    typecase a
    when INT then ...
    when FLT then ...
    when $A then ...
    else ...
    end;

For a description of the typecase statement see The `typecase` statement, section 5.6

An operation that depends on the runtime type of an object held by a variable of abstract type may be performed inside a typecase statement. The variable in the typecase ('a' in the above example) must name a local variable or an argument of a method. If the `typecase` appears in an iterator, then the mode of the argument must be `once`; otherwise, the type of object that such an argument holds could change.

On execution, each successive type specifier is tested for being a supertype of the type of the object held by the variable. The statement list following the first matching type specifier is executed and control passes to the statement following the `typecase`. Within each statement list, the type of the `typecase` variable is taken to be the type specified by the matching type specifier unless the variable's declared type is a subtype of it, in which case it retains its declared type. It is not legal to assign to the `typecase` variable within the statement lists. If the object's type is not a subtype of any of the type specifiers and an `else` clause is present, then the statement list following it is executed.

It is a fatal error for no branch to match in the absence of an `else` clause. The declared type of the variable is not changed within the `else` statement list. If the value of the variable is `void` when the `typecase` is executed, then its type is taken to be the declared type of the variable.

### 15.1.8 `yield` statements

Examples:

    odd_upto!(n: INT): INT is
       i: INT := 0;
       loop until!(i = n);
          if i.is_odd then
             yield i;
          end;
          i := i + 1;
       end;
    end;

`yield` statements are used to return control to a loop and may appear only in iterator definitions. The yield statement must be followed by a value if the iterator has a return value and must be absent if it does not. The value yielded must be a subtype of the return type of the iterator. Execution of a `yield` statement causes the expression to be evaluated and its value to be returned to the caller of the iterator in which it appears. Yield is not permitted within a protect statement. Yield causes assignment to `out` and `inout` arguments in the caller

In the example above the iterator yields odd numbers upto the specified value, "n".

See Loops and Iterators, chapter 3 for more details.

### 15.1.9 `quit` statements

quit statements are used to terminate loops and may only appear in iterator definitions. No value is returned from an iterator when it quits, and no assignment takes place to out or inout arguments in the caller. No statements may follow a quit statement in a statement list.

See Loops and Iterators, chapter 3 for more details.

## 15.2 Expressions

We describe below a few special expressions used in Sather - `void`, `void()` and the short circuit boolean operations `or` and `and`.

### 15.2.1 `void` expressions

A void expression returns a value whose type is determined from context. `void` is the value that a variable of the type receives when it is declared but not explicitly initialized. The value of `void` for objects (except for immutable objects) is a special value that indicates the absence of an object - it is essentially the NULL pointer. Immutable objects are described in their own chapter, but for the sake of reference:

    Class     Initial Value
    -----------------------
    INT       0
    CHAR      '\0'
    FLT       0.0
    FLTD      0.0d
    BOOL      false

For other immutable types the void value is determined by recursively setting each attribute and array element to `void`[18]. For numerical types, this results in the appropriate version of 'zero'.

`void` expressions may appear

- as the initializer for a constant or shared attribute. In fact, for most built-in classes, the only legal constant value is the void value e.g.

    const a: POINT := void;

- as the right hand side of an assignment statement

- as the return value in a `return` or `yield` statement

- as the value of one of the expressions in a `case` statement

- as the exception object in a `raise` statement (see the chapter on Exceptions)

- as an argument value in a method call

- in a creation expression. In this last case, the argument is ignored in resolving overloading.

`void` expressions may not appear:

- as the left argument of the dot '`.`' operator.

      a: POINT := #POINT(3,3); -- ILLEGAL (and silly) a.void

It is a fatal error to access object attributes of a void variable of reference type or to make any calls on a void variable of abstract type. Calls on a void variable of an immutable type are, however, quite legal (otherwise you would not be able to dot into a false boolean or a zero valued integer!)

### 15.2.2 `void` test expressions

Example:

    void(x)

Void test expressions evaluate their argument and return a boolean value which is true if the value is `void` .

    p: POINT;
    #OUT + void(p);   -- Prints out true
    p := #POINT(3,5);
    #OUT + void(p);   -- Prints out false
    p := void;
    #OUT + void(p);   -- Prints out true;
    b: BOOL;
    #OUT + void(b);   -- Prints out true
    b := false;
    #OUT + void(b);          -- Prints out true!
    -- Even though b has been assigned, it has the void value

### 15.2.3 Short circuit boolean expressions: `and` and `or`

Example:

    if (3>a and b>6) or (c = "Goo") then
       #OUT + "Success!"
    end;

and expressions compute the conjunction of two boolean expressions and return boolean values. The first expression is evaluated and if `false`, `false` is immediately returned as the result. Otherwise, the second expression is evaluated and its value returned.

or expressions compute the disjunction of two boolean expressions and return boolean values. The first expression is evaluated and if `true`, `true` is immediately returned as the result. Otherwise, the second expression is evaluated and its value returned.

For an expression description see Short circuit boolean expressions: `and` and `or`, subsection 15.2.3

### 15.2.4 `exception` expressions

Example:

    protect
       ... some code
    when STR then
       #OUT + exception.str;
    when ...
    else ...
    end;

exception expressions may only appear within the statements of the `when` and `else` clauses in `protect` statements. They return the exception object that caused the `when` branch to be taken in the most tightly enclosing `protect` statement. The return type is the type specified in the corresponding `when` clause (See Catching Exceptions with protect, subsection 10.1.2). In an `else` clause the return type is '`$OB`'.

[back] [Abstract] [Copyright Notice] [Contents]

Sather - A Language Manual