Azoth has three loop constructs. They are "loop", "while" and "foreach". All loop bodies must
be block statements.
loop_expression
: simple_loop_expression
| while_expression
| foreach_expression
;
The simplest kind of loop is an infinite loop. Infinite loops are created with the "loop" keyword.
simple_loop_expression
: loop_label? "loop" block
;
Example:
loop
{
console.write_line("Loop Forever");
}
The condition expression of a while loop is evaluated to a bool? using the true operator. The
body is executed as long as the condition evaluates to true.
while_expression
: loop_label? "while" expression block
;
Example:
while condition
{
// do work
}
You might be tempted to write an infinite loop as "while true" however "loop" is better for this
because the compiler treats them differently for definite assignment. Namely, a variable can be
definitely assigned in a "loop" but will not be considered definitely assigned by a "while true"
loop.
If you need something like a "do {} while" loop available in many other languages, the proper way
to do that is:
loop
{
// do work
if not condition => break;
}
Foreach loops allow something to be done a set number of times or for a set of values.
foreach_expression
: loop_label? "foreach" "var"? pattern "in" embedded_expression block
;
Example:
foreach x in 1..10
{
console.write_line("{x}");
}
The foreach loop can operate over any value that implements the foreach operator producing a
value that implements the next operator or any value that implements the next operator.
Sometimes the actual value of the loop variable isn't needed. This can be denoted using the underscore.
foreach _ in 1..10
{
console.write_line("Hi");
}
You can use break and next expressions to control loop iteration. Both "break" and "next"
expressions have the type "never".
break_expression
: "break" loop_label? expression?
;
next_expression
: "next" loop_label?
;
The "break" expression can have a value. This value then becomes the value of the loop expression.
All break expressions must have values of compatible types. The type of a "loop" expression is the
type of the values.
Example:
let x = foreach x in items
{
do_something();
if condition
{
break get_value();
}
} ?? alternate_value;
The "while" and "foreach" loop return an optional value to indicate the loop body never
executed. In that situation, the value of the loop expression is "none". This allows you to use
the coalesce operator to easily specify a value for that case.
let x = foreach y in items
{
if condition
{
break 234;
}
} ?? foo;
When using break or next it may be necessary to indicate which loop you are controlling. This can be done by labeling the loop.
loop_label
: identifier ":"
;
Example:
outer: foreach x in 0..10
{
inner: foreach y in 0..10
{
if x % 2 == 0 => next outer;
if y % 2 == 0 => next inner;
console.write_line("x = {x}, y = {y}");
}
}
When loop labels are combined with break values, the loop label is placed before the value as break label value;. If a break expression could be interpreted either as a break with a loop label or a
break with a value, it is taken to be a break with a loop label. If this is not the intended
meaning, a compiler error will likely result from the incorrect loop expression type. It can be
fixed by renaming the loop label or local variable(s) to avoid ambiguity.