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.

Last updated 11 min read Edit on GitHub

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:

Antril
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:

Antril
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, value types, nullable types) starts at that value when it has no initializer.
  • A local of a class or str needs a value: C c; is AN2016 (c needs a value: a C has no default). Declare it nullable (C? c;, null) or give it one.
  • The type is always written: there is no var for locals (var is only a pattern).
  • A name can be declared once in a function, including inner blocks: declaring x inside a block where an outer x is visible is AN2003. Two sibling blocks can each declare their own x.

const declares a local constant. Its value is computed when the program compiles, from literals, other constants and operators (see Members):

Antril
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

Antril
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.
AntrilRun
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);
}
Terminal
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 an Iterator() method (List<T>, Dictionary<K, V>, HashSet<T>…), and an Iterator<T> that a function gives with yield (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:

AntrilRun
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));
}
Terminal
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:

AntrilRun
import System;

Main()
{
	loop (i32 i = 0..5)
	{
		switch (i)
		{
			1 => continue;
			3 => break;
			_ => { }
		}
		Console.Write(i);
	}
	Console.WriteLine();
}
Terminal
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):

AntrilRun
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");
		}
	}
}
Terminal
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: 7 above 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

AntrilRun
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");
		}
	}
}
Terminal
larva run
missing key
invalid: bad state
anything else
  • try is followed by any number of catch clauses and an optional finally; at least one of them (AN0018).
  • catch (T name) handles exceptions of the class T and the classes derived from it. The name can be left out. T is an object type (AN2017).
  • catch without parentheses handles any exception.
  • The clauses are tried in order; the first that matches runs.
  • finally runs when the try ends in any way: normally, by an exception (caught or not), or by return, break or continue.

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).

AntrilRun
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);
	}
}
Terminal
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.

Was this page helpful?

No address is stored; your user name is, if you are signed in.

Esc