This chapter describes what a Mica program does: the statements a body is made of, how they follow one another, assignment and calls, the choices if and case, the loops while, repeat, for and for ... in, the names a loop carries and the statements leave and next that address them, deferred statements, and the release of a heap cell with dispose. Each construct names its rule in the grammar, says what it means, and quotes what the compiler answers when a source breaks the rule. The expressions the statements compute are chapter 5, and the routines a call enters are chapter 7. Choices and repetition teaches the choices and the loops from the start, and initializers, and the leave family and defer, end to end the rest.

Every example is a complete program the website’s gate compiles with the compiler as it is written here, and the output shown beneath it is the output the program prints.

Statements and their sequence

A body is a compound statement, begin, a sequence of statements and end (compound-statement), and a compound statement stands wherever one statement may, so a branch or a loop that does several things holds them in begin ... end. The statements of a sequence are separated by semicolons and run one after the other (statement-sequence). A statement may be empty (statement), so a semicolon before end does no harm and begin end does nothing. Where a statement stands, a sequence may also declare a variable, which chapter 3 describes with its scope.

A statement is one of these (action):

StatementWrittenDescribed
an assignmenttotal := total + pricebelow
a callReport(), strings.UpperCase(name)below
a compound statementbegin ... endabove
a choiceif, casebelow, below
a loopwhile, repeat, for, for ... in, each with an optional namebelow
a leave or a nextleave, leave scan when found, next when skipbelow
a deferred statementdefer dispose cellbelow
a disposaldispose cellbelow
a failurefail NotFounderror domains
an emitemit readingchapter 11
a schedule, a concurrent block, a select, a synchronized statementschedule Worker(1)chapter 11
a tape windowtape ... endthe tape

A call, an assignment from a call, a for ... in loop and a select may end with a consumption, on fail and what to do when the call fails, which the failure channel teaches.

Assignment

An assignment stores the value of the expression on the right in the place on the left (assignment-statement). The place is a variable, a field or an element of one, orders[i].price, or what a pointer points to, value counter (assignment-target). The value takes the type of the place as chapter 4 states, and an assignment of a record or an array copies the whole value, so a change to the copy leaves the original as it was: after b := a; b[1] := 9; the element a[1] keeps its value.

WrittenCodeThe compiler’s words
an integer assigned to a string, s := 3;5143incompatible data types in assignment to s: int32 to string
a constant assigned, Limit := 4;5099'Limit' is a constant, whose value is fixed as the program compiles, so it cannot be assigned: declare a variable for a value the program changes
a loop’s control variable assigned in its body, i := i + 1;5147left side of an assignment cannot be a for-statement control variable: 'i'

Calls

A call statement enters a procedure, or a function whose answer the program does not need, by its name and its arguments in parentheses (call-statement). The name may be qualified by the unit it is imported from, strings.UpperCase(name) after imp strings;, as chapter 2 describes. The arguments are evaluated from the left, each before the routine is entered, and a function called as a statement runs and its answer is dropped. The parentheses stand even where there is no argument, and chapter 3 quotes the refusal of a call without them; chapter 7 states what a call hands over.

If

if, a condition, then and a statement runs the statement when the condition holds, and an else with its statement runs that statement when it does not (if-statement). The condition is a bool. An else belongs to the nearest if without one, so in if a = 1 then if b = 1 then ... else ... the else is the inner if’s, and an if behind an else makes a chain, if ... then ... else if ... then ... else ..., whose first road with a holding condition runs. The statement before else ends without a semicolon, since a semicolon would end the whole if.

WrittenCodeThe compiler’s words
a semicolon before else, a := 1; else2027semicolon before else is not allowed
an integer as the condition, if n then5171statement condition requires boolean data type, found int64

while and repeat answer an integer condition with the same 5171.

Case

case, a selector, of, its arms and end runs the arm whose labels hold the selector’s value (case-statement). An arm is its labels, separated by commas, a colon and a statement (case-element), and the arms are separated by semicolons. An else and its statement may follow the last arm, without a semicolon before it, and run when no label holds the value; a semicolon may stand after the else’s statement, before end.

The selector is of an ordinal type, an integer, a character, a bool, an enumeration or a subrange, or a bitset, or of a sum type. A label is one of these (case-label):

