Sage-Code Laboratory
index<--

Bee Control Flow

Control flow statements are used to alter the linear program workflow. In Bee each control statement start with a different keyword, and ends with one of: done or cycle keywords.

Bookmarks

These are all block statements in Bee language:

Name Description
do block statement
with qualifier suppression block
if conditional execution
cycle repetitive unconditional block
given repetitive block with local scope
match multi-path value selector
trial exception handler block

do

Keyword "do" is used to start a block of code. All composite statements are using this keyword at least once. Related to this keyword we have the keyword: "loop" that enable you repeat execution of a block.

Pattern 1: anonymous block

Next syntax pattern show the anonynous "do" block.

# anonymous block
do
  ** block statements
  ...
done; 

Pattern 2: named block

Next syntax pattern show how to use a label to create a named block.

#namming a block
block_name:
   ** local variables
   make local_variable := value;
   ...
do
   ** statements
   ...
done;   

Note: The label start at beginning of a line and ends with column ":". It is followed by a region of declarations then a keyword that start a block. Usually: {do, if} but also {trial, given} are blocks that can be named.

with

Start a qualifier suppression block. This is a block that eliminate the need for qualifier into a local scope. You can use "with" for any other block statement but most common it apply to "do" block. This kind of block can be also labeled.

Example:

For this example we create a secondary module:

#define test_module
rule .test(x:0) => (r: 0):
   alter r := x + 1;
return;

We load a secondary module and use a qualifier for it:

#using qualifier suppressor
load qualifier:lib_folder.test_module;

with qualifier do:
   ** call external routine .test
   print test(1); //2
   print test(2); //3
done;   

if

Using "if" keyword we can create a condition to start a block. When the condition is false the block statement is ignored and next statement is executed. This statement is sometimes called conditional branch or decision statement.

Pattern 1: one-way conditional branch

Using "if" keyword we can start a conditional branch:

# decision with one branch
label: 
  ** local declarations
  ...
if condition do
  ** statements
  ...
done;

Example:

In this example we use a label named "local" to create a named conditional branch.

# local scope
local:
    make b := 2;
    make a := random();
if a ≠ 0 do
    print b/a;
done;

Pattern 2: two-ways conditional

By using "else" keyword we can split the conditional branch into two branches:

decision

decision diagram

# decision with two branches
local:
  ** local declarations
  ...
if condition do
  ** true branch
  ...
else
  ** false branch
  ...
done;

Pattern 3: nested multi-path conditional

You can use multiple conditions to create nested decisions:

#nested decisions
local:
    ** control variable
    make a :=  random(); 
if a ≤ 0 do
    write "a = ";
    read  a;
    if a = 0 do
        print "a = 0";
    else
        print "a < 0";
    done; //a ≤ 0
done;

Pattern 3: Decision ladder

By using keyword "else" and conditional "if" you can create a cascade of decisions called "decision ladder" that has several branches plus one optional branch that is executed when no other "if" condition is fulfilled.

decision

decision ladder

Example:

#decision ladder
local:
    make a ∈ Z;   //control variable
do    
    read("a = ", a);   
    if a = 0 do            // first decision  
        print "a = 0";
    else if a > 0 do    // second decision
        print "a > 0";
    else if a < 0 do    // third decision
        print "a < 1";  
    else
        print "unexpected:" + a;// unexpected value
    done; 
done;

cycle

You can use "cycle" to create a repetitive block. This keyword is used at the end of block "do" to repeat all statements in the block until a condition ends the cycle. If no condition exists, the cycle is infinite.

Pattern 1: Infinite cycle

Next example show a repetitive "do" block that runs forever. It is not very practical but will be terminated when reaches maxim limit of iteration establish by $iteration variable.

cycle

infinite cycle

#infinite cycle
do
  ** repetitive block
  ...
cycle;

Pattern 2: run condition

Next block will execute multiple times as long as run condition is fulfilled.

run cycle

run-condition cycle

#conditional run cycle 
label:
  ** define local variables
  ...  
do
  ** repetitive block
  ...
  loop if condition;
  ...
  stop if condition;
  ...
cycle if run_condition;

Pattern 3: start condition

Keyword "if" can be used to evaluate a condition and start a repetitive block. The number of repetitions is established by the start condition. If the start condition is not fulfilled the else block is executed. When "else" block is ending the cycle normally stops unless "loop" is used to revive the cycle.

start cycle

start-condition cycle

#conditional start cycle
label:
  ** define local variables
  ...  
if start_condition do
    ** first repetitive block
    ...
    stop if condition; //intrerupt the cycle
    ...    
    loop if condition; //shortcut the cycle
    ...            
else
    ** second repetitive block
    ...    
    loop if restart_condition; //revive the cycle
cycle;

Note 1: The else block runs usually one single time, that is when the condidtion evaluate to false. Otherwise if we repeat the cycle for "else" block we can end-up with infinite loop. You can revive the cycle from the else block by using "loop" statement but you need to be chareful to avoid the infinite loop.

Note 2: You can have only one condition/cycle. For patern 2 the condition is at the end, for patern 3: the condition is at the beginning. You can not have both the beginninc and the end condition on a sincle cycle. The compiler will detect this and complain about it.

Pattern 3: nested cycles

Next syntax pattern show how to use a nested cycles with labels.

# nested blocks
42:
    ** local variables
    make local_variable := value;
    ...
do
    ...
    10: do
        ...
        loop 42 if condition; //restart block 42
        ...
        stop 42 if condition; //exit block 42
        ...
    cycle if condition;
    ...
cycle;
...

Notes:

given

