Skip to content

Latest commit

 

History

History
178 lines (139 loc) · 3.91 KB

File metadata and controls

178 lines (139 loc) · 3.91 KB

Loop Expressions

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
    ;

loop

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");
}

while

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

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");
}

Controlling Loop Iteration

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?
    ;

break Values

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;

Loop Labels

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.