LabelWrittenOver
a constant7, 'q', Sat, Truean ordinal selector
a range of constants9..17, 'a'..'m', Mon..Frian ordinal selector
a masked bit pattern0b????_??10an unsigned integer or a bitset, whose ? digits match either bit
a bitset constructor[0, 2]a bitset
a case of a sum typeCircle(*), Dota sum type, its fields unbound

A label is computed while the program compiles, and two labels do not hold one value, so at most one arm takes a value. Masked patterns are the exception: two of them may match one value, the first arm that matches takes it, and a pattern that an earlier one already covers whole draws warning 5284, since it can never take a value. Bit records teaches masked patterns and sum types the cases of a sum type with the fields an arm reads.

A case says what happens to every value of its selector: its labels cover every value, or it has an else. Over an enumeration, a subrange, a character, a bool or a sum type, the compiler counts the values or the cases the labels cover, so 'a'..'z' alone over a unicode is refused; over a whole integer type the labels can rarely cover the domain, and a case over it without an else is refused with 5458.

{
    Choices - an 'if' with its 'else' roads, and 'case' over an integer, an enumeration and a sum type.

    The 'if' chain takes the first road whose condition holds. The first 'case' reads a list, two ranges and an 'else',
    the second covers every day of the week and needs no 'else', and the third names each case of a sum type with '(*)'.
}
program Choices;

imp
    WriteLn : std;

const
    HourLine = "at %lld the day is in its %ls";
    Morning = "morning";
    Afternoon = "afternoon";
    Evening = "evening";
    KindLine = "%lld counts as %ls";
    Night = "night";
    Early = "early hours";
    Working = "working hours";
    Late = "late hours";
    WorkingDay = "Sat is a working day";
    Weekend = "Sat is in the weekend";
    CircleLine = "a circle";
    SquareLine = "a square";
    DotLine = "a dot";
    AfternoonStart = 12;
    EveningStart = 18;

type
    Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
    Shape = (Circle(radius : float64), Square(side : float64), Dot);

var
    hour : int64;
    kind : string;
    today : Day;
    shape : Shape;

begin
    hour := 14;
    if hour < AfternoonStart then
        WriteLn(HourLine, hour, Morning)
    else if hour < EveningStart then
        WriteLn(HourLine, hour, Afternoon)
    else
        WriteLn(HourLine, hour, Evening);

    case hour of
        0..5: kind := Night;
        6, 7, 8: kind := Early;
        9..17: kind := Working
    else
        kind := Late
    end;
    WriteLn(KindLine, hour, kind);

    today := Sat;
    case today of
        Mon..Fri: WriteLn(WorkingDay);
        Sat, Sun: WriteLn(Weekend)
    end;

    shape := Square(2.5);
    case shape of
        Circle(*): WriteLn(CircleLine);
        Square(*): WriteLn(SquareLine);
        Dot: WriteLn(DotLine)
    end;
end.

It prints:

at 14 the day is in its afternoon
14 counts as working hours
Sat is in the weekend
a square
WrittenCodeThe compiler’s words
a case over the three days of a Day that names two and has no else5285the case over 'Day' covers 2 of the 3 values of its domain and has no else: a case must say what happens to every value — cover the domain or add an else
a case over an int64 without an else5458the case over 'int64' has no else and its arms do not provably cover the domain: a case must say what happens to every value — add an else
a case over a unicode whose one arm is 'a'..'z', with no else5285the case over 'unicode' covers 26 of the 1114112 values of its domain and has no else: a case must say what happens to every value — cover the domain or add an else
the label 1 twice, on lines 7 and 85166case statement has duplicate label value 1, first written at line 7, column 9
the ranges 1..5 and 4..9, on lines 7 and 85345case statement label covering 4..9 overlaps an earlier label, first written at line 7, column 9
a variable as a label, b:5162case statement label must be constant at compile time: 'b', a variable
a real as the selector5161case statement selector requires an ordinal or bitset data type, found float64
a string as the selector5161case statement selector requires an ordinal or bitset data type, found string
a masked pattern over an int645279a masked bit pattern cannot label a case over 'int64': the don't-care digits select bits, so the selector must be a bitset or an unsigned integer
an integer as a label over a bitset, 0b0101:5281a case label over the bitset selector 'Flags' must be a constant bitset constructor or a masked bit pattern, found int32
a semicolon before else2027semicolon before else is not allowed

Loops