You can start an iterative cycke by using keyword "given". In other languages you may be using "for" and "foreach". Using "given" we are making a limited cycle. We generate a control variable from a range or domain or visit elements of a collection.

given

given cycle

#iterative cycle
given cv <- (min..max:rate) do
  ** first block
  ...  
  next if condition; //fast forward
  ** second block
  ...  
  stop if condition; //early transfer
  ** third block
  ...
cycle;

Notes:

Example 1:

#range iteration with rate 1
given i <- (1..10) do
  if i % 2 = 0 do
    next;    //rerun fron the beginning
  else
    write i; //odd numbers
  done;
  write ',' if (i < 10);
cycle;
1,3,5,7,9

Example 2:

Ratio: Using domain ratio the example above can be simplified:

#range iteration with rate 2
given i <- (1..9:2) do
  write(i); //odd numbers
  write(',') if (i < 9);
cycle;
print;

Notes:

match

This is a multi-path selector similar also named: jump table. A jump table in Bee has two variants: "all" or "one". That is optional keyword. If used, ALL will evaluate all "when" values it may be matching multiple when blocks. If "one" is used (this is the default), only first block 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:

#match selector
label: 
    ** prepare variables
    ...
match x [all | one]
    when v1 do
      ** first path
    when v2 do
      ** second path
    when (v1, v2, v3) do
      ** other path
    none
      ** default path
done;

Example:

Using cycle with nested match selector:

#local scope demo with nested cycle and check
local:
    ** local scope
    make a ∈ N; 
given b <- (1..10) do
    ** repetitive scope
    alter a := random(0..20);
    print " a = " + a;
    match a one
        when 0 do
            print ("a = 0");
        when (1,3,5,7,9) do
            print "a is odd";
        when (2,4,6,8) do
            print "a is even";
        other
            print ("random: a = " + a);
    done;
cycle;

trial

The trial is by far the most complex statement of Bee language. A trial block is used to handle a process that can have exceptions. It has a default block and optional can include multiple case regions. Each case can resolve one error or several errors.

trial

Trial Diagram

Notes:

Keywords:

Next keywords are used to create a full trial block:

worddescription
trialstart a process that may fail
case catch errors with specific code or code range
othercatch all other errors not fixed by a patch
finalfinalization region, executed before trial end

Pattern:

#complex trial with many cases
local:
    ** local variables 
    ....
trial
    ** preconditions: early termination without errors
    abort if condition;
       ...
    ** initial or default statements
       ...
    ** check a condition and continue or fail    
    pass if condition;
        ...
    ** optional create error    
    fail {code, message} if condition; 
    ...
case @error.code = code do
    ** handler1
    ...
    abort; //stop the trial
case @error.code ∈ (code1,code2) do
    ** handler2
    ...
    retry; //repeat trial
other
    ** cover all other errors
    ...
    raise; //propagate last error
final
    ** finalization statement (executed before leaving)
    print "final error:" + @error.code if @error.code > 0;
done;

Note:

Errors

Errors can be defined in your program using next notation:

** define error
make error_name := {code,"message"} ∈ Error;

Errors can be issued using "fail" or "pass" keywords.

** "raise" can create a customized error or message
fail error_name  if condition //create an error that is predefined
fail "message"   if condition //create instant user error code: 200
fail {code,"message"} //create instant custome error with code

Note: The standard module will define standard error objects as system constants. Code 100 is used for default $user_error, created by pass and fail keywords.

case

This region can catch an error and decide what to do next. Error can be catched by code or a list of codes. For each cases you can handle the errors using keywords: "abort","retry" or "raise". If a case is not using any of these keywords, the next case is executed. If no case resolve the error, this error autopropagate.

Transfer Statements

Next statements are transfer statements used in trial block:

word description
abort early stop the trial and transfer execution to final block
fail transfer execution to case blocks to resolve an error
pass continue trial or fail and transfer execution to trial cases
raise stop the trial, skip all other cases and continue with the final block
retry repeat the trial, equivalent to "loop" but usable only in trial blocks

other

This region is used for any other error that is not handled by case handlers. You can use any selector in this region to find an exceptions by code but you can also just report the error or log the error and stop the trial.

final

This region is executed just before trial is done, regardless of error status. Even if there is no error to propagate this region is still executed. This region is mandatory execute even if the trial is aborted.

It can contain:

Example:

#simple trial block
local:
    make  x ∈ Q;
    make  y ∈ Q;
trial
    alter x := 1/0;
final
    print "x = " + x;
    print @error.message if @error.code ≠ 0;
done;

fail:

In this example we use "fail" statement to create an error on purpose:

#define a custom error
local:
    make my_error := {200, "my error"} ∈ E;
trial
    fail my_error; //issue user error
case @error.code == 200 do
    ** we catch the the error and do something
    print @error.message;
    print @error.line;
other
    raise; // propagate all errors
done;

Notes:

retry:

By using "retry" you can repeat a trial several times until other ending conditions are encounter: {abort, pass, fail}. You can issue "retry" from trial block using a condition or from case blocks for specific errors.

#example of repeating trial
local:
    make count ∈ (1..3);
    make a ∈ (0..9);
** try maximum 3 times
trial
    alter count += 1;
    read a;
    if  a < 0 do //trigger patch     
      fail;
    else if a < 9 do
      write "correct";    
    else if  a > 9 do    
      write "incorrect";
    else
      pass;
    done;      
case $out_of_range do
    if count < 3 do
      write "wrong try again:"
      retry; //try again the entire trial
    else
      write "wrong 3 times!";      
      fail $out_of_range;
    done;
final
    print;
done;

Note:


Read next: Rules