Statements

Grammar§

Statement ::= AssertStatement | BlockStatement | BreakOnStatement | BreakStatement | ContinueOnStatement | ContinueStatement | ExpressionStatement | ForeachStatement | GotoStatement | IfElseStatement | ReturnStatement | StaticAssertDeclaration | SwitchStatement | VersionBlockStatement | WhileStatement | WithStatement ; DeclarationOrStatement ::= Declaration | Statement ;

AssertStatement§

Grammar§

StaticAssertDeclaration ::= "static" AssertStatement AssertStatement ::= "assert" "(" Expression ( "," Expression)? ")" ";"

Semantics§

The first expression must be evaluable as a condition. The second expression, when specified, must give a null terminated UTF8 string. When not specified it is automatically set to a StringExpression the represents the source code of the first expression.

When the condition is not verified a message is displayed then either the compilations stops if the assertion is statically evaluated and otherwise the program crash.

assert(echo(is, s8, s32)); // error: assert failure ...

Assertions are enabled with the compile switch --check=asserts

static limitation§

static evaluation of assert is limited to

Unreachable§

assert(0) and assert(false) are used to mark the current block as unreachable. Unless specified, assertions that denote unreachability dont output a message.

AtLabel§

Grammar§

AtLabel ::= "@" Identifier

Semantics§

Specify the GotoStatement target or the optional BreakStatement and ContinueStatement target scope.

BlockStatement§

Grammar§

BlockStatement ::= "{" DeclarationOrStatement* "}"

Semantics§

A block introduces a sub scope.

var u64 a;
{
    var u32 a;
    a = 42;     // operate on the u32 one
}
assert(a == 0); // operate on the u64 one

In this scope forward use of declarations is not allowed.

BreakOnStatement§

Grammar§

BreakOnStatement ::= "break" "on" ";"

Semantics§

Explicitly exit the current SwitchStatement.

BreakStatement§

Grammar§

BreakStatement ::= "break" ((AtLabel ("," Expression)?) | Expression)? ";"

Semantics§

If specified, evaluate the expression. If the label is not specified then break the current loop. If the label is specified then break the loop in which the label is declared.

struct F
{
    var s32 m;
}

var F*[][] items;

foreach (var F[] row) in items do
{
    label A;
    foreach (var F f) in row do
    {
        if f.m == 8 do
            break;      // continue to next row
        if f.m == 12 do
            break @A;    // break the foreach that declares row
    }
}

ContinueOnStatement§

Grammar§

ContinueOnStatement ::= "continue" "on" Expression? ";"

Semantics§

The ContinueOnStatement must be located in the body of a OnMatchStatement. When the optional expression is not specified then the statement has for effect to jump in the body of the next, lexicographical, OnMatchStatement.

var s32 a;
switch a do
{
    on 0 do { a+=32; continue on;   } // set a and go to next match
    on 1 do {/*a is either 1 or 32*/}
    else do {                       }
}

If the expression is not specified then it is an error to position the statement in the last match body.

var s32 a;
switch a do
{
    on 0 do { a+=32; continue on;   } // set a and go to next match
    on 1 do { continue on;          } // error, cannot find target...
    else do {                       }
}

It is an error not to position the statement at the end of the body

var s32 a;
switch a do
{
    on 0 do { continue on; a+=32;   } // error, stmt not reachable...
    on 1 do {                       }
    else do {                       }
}

If the expression is specified then it must match to an existing match and the statement has for effect to jump in the body of this match.

var s32 a;
switch a do
{
    on 0 do { a+=32; continue on 1;    } // set a and go to match for 1
    on 1 do { /*a is either 1 or 32 */ }
    else do {                          }
}

ContinueStatement§

Grammar§

ContinueStatement ::= "continue" ((AtLabel ("," Expression)?) | Expression)? ";"

Semantics§

If specified, evaluate the expression. If no label is not specified then continue the current loop. If the label is specified then continue the loop in which the label is declared.

struct F
{
    var s32 m;
}

function log(s8* text);

var F*[][] items;

foreach (var F[] row) in items do
{
    label A;
    foreach (var F* f) in row do
    {
        if f.m == 12 do
            continue @A, log("encountered 12..."); // continue to next row
    }
}

ExpressionStatement§

Grammar§

ExpressionStatement ::= Expression ";"

Semantics§

ForeachStatement§

Grammar§

ForeachStatement ::= "foreach" ForEachVariables "in" Expression "do" DeclarationOrStatement ForEachVariables ::= ForeachVariableList | "(" ForeachVariableList ")" ; ForeachVariableList ::= VariableDeclaration ( "," VariableDeclaration )*

Semantics§

iterating over a numeric range§

If the expression is a RangeExpression then the two lower and the upper range bounds must have an integer Type. Their value gives the count of iteration, starting from left to right (exclusive).

A single variable is allowed and it must be implictly convertible to the type of the numeric range.

@foreign function printf(s8* specifier; ...): s32;
foreach const s8 i in 0 .. 8 do
    printf("%d ", i);               // 0 1 2 3 4 5 6 7

iterating over arrays§

When the expression is not a range then it must resolve to a static array, a reference counted array, or a slice.

Two variables are allowed.

The first optional variable, the “counter”, represents the current iteration number. The counter type must be implicitly convertible to the type of the array length.

For TypeSlices and TypeRcArrays this is always usize or ssize.

For TypeStaticArrays this depends on its .length property.

The last variable represents an array element.

The variable type must exactly match to the array element type.

@foreign function printf(s8* specifier;... ): s32;

// over an array literal (like a static array)
foreach const s8* s in ["0", "1", "2" ] do
    printf("%s", s);                                // 0 1 2

