Conditional Execution

Sather - A Language Tutorial
Prev | Chapter 2. Classes and Objects | Next

# 2.5. Conditional Execution

Sather supports the standard constructs for conditional execution - if statements and multi-way case statements

## 2.5.1. if statements

if statements are used to conditionally execute statement lists according to the value of a boolean expression. In this form, the if keyword is followed by a boolean expression, the keyword then, a list of statements and the final keyword end. When the statement is executed, the boolean expression is evaluated and if the result is true the statements in the statement list are executed. If it is false,then control passes directly to the end of the if statement.

    i:INT := -15;
    if i < 0 then
       i:=-i;
    end;
    #OUT + i;                  -- Prints out 15
    j:INT := 15;
    if j < 0 then
       j:=-j;
    end;
    #OUT + j;                  -- Prints out 15

It often happens that one wishes to perform a sequence of tests, executing only the statements which correspond to the first test in the sequence which evaluates to `true`. For example, we may want to produce a integer value `'y'` from an integer value `'x'` which has the shape of a triangular bump. It should be zero when `'x<0'`, equal to `'x'` when `'0<=x<100'`, equal to `'200-x'` when '`100 <= x<200'`, and equal to `'0'` when `'x>=200'`. This can be accomplished with a nested series of if statements:

    if x < 0 then
       y := 0;
    else
       if x < 100 then
          y := x;
       else
          if x < 200 then
             y := 200 - x;
          else
             y := 0;
          end;
       end;
    end;

Because this kind of construct is so common and the deeply nested `if` statements can get confusing, Sather provides a special form for it. A series of `elsif` clauses may appear after the statements following the `then` keyword:

    if x < 0 then
       y := 0;
    elsif x < 100 then
       y := x;
    elsif x < 200 then
       y := 200 - x;
    else
       y := 0;
    end;

There may be an arbitrary number of such elsif clauses. Each is evaluated in turn until one returns true. The statement list following this clause is evaluated and the statement finishes. If none of the expressions is true, the statements following the final `else` clause are evaluated.

## 2.5.2. case statements

Multi-way branches are implemented by case statements. There may be an arbitrary number of when clauses and an optional else clause. The initial construct is evaluated first and may have a return value of any type.

    i:INT := 7;
    switch i
    when 1,2,3 then
       j := 3;
    when 4,5,6 then
       j := 4;
    when 7,8,9 then
       j := 5;
    else
       j := 10;
    end;
    #OUT + j;           -- Prints out 5

This type must define one or more routines named `'is_eq'` with a single argument and a boolean return value.

    class POINT is
       attr x,y:INT;

       create(x,y:INT):POINT is
          res:POINT := new; res.x := x; res.y := y; return res;
       end;

       is_eq(point2:POINT):BOOL is
          -- In Sather,= is short hand for a call on 'is_eq'
          return x = point2.x and y = point2.y;
       end;

       str:STR is return "X=" + x + " Y=" + y; end
    end;

Points can then be used in a case statement as shown below

    p:POINT := #POINT(3,4);
    zero_point:POINT := #POINT(0,0);

    case p
    when zero_point then
       #OUT + "Zero point\n";
    when #POINT(1,1), #POINT(1,-1),#POINT(-1,-1), #POINT(-1,1) then
       #OUT + "Unit point: " + p.str + "\n";
    else
       #OUT + "Some other point\n";
    end;

Note that the equal sign is really short hand for the routine `is_eq.` The case statement is equivalent to an `if` statement, each of whose branches tests a call of `is_eq`. Thus the above case is equvalent to

    if p = zero_point then
       #OUT + "Zero point\n";
    elsif p = #POINT(1,1) or p = #POINT(1,-1) or ... etc. then
       #OUT + "Unit point:" + p.str + "\n";
    else
       #OUT + "Some other point\n";
    end;

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 for case statements but not for if statements.

## 2.5.3. Short circuit boolean expressions: and and or

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.

Consider the code

    p: POINT;
    if p.x > 3 then
       #OUT + p.x;
    end;
     -- Runtime error if p is void

The above block of code will work if `p` is not void. If it is void, however, the test `p.x >3` will result in a runtime error, since it is attempting to dot into a void reference type. We can catch this problem by using the following piece of code, and the semantics of the short-circuit and

    if ~void(p) and p.x > 3 then
       -- The ~ symbol indicates logical negation
       #OUT + p.x;
    end;

The above piece of code will not generate an error, even if `p` is void. The first part of the and expression tests for whether `p` is void. If it is void, then the void test returns true and the not turns this into a false. The and therefore fails before trying to evaluate the dotted expression `p.x`.

A similar behavior can be seen with the short-circuit or statement, where the second expression is not examine if the first expression evaluates to true

    a:INT := 15;
    p:POINT;
    if a>10 or p.x < 10 then
       -- Since a>10 is true, the second expression is not evaluated

- Note that booleans also define an `and_rout` routine, which does not have the same short-circuit behavior:

      if ~void(p).and_rout(p.x > 3) then
      -- May generate a run-time error, when 'p' is void
      -- The argument to the 'and_rout' routine (p.x) is evaluated
      -- even when the first condition, ~void(p) fails.
      -- Hence, if 'p' is void, p.x is still evaluated and generates a
      -- run-time error (attribute access of void)

Prev | Home | Next
Routine definitions | Up | Attribute Accessor Routines