while, a condition, do and a statement tests the condition before each round and runs the statement while it holds, so a loop whose condition is false at once runs no round (while-statement). repeat, its statements, until and a condition runs the statements and then tests, until the condition holds, so its body runs at least once (repeat-statement). The body between repeat and until is a sequence of its own, with no begin, and holds at least one statement. A variable it declares where a statement stands lives to the end of that sequence, which ends before until, so the condition cannot read it: until k > 3 over a k declared in the body is refused with 5018, identifier not found: k.

The counted for runs its statement once for every value from the first bound to the second, upward with to and downward with downto (for-statement). Its control variable is a variable the routine or the program declares, of an ordinal type: an integer, a character or an enumeration, for d := Mon to Fri do. The bounds are evaluated once, before the first round, so a body that changes a variable in the second bound does not change the number of rounds. A first bound past the second runs no round and leaves the control variable as it was; after a loop that ran, the control variable holds the value of its last round.

While the loop runs, nothing but the loop changes its control variable. The body reads it and never stores into it: no assignment, no address of it, no loop of its own over it, and no select arm or on fail handler that stores a value into it. A routine declared inside the loop’s routine, at any depth, may read the control variable and never assigns it, whether the loop calls that routine or not, and neither the routine nor a routine inside it takes the control variable’s address, except to hand it whole to a const pointer parameter, which only reads through it. Each of these is refused at the loop or at the statement that makes it, and the routine itself may assign the control variable before and after the loop.

for, a variable, in, a container, do and a statement runs the statement once for each element of the container, the variable holding the element. A fixed array, a dynamic array, a vector, a matrix and a span give their elements in index order, a matrix walking its rows one after the other, a string gives its characters, a bitset the positions of its set bits in ascending order, and a generator’s stream the values the generator emits, which chapter 11 describes with the consumption and the on lost section such a loop may carry. The loop walks the container in place, so the container is a variable or a parameter, and the control variable, declared as the counted loop’s is, holds each element and is never assigned. After the loop it holds the last element it was given.

{
    Loops - the three loops, a named loop that 'next' and 'leave' address, and a for-in over an array.

    'while' tests before each round and 'repeat' after it, so the repeat's body runs at least once. The counted 'for'
    reads its bounds once, before the first round, and counts down with 'downto'. The for-in walks the readings in index
    order: 'next scan' skips a negative reading and 'leave scan' ends the loop at the first reading past the limit, and
    the program goes on after the loop.
}
program Loops;

imp
    WriteLn : std;

const
    HalvedLine = "100 halves to 1 in %lld rounds";
    RepeatLine = "the repeat stopped at %lld";
    CountdownLine = "countdown %lld";
    TotalLine = "the readings up to the first past %lld add up to %lld";
    Start = 100;
    Step = 3;
    StepLimit = 10;
    ReadingLimit : int64 = 20;

type
    Readings = array[1..6] of int64;

var
    readings : Readings := (4, 9, -1, 16, 25, 7);
    reading, total, rounds, n : int64;

begin
    n := Start;
    while n > 1 do
    begin
        n := n / 2;
        rounds := rounds + 1;
    end;
    WriteLn(HalvedLine, rounds);

    n := 0;
    repeat
        n := n + Step;
    until n > StepLimit;
    WriteLn(RepeatLine, n);

    for n := 3 downto 1 do
        WriteLn(CountdownLine, n);

    scan : for reading in readings do
    begin
        next scan when reading < 0;
        leave scan when reading > ReadingLimit;
        total := total + reading;
    end;
    WriteLn(TotalLine, ReadingLimit, total);
end.

It prints:

