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 |
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.
Next syntax pattern show the anonynous "do" block.
# anonymous block
do
** block statements
...
done;
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.
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.
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;
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.
Using "if" keyword we can start a conditional branch:
# decision with one branch
label:
** local declarations
...
if condition do
** statements
...
done;
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;
By using "else" keyword we can split the conditional branch into two branches:
decision diagram
# decision with two branches
local:
** local declarations
...
if condition do
** true branch
...
else
** false branch
...
done;
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;
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 ladder
#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;
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.
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.
infinite cycle
#infinite cycle
do
** repetitive block
...
cycle;
Next block will execute multiple times as long as run condition is fulfilled.
run-condition cycle
#conditional run cycle
label:
** define local variables
...
do
** repetitive block
...
loop if condition;
...
stop if condition;
...
cycle if run_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-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.
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;
...
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 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;
#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
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;
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 Diagram
#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;
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;
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 Diagram
Keywords:
Next keywords are used to create a full trial block:
word | description |
---|---|
trial | start a process that may fail |
case | catch errors with specific code or code range |
other | catch all other errors not fixed by a patch |
final | finalization region, executed before trial end |
#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;
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.
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.
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 |
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.
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:
#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;
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;
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;
Read next: Rules