Eve Control Flow

Most languages have similar flow control statements. These are block statements that can execute or no depending on one or more conditions. A control flow statement can be a decision statement a repetitive statement or a selection statement.

General Pattern

Most control statements have a block structure with data scope, specific keyword pairs for declaration region, execution region start and end of block. We diverge from classic begin...end or {}, to create a more verbose and relevant syntax.


[label:] <control_type>
    ** Declarations & local scope initializations
<transition_keyword>
    ** Executable statements
[optional clauses]
<terminator>

Statements that represent a note, or a jump point require label. Statements that do not represent a jump point have optional label. We use a label only if we need to be specific about the control statement closing it or jumping back to it or refer it.

Sequential job

A job is a labeled block of statements in a process. It has no error handlers: write the job so that it does not fail. If it fails anyway, the error is handled at process level, in the recover region, which can run the job again, continue after it, or abort the process.

Syntax pattern

A job usually has a label and belong to a process. It starts with label: job and ends with done label;. If a job do not have label, it can't be referred. If code fail, we do not know what job has failed and we can't handle the error properly:


** job description or notes
[job_name:] job
  ** local declarations
  ...
do
  ** job statements
  ...
  stop if condition; ** leave the job without error
  ...
  raise "message" if condition; ** fail the job: go to recover
  ...
done [job_name];

Notes:

  • Jobs are used only in a process (driver or aspect), at the top level. Jobs can't be nested; functions and methods do not have jobs.
  • The label is mandatory, and the closing done label; must repeat it. There is no implicit job name and no done job.
  • A job passes when it reaches done or stop. It fails when an error is raised inside it, including errors from the methods it calls. The job status is recorded automatically: "none" for a job that was never executed, "pass" or "fail".
  • The status belongs to the process, in the map jobs: jobs["name"].status, jobs["name"].error and jobs["name"].line. The recover region reads $error.job (a string) to know which job failed.
  • Nothing is reported automatically. The process reads the job states and writes its own report in the finalize region.
  • When a job fails, the process jumps to its recover region: see Recover from exception.

Conditional If

Keyword "if" establish a conditional statement. Also it can be used in expressions. A conditional statement can be a simple statement or a block statement. You can also use "if" after a simple statement or before a block statement.

Code pattern

This is syntax for a simple statement with conditional execution. It enable or prevent a statement to be executed.

# conditioned statement
statement if boolean_variable;
statement if boolean_expression;

Example

Next examples show how we can use conditional to print something when expression is fulfilled.

# conditional print
print "a is even" if (a % 2 == 0);

print "a is odd"  if (a % 2 <> 0);

Note: The "if" is restricted for some statements. It can't be used after expect or done. A loop ends early with break if condition;. We will explain syntax for each block statement.

Decision block

In next pattern we use "if/else" keywords to create a decision branch/fork.

decision

decision diagram

# dual path decision
if expression do
  ** true branch
  ...
else
  ** false branch
  ...
done; ** you can't use if after done

Notes:

  1. The if block does not have a name;
  2. The if block does not open a scope: a variable created in a branch belongs to the parent scope;
  3. The if block is closed with "done;";
  4. The else branch is optional;

Ladder

A ladder is a conditional multi-path selector based on exclusive conditions. Only one branch is executed. If no condition is satisfied the alternative branch is executed. Alternative branch is optional.

ladder

decision ladder

Example:

#selection ladder
if condition_1 do
  ** first branch
  ...
else if condition_2 do
  ** second branch
  ...
else if condition_3 do
  ** third branch
  ...
[else]
  ** alternative branch
  ...
done; ** you can't use if after done

Notes: Previous pattern show a complex situation having several conditions in cascade. Cases should be exclusive and most probable case should be evaluated first. Last block, after else is optional. Like the if block, the ladder does not open a scope.

Match Selector

This is a multi-path selector similar to switch also named: jump table. It has two variants: "all" or "one". That is an optional keyword. If used, "all" will evaluate all "when" branches it may be matching multiple cases. If "one" is used (this is the default), only first case that has a matching value will be executed and the rest are ignored, even if a second match exist. The second match should not be present though and is probably a mistake!