100 halves to 1 in 6 rounds
the repeat stopped at 12
countdown 3
countdown 2
countdown 1
the readings up to the first past 20 add up to 29
WrittenCodeThe compiler’s words
a control variable no section declares, for k := 1 to 3 do5018identifier not found: k
a real control variable, for r := 1.0 to 3.0 do5157for statement control variable requires a supported ordinal data type, found float64
a for-in over a set of Day5257a for-in statement iterates a container — a bitset visits its set-bit positions in ascending order, a string its code points, a fixed array, a vector, a matrix, a dynamic array, or a span its elements in index order (a matrix walking its contiguous row-major block), and a generator's stream the values it emits — but the container expression has type: Days
an int64 walking the bits of a bitset5258a for-in control variable holds raw bit positions, so 'n' must be a uint64: int64
a for-in over a typed constant array, for x in Readings do5490a for-in over 'Row' walks its elements in place, through a borrowed view of the array's own storage, so the container must be an array variable or parameter — give the value a name and iterate that name, or, when the elements come from a call, take the dynamic array the call answers
the element assigned in the body, x := 1;5147left side of an assignment cannot be a for-statement control variable: 'x'
a routine Reset inside the loop’s routine assigning the control variable on line 9, i := 0;5782'Reset' assigns 'i', the index of this loop, at line 9, column 9, and nothing but the loop may change its index while it runs: give 'Reset' a variable of its own for what it stores, or count this loop with a variable no nested routine assigns
the control variable’s address taken ahead of the loop on line 11, cell := address i;5783the address of 'i', the index of this loop, is taken at line 11, column 13, and a write through that pointer could change the index while the loop runs: take the address of another variable, or count this loop with a variable whose address is never taken
a select arm in the body storing into the control variable, on i in numbers do5160for statement control variable 'i' is already protected in the current lexical scope
a repeat with no statement before until2017expected the begin of a statement, found until

Leave and next

A loop may carry a name, written before it with a colon, scan : for ... (labeled-loop). The name belongs to a while, a repeat or a for loop and to nothing else, and it is what leave and next address.

leave with a loop’s name ends that loop, and the statement after the loop runs next. leave without a name ends the routine it stands in, as chapter 7 describes for a function’s answer, in the program’s body it ends the program, which exits with status zero (leave-statement), and in the body of a library or an object unit it ends that body, as deferred statements shows. A bare leave therefore never ends only a loop: a loop that is left early carries a name.

next ends the current round of a loop, which goes on with its next round: a while and a repeat test their condition, and a for moves to its next value (next-statement). next without a name continues the innermost loop around it, and next with a name the loop of that name. Both statements take a guard, when and a condition, and then act only where the condition holds: leave scan when reading > Limit, next when odd i (when-guard). The example under loops uses both.

WrittenCodeThe compiler’s words
a name that no loop around the statement carries, leave outer;5227no enclosing loop is named 'outer': a loop name can only be targeted from inside the loop it names
next outside every loop5226'next' advances a loop iteration, so it requires an enclosing for, while, or repeat statement
a loop inside another loop of the same name5228loop name 'scan' is already carried by an enclosing loop, so a target spelling it would be ambiguous
a name before begin, block : begin2045a loop name must introduce a for, while, or repeat statement, found begin

Deferred statements

defer and a statement registers the statement to run when the routine it stands in ends (defer-statement), at its last statement or at a leave, and in the program’s body when the program ends. The registered statements run newest first, so a teardown mirrors its setup, and each reads the values that hold when it runs, not those of its registration. A path that never reached a defer does not run it, so a leave above a registration ends the routine without it. Leaving a loop is not the end of the routine and runs nothing. A defer stands unconditionally in the body of its routine, outside every choice and loop.

defer until and an owner, the name of a routine around it or program, registers a dispose to run when that owner ends instead (until-owner), and new takes the same owner, new Node until program, as heap end to end teaches.

In the body of a library or an object unit, defer until program and any statement registers a finalizer of the unit, which runs once when the program has ended (libraries). A finalizer stands at the top level of that body, outside every choice and loop, so every run of the body reaches it and the finalizers a program runs are the ones its units write, newest first. A cleanup that depends on a condition keeps its finalizer at the top level and puts the condition inside the statement, which reads the unit’s state when the program has ended:

{
    Ledger - a library whose finalizers stand at the top level of its body, one of them holding its condition.

    The body runs once when a program that uses the library starts, and registers two finalizers. When the program has
    ended they run, the last registered first: the guarded one closes the entries the program opened, or says there were
    none, and the first one reports the count it reads then.
}
library ledger;

imp
    WriteLn : std;

const
    ClosedLine = "closed %lld entries";
    EmptyLine = "nothing to close";
    CountLine = "the ledger ends with %lld entries";

var
    entries : int64;

{ Open adds one entry to the ledger. }
exp procedure Open();
begin
    entries := entries + 1;
end;

begin
    entries := 0;

    defer until program WriteLn(CountLine, entries);

    defer until program
        if entries > 0 then
            WriteLn(ClosedLine, entries)
        else
            WriteLn(EmptyLine);
