Statements
Every statement of Antril - blocks, local declarations, if, the forms of loop, switch, try, throw, return, break and continue, checked, region, unsafe and delete.
On this page
A function's body is a block of statements. This page lists every statement the compiler accepts. Expressions are described in Expressions and patterns in Patterns.
| Statement | Form |
|---|---|
| Block | { statements } |
| Empty | ; |
| Local declaration | T name = value;, T a, b = 2;, const T name = value; |
| Expression | expression; (a call, an assignment, ++x, await t…) |
if |
if (condition) statement [else statement] |
loop |
six forms: see loop |
do |
do statement loop (condition); |
static loop |
static loop (E in T) statement |
switch |
switch (value) { pattern => statement … } |
try |
try { } catch (T e) { } finally { } |
throw |
throw value;, throw; |
return |
return;, return value; |
break, continue |
break;, continue; |
yield |
yield value;, yield break; (in iterators) |
checked, unchecked |
checked { … }, unchecked { … } |
region |
region { … } |
unsafe |
unsafe { … } |
delete |
delete value; |
effect |
effect { … } |
Blocks
A block { … } groups statements and opens a scope: a local declared inside it is not seen after the }. Wherever a statement is expected, a single statement can stand without braces:
if (ready)
Start();
A lone ; is an empty statement.
Local declarations
A local variable is declared with its type first, then its name and an optional value. Several locals of one type share a declaration, each with its own value or none:
i32 count = 0;
i32 width = 4, height = 3;
i32 a, b = 2; // a starts at 0
- A local of a type that has a zero value (numbers,
bool, enums,valuetypes, nullable types) starts at that value when it has no initializer. - A local of a class or
strneeds a value:C c;is AN2016 (cneeds a value: aChas no default). Declare it nullable (C? c;, null) or give it one. - The type is always written: there is no
varfor locals (varis only a pattern). - A name can be declared once in a function, including inner blocks: declaring
xinside a block where an outerxis visible is AN2003. Two sibling blocks can each declare their ownx.
const declares a local constant. Its value is computed when the program compiles, from literals, other constants and operators (see Members):
const i32 Limit = 3, Twice = Limit * 2;
Assigning a constant is AN2140.
Expression statements
An expression followed by ; is a statement: a call, an assignment (x = 1;, x += 2;, also chained: a = b = 4;), an increment (i++;), an await, or new of an object whose constructor does the work.
if
if (temperature > 30)
Console.WriteLine("hot");
else if (temperature > 15)
Console.WriteLine("mild");
else
Console.WriteLine("cold");
The condition is a bool; an integer is not a condition (if (x) with i32 x is AN2084). An else belongs to the nearest if without one.
loop
loop is the only looping keyword. What is in its parentheses decides the form:
| Form | What it does |
|---|---|
loop (T x in items) body |
Runs body for each element of an array, a string, a collection or an iterator. |
loop (condition) body |
Runs while condition is true. |
do body loop (condition); |
Runs body once, then while condition is true. |
loop (T i = a..b) body |
i from a up to b, without b. a..=b includes b. |
loop (T i = start; condition; step) body |
Declares i, runs while condition holds, evaluates step after each turn. |
loop (T i = start) body |
Without end: i starts at start and goes up by one each turn, until a break or return. |
import System;
Main()
{
loop (i32 i = 5..=7)
Console.Write(i);
Console.WriteLine();
loop (i32 k = 0; k < 6; k += 2)
Console.Write(k);
Console.WriteLine();
loop (i64 j = 10)
{
if (j > 12)
break;
Console.Write(j);
}
Console.WriteLine();
List<i32> list = new List<i32>();
list.Add(4);
list.Add(5);
loop (i32 x in list)
Console.Write(x);
Console.WriteLine();
loop (u16 unit in "hey")
Console.Write(unit + " ");
Console.WriteLine();
i32 n = 1;
loop (n < 100)
n *= 3;
Console.WriteLine(n);
do
n -= 50;
loop (n > 0);
Console.WriteLine(n);
}
larva run
567
024
101112
45
104 101 121
243
-7
- Elements.
loop (T x in c)walks an array (each dimension in row order, for a multi-dimensional one), a string (its code units, as numbers), any collection with anIterator()method (List<T>,Dictionary<K, V>,HashSet<T>…), and anIterator<T>that a function gives withyield(see Iterators and async). The element type is written. - Ranges. The counter of a range or counter loop is an integer (AN2014 for anything else). The bounds are evaluated once. A range whose end is not above its start runs zero times:
loop (i32 r = 3..0)does nothing. - C-style. The parser also accepts a comma between the condition and the step:
loop (i32 k = 0; k < 6, k += 2). Write a;. - The counter of every form is a new local, seen only inside the loop.
static loop
static loop (E in T) statement, inside a function whose last type parameter is a pack T..., repeats the statement once per type of the pack, when the program compiles. Inside, E is that type:
import System;
Describe<T...>(T values) : str
{
str text = "";
static loop (E in T)
text = text + typeof(E).Name + " ";
return text;
}
Main()
{
Console.WriteLine(Describe(1, "a", 2.5));
}
larva run
Int32 String Float64
A static loop among a type's members declares members instead; see Generics. from N (numbering the members) goes only on that form: in a body it is AN0036.
break and continue
break leaves the innermost loop or switch statement; continue goes to the next turn of the innermost loop. Outside them they are AN2010 and AN2011.
Inside a switch statement, break leaves the switch, not the loop around it, while continue goes on to the loop's next turn:
import System;
Main()
{
loop (i32 i = 0..5)
{
switch (i)
{
1 => continue;
3 => break;
_ => { }
}
Console.Write(i);
}
Console.WriteLine();
}
larva run
0234
return
return value; leaves a function that has a result type; return; leaves one without. Returning a value from a function without a result is AN2009, and a function with a result whose end can be reached without return is AN2001. In an iterator, return; ends the sequence like yield break;.
A finally runs before the function returns.
switch
A switch statement matches a value against arms, in order, and runs the first arm whose pattern matches (and whose when condition holds):
import System;
Main()
{
loop (i32 n in [0, 3, 7, 12])
{
switch (n)
{
0 => Console.WriteLine("zero");
1..5 =>
{
Console.Write("small ");
Console.WriteLine(n);
}
> 10 => Console.WriteLine("big");
}
}
}
larva run
zero
small 3
big
- Each arm is
pattern [when condition] => statement. The statement can be a block. - Arms end with
;(the statement's own end) or with,. - When no arm matches, nothing runs:
7above prints nothing. Add_ => …to handle the rest. - Every pattern form works; see Patterns.
The switch expression (value switch { … }) gives a value; see Expressions.
try, catch and finally
import System;
Main()
{
loop (i32 i = 0..3)
{
try
{
if (i == 0)
throw new KeyNotFoundError();
if (i == 1)
throw new InvalidOperationError("bad state");
Int32.Parse("x");
}
catch (KeyNotFoundError)
{
Console.WriteLine("missing key");
}
catch (InvalidOperationError e)
{
Console.WriteLine("invalid: " + e.Message);
}
catch
{
Console.WriteLine("anything else");
}
}
}
larva run
missing key
invalid: bad state
anything else
tryis followed by any number ofcatchclauses and an optionalfinally; at least one of them (AN0018).catch (T name)handles exceptions of the classTand the classes derived from it. The name can be left out.Tis an object type (AN2017).catchwithout parentheses handles any exception.- The clauses are tried in order; the first that matches runs.
finallyruns when thetryends in any way: normally, by an exception (caught or not), or byreturn,breakorcontinue.
yield and await cannot go inside a catch (AN2113, AN2116). See Exceptions for declaring exception classes and what an uncaught exception does.
throw
throw value; throws an object of a class derived from System.Exception. A type parameter constrained with T : exception can be thrown too.
throw; inside a catch throws the exception being handled again; elsewhere it is AN2006.
Warning
Not yet available: throwing values that are not objects. throw 5; is AN2007 (only objects are thrown).
checked and unchecked
Integer arithmetic wraps around by default: Int32.MaxValue + 1 is Int32.MinValue. Inside checked { … }, an integer operation whose result does not fit its type throws OverflowError. unchecked { … } turns the check off again inside a checked block. Both also exist as expressions: checked(a + b).
import System;
Main()
{
i32 big = Int32.MaxValue;
Console.WriteLine(big + 1);
try
{
checked
{
i32 x = big + 1;
Console.WriteLine(x);
}
}
catch (OverflowError e)
{
Console.WriteLine("overflow: " + e.Message);
}
}
larva run
-2147483648
overflow: the number does not fit its type
region
region { … } frees the objects made inside it when the block ends, however it ends. It matters in projects built with gc: arena; see Memory.
unsafe
unsafe { … } allows pointers, addresses (&x) and reading through them (*p, p[i], p->field) inside the block. A project with unsafe: deny rejects it (AN1104). See Memory.
delete
delete obj; runs an object's destructor and frees it, in projects built with gc: manual. Its operand is an object (AN2012 otherwise). Write it inside unsafe code; see Memory.
yield
yield value; gives the next value of an iterator and yield break; ends it. They go in a function whose result is Iterator<T>; see Iterators and async.
effect
effect { … } as a statement starts an effect that runs now and again whenever a reactive value it read changes. Written as an expression, it gives the Effect so that it can be stopped. See Reactive values.
Edit this page on GitHub Report an issue