match

Match Diagram

Pattern:


# Define a selector variable
class Type = {V1, V2, V3, V4} <: Ordinal;
...
process main is
  new s := V1;

  ** create match selector
  match s [one | all]
    ** declare locals
    ...
  when V1 do
    ** first path
    ...
  when (V2, V3) do
    ** second path
    ...
  when other do
    ** no other path matched: V4 found
    ...
  done; ** you can't use if after done
  ** whatever path was taken, the next statement runs after the match
  ...
return;

Notes:

  • options: all or one are exclusive (one) is the default;
  • the default block (last) is when other: it is executed only if no when matched;
  • there is no then clause: write the statements after done;, in the scope of the process or job; they run for any path, including other;
  • a when can test a value, several values or a range;
  • match statement can have a label. A labeled match closes with done label;, otherwise with done;;
  • match statement has a local scope (before the first when).

Example with ranges:

#match with ranges
process main is
  new n := 7;
  match n
    when (1..5) do
      print "low";
    when (6..10) do
      print "high";
    when other do
      print "out of range";
  done;
return;

The compiler will generate a warning message if not all cases are covered when you do not create the default block. It may be an error but not necessarily. Is better to have information to prevent wrong doing.

While Loop

Execute a block of code as long as one condition is true. When the condition become false the repetitive block ends. If the condition is false the first time, only the "else" block is executed. If the condition was true at least once, the else block is not executed. The statements after done; run every time the loop ends, also after a break. A loop that must run at least once, or that runs until break, is the Repeat Loop.

while-loop

while loop

Syntax:

#conditional loop
[label:] loop
  ** declare control variables (optional header)
  ...
while condition do
  ** go back to the condition: skip the rest of the cycle
  skip if condition;
  ...
  ** early interruption
  break if condition;
  ...
else
  ** alternative path
  ...
done [label]; ** you can't use "if" here
** the next statement runs every time the loop ends, also after break

Example:


# Visit the members of a list using an unlabeled loop
primary aspect while_demo is
  ** create a shared list
  set letters = ("a","b","c","d","e");
  process main is
    ** i is declared in the process: it is visible after the loop
    new i = 1 :Integer;
    while i <= letters.length() do
      ** e is created again at every iteration
      new e = letters[i] :String;
      let i += 1;
      if e >= "c" do
        write e;
        write "," if i <= letters.length();
      done;
    else
      panic; ** the list is empty
    done;
    ** flush the write buffer
    print;
    print ("iterations", i - 1, separator: " ");
  return;
end while_demo;

Output:

c,d,e
iterations 5

Notes:

  • The loop header is optional. It holds the label and the declarations of the loop. These variables live in the loop scope, survive all iterations and are available in the body and in else, not after done. Without a header, the loop body has no private scope: it uses the parent scope;
  • The do keyword starts the executable region. A variable created there with new is created on the stack at every iteration. Declare in the header what must survive the cycles, and in the process what must be read after the loop;
  • skip [label] [if condition]; ends the current cycle early and goes back to the condition. It works in every loop: see Repeat Loop and For Loop;
  • Some other languages use {break & continue}, we use {break & skip};
  • while True do is accepted without a warning and its else block never runs, but a loop without a condition is written loop do … repeat; (Repeat Loop);
  • A while loop is closed with done: done label; for a labeled loop, done; otherwise;
  • There is no then clause: the statements after done; run when the loop ends, also after break. stop is used only for jobs and break only for loops.

Repeat Loop

The repeat loop tests its condition at the end, so its body runs at least once. It starts with loop, may declare variables in its header, runs the body after do, and ends with repeat. repeat while condition; starts another cycle while the condition is true. A bare repeat; always starts another cycle: the loop ends only with break. This is the infinite loop of Eve.

Syntax:

#loop tested at the end
[label:] loop
  ** declare control variables (optional header)
  ...
do
  ...
  ** go to the test at the end: the condition is still verified
  skip if condition;
  ...
  ** early interruption
  break if condition;
  ...