end.

A bare leave in such a body ends the body, as it ends a routine’s, and the finalizers written above it run when the program has ended. A leave stands below every finalizer of the body: one above a finalizer would skip its place while the finalizer still ran, so the compiler refuses it and names the finalizer. A body that may end early writes its finalizers first:

{
    Index - a library whose body ends early when there is nothing to index, below its finalizer.

    The body registers its finalizer first, then leaves when the index has no capacity, so the line below the 'leave'
    does not run on that path. When the program has ended the finalizer reports the capacity it reads then, whichever
    way the body ended.
}
library index;

imp
    WriteLn : std;

const
    BuiltLine = "the index holds %lld slots";
    EndLine = "the index ends with %lld slots";
    DefaultCapacity = 8;

var
    capacity : int64;

{ Resize sets the capacity the index reports when the program has ended. }
exp procedure Resize(slots : int64);
begin
    capacity := slots;
end;

begin
    capacity := DefaultCapacity;

    defer until program WriteLn(EndLine, capacity);

    leave when capacity = 0;

    WriteLn(BuiltLine, capacity);
end.
{
    Cleanup - deferred statements, run newest first when their routine ends, and a heap cell released by one.

    'Work' registers three deferred statements. They run when 'Work' ends, the last registered first, and each reads
    the values that hold then, so the report prints the weight the body set after it was registered. The cell is
    released by the deferred 'dispose', which the routine registered right after allocating it.
}
program Cleanup;

imp
    WriteLn : std;

const
    WeightLine = "the cell weighs %lld";
    EndLine = "Work ends, last of all";
    ReportLine = "the report reads the weight at the end: %lld";
    BackLine = "back in the program";
    FirstWeight = 1;
    FinalWeight = 42;

type
    Node = record
        weight : int64;
    end;

{ The work of one routine whose cleanup is registered as it goes. }
procedure Work();
var
    cell : pointer Node;
begin
    defer WriteLn(EndLine);
    cell := new Node;
    defer dispose cell;
    cell.weight := FirstWeight;
    defer WriteLn(ReportLine, cell.weight);
    cell.weight := FinalWeight;
    WriteLn(WeightLine, cell.weight);
end;

begin
    Work();
    WriteLn(BackLine);
end.

It prints:

the cell weighs 42
the report reads the weight at the end: 42
Work ends, last of all
back in the program
WrittenCodeThe compiler’s words
a defer inside an if5192defer statement must be placed unconditionally in the body of its function, but found it inside one 'if statement'
an owner on a statement that is not a dispose, defer until Outer WriteLn(...)5197a deferred statement with an 'until' owner must be a dispose statement
an owner that names nothing, defer until Nowhere dispose p;5018identifier not found: Nowhere
a finalizer inside a loop of a library’s body, for order := 1 to 3 do defer until program WriteLn(...)5799a 'defer until program' in an initialization block is a unit finalizer, which runs once when the program ends whether or not its place ran, so it must stand at the top level of the block, but found it inside one 'for statement' - move it to the top level of the initialization block and put the condition inside its statement, or, where the cleanup belongs to the conditional work, do that work in a routine whose own 'defer' runs when the routine ends
a leave above a finalizer of a library’s body, if ready = 0 then leave; on line 18 above defer until program WriteLn(...) on line 205801a bare 'leave' ends the initialization block, so it skips the place of the unit finalizer at line 20, column 5 below it, which would still run when the program ends - move the finalizer above the 'leave', or put the work that leaves early in a routine the block calls, whose own body the 'leave' then ends

Disposal

dispose and a pointer releases the heap cell it points to (dispose-statement), and dispose and a variable of the routine whose record or array type holds heap cells releases those cells. Every cell a program allocates is released on every path, by a dispose, a deferred one or the owner its new names, and the compiler follows each cell to its release and refuses a program that reads a cell after it, as heap end to end and ownership teach.

WrittenCodeThe compiler’s words
an integer disposed, dispose n;5190dispose statement requires a pointer, or a variable of its own routine whose record or array type holds a heap cell, but found data type 'int64'
a field read after its cell is disposed, dispose p; WriteLn("%lld", p.w);13105heap cell is dereferenced after it was already released on every path reaching here — the cell allocated at line 15 is used after its dispose; move the use before the release