Statements¶
This chapter specifies all ABS statements.
Statement ::= |
SkipStmt |
| VarDeclStmt |
|
| AssignStmt |
|
| ExpStmt |
|
| AssertStmt |
|
| AwaitStmt |
|
| SuspendStmt |
|
| ThrowStmt |
|
| ReturnStmt |
|
| Block |
|
| IfStmt |
|
| SwitchStmt |
|
| WhileStmt |
|
| ForeachStmt |
|
| TryCatchFinallyStmt |
Skip¶
The skip statement is a statement that does nothing.
SkipStmt ::= |
|
Example:
skip;
Variable declarations¶
A variable declaration statement is used to declare variables. Variable declarations can occur at any point in a sequence of statements; i.e., it is not necessary to declare variables only at the beginning of methods or blocks.
Variables declared inside a block are in scope for the duration of the block. It is an error to declare a variable with the same name of another variable in scope. A local variable can have the same name as an object field.
A variable declaration has an expression that defines the initial
value of the variable. The initialization expression is mandatory
except for variables of reference types (interfaces and futures), in
which case the variable is initialized with null if the
initialization expression is omitted.
VarDeclStmt ::= |
Type SimpleIdentifier [ |
Example:
Bool b = True;
Note that the initialization expression does not need to be a pure expression.
Constant declarations¶
Variable and field declarations can carry a Final annotation. The
effect of such an annotation is to forbid re-assignment to the
variable or field.
The following example will cause a compile-time error since we are
trying to assign a new value to constant_i:
{
[Final] Int constant_i = 24;
constant_i = 25;
}
Assignment¶
The assign statement assigns a value to a variable or a field.
Assignments to a field f can be written either this.f = e; or
f = e;. In case a local variable f is in scope at the point
of the assignment statement, the this prefix has to be used to
assign to the field f; assignment to f will change the value
of the local variable.
AssignStmt ::= |
[ |
Example:
this.f = True;
x = 5;
Note that the expression does not need to be pure.
Expressions as statements¶
An expression statement is a statement that consists of a single expression. When an expression statement is executed, the expression is evaluated and the resulting value is discarded.
Expression statements are used for their side effects, for example issuing an asynchronous method call without waiting for its result.
ExpStmt ::= |
Exp ; |
Note
Creating an object without storing a reference (and hence
never invoking a method on the new object) can be a
meaningful operation, for example when the object has a
run method and interacts with the rest of the system by
calling methods on references passed in via the new
expression.
Example:
server!operate();
new Client(server);
Assertions¶
An assert statement is a statement for asserting certain conditions.
If the expression evaluates to True, executing an assertion is
equivalent to skip;. If the expression evaluates to False, it
is equivalent to throw AssertionFailException;.
AssertStmt ::= |
|
Example:
assert x != null;
Await (statement)¶
An await statement suspends the current task until the given guard
becomes active (evaluates to True). While the task is suspended,
other tasks within the same COG can be scheduled.
Guards can wait for a futures to become resolved, for a Boolean condition over the object state to become true, or (in timed ABS) for a certain duration to pass.
Note
When evaluating a guard results in an exception, that exception will be thrown when the task is scheduled next, and can be caught as normal (see Handling exceptions with try-catch-finally). Note that a guard can be evaluated multiple times before its task is scheduled – the most recent exception will be thrown even if the guard evaluated without an exception afterwards.
In general, each cog will continue running a task without preempting
it until the task is finished or it reaches a scheduling point.
Await statements are scheduling points, as are suspend statements
and assignment or expression statements containing an await expression
(see Await (expression)).
AwaitStmt ::= |
|
Guard ::= |
[ |
| PureExp |
|
| Guard |
|
| |
Example:
Fut<Bool> f = x!m();
await f?; ①
await this.x == True; ②
await f? & this.y > 5; ③
await duration(3, 5); ④
Unconditional release: suspend¶
The suspend statement causes the current task to be suspended.
SuspendStmt ::= |
|
Note
There is no guarantee that the cog will choose another task to run; the current task might be resumed immediately after suspending itself.
Warning
It is tempting to misuse the suspend statement for
busy-waiting (while (!condition) suspend;
doTheThing();). Since ABS does not guarantee fair
scheduling, it is highly recommended to await on the
condition intead (await condition; doTheThing();).
Example:
suspend;
Return¶
A return statement returns a value from a method. A return statement can only appear as the last statement in a method body.
For asynchronous method calls, executing the return statement will cause the future to be resolved so that it contains a value. Any claim guards awaiting the future will become active.
Methods that have a Unit return type do not need an explicit
return statement. The future will be resolved when the method
terminates.
ReturnStmt ::= |
|
Example:
return x;
Note
ABS methods are “single entry single exit”, i.e., do not
allow exiting from multiple points via multiple return
statements. This makes model analysis easier.
Throw¶
The throw statement signals an exception (see
Exceptions). It takes a single argument of type
ABS.StdLib.Exception, which is the exception value to throw.
ThrowStmt ::= |
|
Example:
throw AssertionFailException;
Note that the throw statement can only be used inside imperative code.
Functional code that cannot return a value in all cases should use the
Maybe datatype:
def Maybe<Int> f(Int x, Int y) = if (y < 0) then None else Just(x);
But also note that some built-in exceptions, like
DivisionByZeroException and PatternMatchFailException can
originate from functional code. See
Predefined exceptions in the standard library for a list of built-in exceptions.
Blocks of statements¶
A sequence of statements is called a block. A block introduces a scope for local variables.
Block ::= |
|
Example:
{
Int a = 0; ①
a = a + 1;
n = a % 10; ②
}
{ } ③
a is in scope until the end of the block.n has to exist in an outer block or as a class field.skip;.Note
Semantically, a whole block is a single statement and can be written anywhere a single statement is valid.
Conditionals¶
ABS has the standard conditional statement. The condition has to evaluate to a Boolean value.
IfStmt ::= |
|
Example:
if (5 < x) {
y = 6;
} else {
y = 7;
}
if (True)
x = 5;
Switch: pattern matching¶
The switch statement, like the case expression (see Case), consists of an expression and a series of branches, each consisting of a pattern and a statement (which can be a block).
When a switch statement is executed, its input expression is evaluated and the value matched against the branches until a matching pattern is found. The statement in the right-hand side of that branch is then executed. Any variable bindings introduced by matching the pattern are in effect while executing that statement.
If no pattern matches the expression, a PatternMatchFailException is
thrown.
For a description of the pattern syntax, see Case.
SwitchStmt ::= |
|
SwitchStmtBranch ::= |
Pattern |
Example:
Pair<Int, Int> p = Pair(2, 3);
Int x = 0;
switch (p) {
Pair(2, y) => { x = y; skip; }
_ => x = -1;
}
The while loop¶
The while loop repeats its body while the condition evaluates to
True. The condition is re-evaluated after each iteration of the
loop.
WhileStmt ::= |
|
Example:
while (x < 5) {
x = x + 1;
}
The foreach loop¶
The foreach loop repeatedly executes its body with a loop value variable bound to each element of the given list, in sequence. An optional loop index variable is bound to the index of the element bound to the value variable, starting with 0.
The rules for the loop variables follow the rules of local variable declarations in blocks: the loop variables cannot shadow existing variables, but can use the same name as an object field. Loop variables go out of scope after execution leaves the body of their foreach loop.
ForeachStmt ::= |
|
Example:
foreach (v in list["a", "b", "c"]) {
println(`v is bound to $v$`);
}
foreach (v, i in list["a", "b", "c"]) {
println(`Element number $i$ has value $v$`);
}
Note
Also see the second-order functions map, filter,
foldl and foldr in Section Lists for common
list processing idioms.
Handling exceptions with try-catch-finally¶
Executing a statement can result in an exception, either explicitly
signaled using the throw keyword or implicitly, for example by
dividing by zero. The try-catch-finally statement is used to handle
exceptions and resume normal execution afterwards.
TryCatchFinallyStmt ::= |
|
|
|
[ |
The statement protected by try (which can be a block) is executed
first. If no exception is thrown, execution continues with the
optional finally statement, then with the next statement after the
try-catch-finally statement.
If during execution of the statement protected by try an exception
is thrown, it is matched one-by-one against the exception patterns
defined in the catch block. The statement following the first
matching pattern will be executed, as in the switch statement (see
Switch: pattern matching). Execution continues with the optional
finally statement, then with the statement following the
try-catch-finally statement.
If during execution of the statement protected by try an exception
is thrown that is not matched by any branch in the catch block,
the exception is unhandled. In this case, first the optional
finally statement is executed. If the try-catch-finally was
protected by another try-catch-finally statement, the unhandled
exception is passed on to this surrounding try-catch-finally
statement. Otherwise, the current process terminates and its future
is resolved by storing the unhandled exception. Any get
expression on this future will re-throw the exception (see
Get). The object that ran the aborted process will
execute its recovery block with the unhandled exception as parameter
(see Classes).
Example:
try {
Rat z = 1/x; ①
} catch {
DivisionByZeroException => println("We divided by zero"); ②
} finally {
println("Leaving the protected area"); ③
}
x is zero, this will throw an exceptionAs a syntactic convenience, when matching only a single pattern, the braces around the catch block can be omitted:
try b = f.get; catch _ => b = False; ①
b to a default value
in case an unhandled exception was propagated via fNote
The finally block has the same restrictions as the class
init and recovery blocks, i.e., it cannot contain processor
release points (i.e., await or suspend), blocking
expressions (i.e., get), or explicitly throw an
exception via the throw statement.