repeat [label] [while condition];

Example:

#test at the end
process main is
  new n = 0 :Integer;
  loop do
    let n += 1;
    write n;
    write "," if n < 3;
  repeat while n < 3;
  print;
return;

Output:

1,2,3

A skip in a repeat loop goes to the test at the end. The condition is verified after every cycle, also a skipped one:

#skip the even numbers
process main is
  loop
    new i = 0 :Integer;
  do
    let i += 1;
    skip if i % 2 == 0;
    write i;
    write "," if i < 5;
  repeat while i < 6;
  print;
return;

Output:

1,3,5

A loop without a condition ends with break:

#loop until break
process main is
  new line = "" :String;
  loop do
    read(@line, "command:");
    break if line == "quit";
    skip if line == "";
    print ("you said", line, separator: " ");
  repeat;
return;

Notes:

  • repeat closes the loop. It is not a statement inside the body;
  • The body always runs once, so a repeat loop has no else. It has no then clause, as no loop has one: the code after repeat runs when the loop ends;
  • The header variables live in the loop scope, survive all the cycles and are visible in the condition after repeat while;
  • skip goes to the test at the end, so the condition is verified also after a skipped cycle. With a bare repeat;, skip starts the next cycle;
  • break leaves the loop at once, without the test.

For Loop

This statement use one control variables to visit elements in a collection or domain. First use-case of this is to iterate over a range of numbers. In Eve the range is represented as (min..max).

for-loop

for ... loop

Pattern:

#for loop syntax
[label:] for var in (min..max) do
    ** block statements;
    ...
    ** go to the next element
    skip if condition;
    ...
    ** early interruption
    break if condition;
done [label]; ** you can't use "if" here
** what happens after the loop

Notes:

  • Control variable is auto-incremented at end of each iteration;
  • Control variable is declared in the loop header. If the loop has no header, the for statement creates an implicit scope for it; if the header exists, for uses that scope and creates none;
  • skip [label] [if condition]; ends the current cycle and advances the control variable to the next element. It is the continue of other languages;
  • Variables created by new in the do region are created again at every cycle. Declare in the header what must survive, for example a counter;
  • Control variable is local to the loop: it is not available after done;
  • break leaves the loop (or the outer loop, when a label is used) and the statements after done; are still executed;
  • Inside a loop that is inside a job, stop ends the job and break ends only the loop.

Example:

Range iteration using step ratio 2:

#example of range iteration
for i in (1..10)(2) do
    ** write only odd numbers
    write i;
    write ',' if (i < 9);
done;

print;

Output:

1,3,5,7,9

Same loop without a header: the for statement creates the scope of i, which ends at done:

#anonymous scope
for i in (1..5) do
    write i;
    write "," if (i < 5);
done;
print;

Output:

1,2,3,4,5

Skip an element: skip ends the cycle and the control variable takes the next value:

#skip an element
for i in (1..6) do
    skip if i % 3 == 0;
    write i;
    write "," if i < 5;
done;
print;

Output:

1,2,4,5

Block summary

Block Opener Middle keywords Closer Label
jobname: jobdodone name;mandatory
ifif c doelse if c do
else
done;no
match[label:] match swhen v do
when other do
done [label];optional
while[label:] loopwhile c do
skip [if c]
break [if c]
else
done [label];optional
repeat[label:] loopdo
skip [if c]
break [if c]
repeat [label] [while c];optional
for[label:] loopfor x in r do
skip [if c]
break [if c]
done [label];optional
parallel[name:] paralleldodone [name];optional

Closers: blocks end with done, except the repeat loop, which ends with repeat [label] [while c];. A block that has a label ends with done label;; a block without a label ends with done;. There is no done if, done while, done loop or done job, and if cannot follow done. Eve is not regular for its own sake: subprograms end with return, classes and modules with end, and a loop tested at the end with repeat.

Interruptions: skip ends the current cycle of a loop and goes to its continuation point (the next element of a for, the condition of a while, the test of a repeat), break leaves a loop, stop ends a job without error.


Read next: Strings