Table of Contents



Control Flow

Control flow decides which statements are evaluated, how branches are selected, and how loops are repeated or stopped.

Expr control-flow constructs are expressions. They can be used as statements, and several of them can also produce a value.


Control-Flow Keywords

Expr supports these control-flow keywords:

The condition of `if`, `while`, `for`, and `loop` is written inside parentheses.

if(condition)
{
    // body
};

A control-flow construct normally ends with a semicolon when it is used as a statement.


Blocks

A block is a list of statements inside braces.

{
    a = 10;
    b = 20;
    a + b;
};

A block evaluates its statements in order. The block result is the result of the last evaluated statement, unless execution is interrupted by `return`, `break`, or `continue`.

Blocks are commonly used as the body of `if`, `loop`, `while`, and `for`.

A single statement can be used without braces, but braces are recommended for scripts that may later be edited.

if(a > 0)
    sign = 'positive';

If and Else

`if` evaluates a condition and runs a branch only when that condition is true.

if(value > 0)
{
    print('positive');
};

The condition must be inside parentheses.

Use `else` to provide a branch for the false case.

if(value >= limit)
{
    state = 'high';
}
else
{
    state = 'normal';
};

Without an `else` branch, an `if` whose condition is false produces `none()`.

result = if(false)
{
    123;
};
 
result.is_none();      // true

Else If

Expr does not have a separate `elseif` keyword.

An `else if` chain is an `else` branch that contains another `if`.

probeError = 0.03;
 
status = if(probeError <= 0.01)
{
    'excellent';
}
else if(probeError <= 0.05)
{
    'acceptable';
}
else if(probeError <= 0.10)
{
    'warning';
}
else
{
    'failed';
};

The conditions are tested from top to bottom. The first true branch is evaluated, and the remaining branches are skipped.

`else` binds to the nearest unmatched `if`.

if(a)
    if(b)
        print('a and b');
    else
        print('a and not b');

In this example, the `else` belongs to `if(b)`, not to `if(a)`.

Use braces when nested branches could be ambiguous.


If as a Value

Because `if` is an expression, it can be assigned to a variable.

message = if(toolLoaded)
{
    'Tool loaded';
}
else
{
    'No tool';
};

This is useful for selecting one value from several branches.

When an `if` is used where a value is required, provide an `else` branch unless `none()` is an acceptable result.

feed = if(material == 'aluminum')
{
    900;
}
else
{
    600;
};

Loop

`loop` repeats its body a fixed number of times.

count = 0;
 
loop(5)
{
    count += 1;
};

The count expression is evaluated once before the loop starts.

repeats = 3;
 
loop(repeats)
{
    print('step');
};

The count is converted to an integer count. A zero or negative count runs zero iterations.

loop(0)
{
    print('not printed');
};

`loop` is best when the number of repetitions is already known.


While

`while` repeats its body while its condition is true.

a = 1;
 
while(a < 10)
{
    a *= 2;
};

The condition is evaluated before each iteration. If it is false before the first iteration, the body is not evaluated.

ready = false;
 
while(ready)
{
    print('not printed');
};

A `while` loop must change something that can eventually make the condition false. Expr has safety limits for excessive loop execution, but scripts should still be written so loops finish normally.


For

`for` is a loop with initialization, condition, and iteration expressions in the header.

for(initialization; condition; iteration)
{
    // body
};

The execution order is:

  1. The initialization expression is evaluated once.
  2. The condition is evaluated before each iteration.
  3. If the condition is false, the loop stops.
  4. The body is evaluated.
  5. The iteration expression is evaluated.
  6. Execution returns to the condition check.

Example:

sum = 0;
 
for(i = 0; i < 5; i += 1)
{
    sum += i;
};

After this script, `sum` is `10`.

Any `for` header expression can be omitted.

for(; i < 10; )
{
    i += 1;
};

If the condition is omitted, the loop condition is treated as true.

for(;;)
{
    break;
};

Use `break` or another terminating condition when writing a loop without a condition.


Break

`break` exits the nearest enclosing loop.

count = 0;
 
while(true)
{
    count += 1;
 
    if(count >= 3)
    {
        break;
    };
};

After this script, `count` is `3`.

`break` is valid only inside `loop`, `while`, or `for` bodies.

In nested loops, `break` exits only the innermost loop.

outer = 0;
 
loop(3)
{
    loop(3)
    {
        break;
    };
 
    outer += 1;
};

The inner `break` does not exit the outer `loop`.


Continue

`continue` skips the rest of the current loop body and moves to the next iteration of the nearest enclosing loop.

sum = 0;
 
for(i = 0; i < 5; i += 1)
{
    if(i == 2)
    {
        continue;
    };
 
    sum += i;
};

After this script, `sum` is `8` because `2` is skipped.

`continue` is valid only inside `loop`, `while`, or `for` bodies.

In a `for` loop, the iteration expression still runs after `continue`.

for(i = 0; i < 5; i += 1)
{
    if(i < 3)
    {
        continue;
    };
 
    print(i);
};

The `i += 1` iteration expression still runs after each `continue`.


Return

`return` stops the current evaluation scope and optionally provides a value.

function Clamp01(value)
{
    if(value < 0)
    {
        return 0;
    };
 
    if(value > 1)
    {
        return 1;
    };
 
    return value;
}

`return` can be written with or without a value.

return;
return 123;

A `return` with a value returns that value. A `return` without a value returns no explicit value.

At top-level script evaluation, `return` stops evaluation of following statements.

a = 1;
return a;
a = 2;       // not evaluated

Loop Results

Loops can produce a result. The result is normally the last value produced by the body.

last = loop(3)
{
    value += 1;
    value;
};

If a loop body is not evaluated, the loop result is `none()`.

result = loop(0)
{
    123;
};
 
result.is_none();      // true

When a loop is used mainly for side effects, assign the values you need explicitly instead of relying on the loop result.


Truthiness

Control-flow conditions use Expr truthiness rules.

Common cases:

Examples:

if(0)
{
    print('not printed');
};
 
if('text')
{
    print('printed');
};

For type-specific rules, see Type system.


Common Pitfalls

Missing Else When a Value Is Required

An `if` without an `else` can produce `none()`.

value = 10 + if(false)
{
    5;
};

Use an `else` branch when the surrounding expression needs a number, string, or object.

value = 10 + if(false)
{
    5;
}
else
{
    0;
};

Infinite Loop Conditions

This loop does not change `position`, so it cannot finish normally:

position = 0;
 
while(position < 10)
{
    print(position);
};

Update the condition variable, use `break`, or choose `loop` when the number of repetitions is fixed.

Ambiguous Nested If

Without braces, `else` binds to the nearest unmatched `if`.

if(a)
    if(b)
        print('both');
else
    print('which if?');

Use braces to make the intended branch explicit.

Previous: Expression rules

Next: Functions