// also count
foreach (const s8 counter, const s8* s) in ["0", "1", "2" ] do
    printf("%d-%s ", counter + 10, s);               // 10-0 11-1 12-2

Reversed foreach§

The DotExpression property .reverse is used to iterate from the last element to the first.

@foreign function printf(s8* specifier;... ): s32;

foreach const s8* s in (0 .. 3).reverse do
    printf("%d", s);                            // 2 1 0

foreach (const s8 counter, const s8* s) in ["00", "11", "22" ].reverse do
    printf("%d-%s ", counter, s);               // 2-22 1-11 0-00

GotoStatement§

Grammar§

GotoStatement ::= "goto" AtLabel ( "," Expression)? ";"

Semantics§

If specified, evaluate the expression then jump to the position following the [LabelDeclaration] given by the AtLabel identifier.

function func(var s32 max)
{
    var s32 i;
    label nxt;
    i += 1;
    if i < max do
        goto @nxt;
}

IfElseStatement§

Grammar§

IfElseStatement ::= "if" IfCondition "do" DeclarationOrStatement ("else" "do" DeclarationOrStatement)? IfCondition ::= Expression | VariableDeclaration | "(" VariableDeclaration ")" ;

Semantics§

The condition is either an expression that is evaluable as a condition or a VariableDeclaration that is evaluable as a condition. If the variable is declared then it only exists in the scope of the DeclarationOrStatement that follows the condition.

ReturnStatement§

Grammar§

ReturnStatement ::= "return" Expression? ";"

Semantics§

Exits the current function. The optional expression gives the return value. It must be implictly convertible to the function return type.

If the current function has no return type then return can be omitted, in which case it is implicitly added as last statement of the body.

SwitchStatement§

Grammar§

SwitchStatement ::= "switch" Expression "do" "{" OnMatchStatement+ ("else" "do" DeclarationOrStatement)? "}" OnMatchStatement ::= "on" OnMatchExpressions "do" DeclarationOrStatement OnMatchExpressions ::= OnMatchExpressionList | "(" OnMatchExpressionList ")" ; OnMatchExpressionList ::= Expression ("," Expression)*

Semantics§

The switch statement is comparable to a suite of tests on a constant condition but with restrictions on what can be tested so that the compiler can always generate fast jump tables.

The restriction are the following:

There are two ways to express a match to the condition. They can be mixed but duplicate cases are not allowed.

Individual matches§

When a list of individual values is specified, the matching block is executed if the condition matches exactly to one value of the list.

var s32 a;
switch a do
{
    on 0,1,2 do {}
    on 3,4,5 do {}
}

Matches over ranges§

When a list of range is specified, the matching block is executed if the condition value is within (right value is inclusive) one of the range of the list.

var s32 a;
switch a do
{
    on 0 .. 2 do {}
    on 3 .. 5 do {}
}

Implicit break§

After a match, and if the block for the match does not breaks, continues, neither continues on another case nor returns then the program execution continues passed the switch.

var s32 a = 0;
var s32 b;
switch a do
{
    on 0 .. 2 do b = 0; // implicitly go to L0
    on 3 .. 5 do b = 3; // implicitly go to L0
    else      do b = 6; // implicitly go to L0
}
label L0;
assert(b == 0);

Explicit break§

Although breaks are usuall implicit, early breaks can be made using the BreakOnStatement.

Fallthrough§

Fallthroughs are explicitly performed using ContinueOnStatements.

Else block§

If specified the else block is exectued when none of the OnMatchStatements matches.

Missing and checked else block§

When the else block is not specified and if an uncovered match is reached and if the compiler was passed the --check=switches argument then the program displays a diagnostic before crashing.

For example

var s32 a;
switch a do
{
    on 0 .. 2 do {}
    on 3 .. 5 do {}
}

Is turned, under X86, into

var s32 a;
switch a do
{
    on 0 .. 2 do {}
    on 3 .. 5 do {}
    else      do
    {
        printf("<source loc info>: error, switch match `%llu` is not covered", a);
        fflush(0);
        asm("ud2");
    }
}

VersionBlockStatement§

Grammar§

VersionBlockStatement ::= VersionExpression DeclarationOrStatement ("else" "do" DeclarationOrStatement)?

Semantics§

Same as VersionBlockDeclaration excepted that statements are allowed.

WhileStatement§

Grammar§

WhileStatement ::= "while" Expression "do" DeclarationOrStatement

Semantics§

Evaluate the declarations and statements while the condition given by the expression is true.

WithStatement§

Grammar§

WithStatement ::= "with" WithExpressions "do" DeclarationOrStatement WithExpressions ::= Expression ( "," Expression)* | "(" Expression ( "," Expression)* ")" ;

Semantics§

The with statement is used to introduce one or several resolution symbols. Each expression must either give a class, a struct, an union or a unit.

The expressions are used to resolve IdentExpressions and TypeIdentifiers located in the nested block. Expressions are tried from left to right to perform a “strict” search.

static struct F
{
    var s32 member;
    static struct G
    {
        static var s32 member;
    }
}

var F f;
var s32 member; // never assigned
with f.G, f do
    member = 0; // f.G.member is assigned
with f, f.G do
    member = 0; // f.member is assigned

The first expression that allows to solve an unknown IdentExpression is always selected, even if not semantically correct.

struct S1 { static var s8 a; }
struct S2 { static var s32 a;}

function main()
{
    with S1, S2 do
        a = :u16.max + 1;   // `S1` contains `a` so select it as scope, then...
                            // error, cannot implicitly convert `65535 + 1:u16` of type `u16` to type `s8`
}

If none of the expressions allows to solve an identifier then resolution continues as usual.