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

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