Jean-Baptiste Tristan & K. Rustan M. Leino |
method MethodName<T>(x: T, y: string) {
print x;
print y;
}
method Call() {
MethodName("Hello, ", "World\n");
}
method VariableDeclaration() {
var x: int := integerExpression;
}
:=
is a RHS expression
method Assignment() {
var x := integerExpression;
x := x + 1;
}
:=
is an $\ell$-value
method Assignment() {
var w: int := integerExpression;
var x: int;
x := integerExpression;
var y := integerExpression;
var z;
z := integerExpression;
}
method MultipleInsAndOuts(a: int, b: int) returns (x: int, y: int) {
x := a + b;
y := x + 1;
}
method MultipleInsAndOuts(a: int, b: int) returns (x: int, y: int) {
x := a + b;
y := x + 1;
return;
y := 500; // this statement is never reached
}
}
return
statement abruptly exits the body of a method
return
is like a control-flow jump to the method body's }
method Return() returns (o: int) {
return integerExpression;
}
method ReturnArgumentsMeaning() returns (o: int) {
o := integerExpression;
return;
}
return
statement can be used with arguments, which are assigned to the out-parameters
method Calls() {
var s: int := Return();
var x, y := MultipleInsAndOuts(20, 30);
}
method If() {
if booleanExpression {
// ...
}
}
method IfElse() {
if booleanExpression {
// ...
} else {
// ...
}
}
method CascadingIf() {
if booleanExpressionA {
// ...
} else if booleanExpressionB {
// ...
} else {
// ...
}
}
method WhileLoop() {
while booleanExpression {
// ...
}
}
break
and continue
statements
method ForLoop() {
for x := loExpression to hiExpression {
// ...
}
for x := hiExpression downto loExpression {
// ...
}
}
loExpression
and hiExpression
of each loop are evaluated once, on entry to the loop
loExpression <= hiExpression
x
satisfies loExpression <= x < hiExpression
method Match() {
match datatypeValue {
case Case1 => // ...
case Case2 => // ...
}
}
case
are in scope in just that case
{
}
needed
match
where no case
applies
datatype Status<T> =
| Success(value: T)
| Failure(error: string)
{
predicate IsFailure() { this.Failure? }
function PropagateFailure(): Status<T> { this }
function Extract(): T { this.value }
}
IsFailure
, PropagateFailure
, and Extract
method Callee(i: int) returns (r: Status<int>)
{
if i < 0 {
return Failure("negative");
}
return Success(i);
}
method Caller(i: int) returns (r: Status<int>)
{
var x: int :- Callee(i);
return Success(x + 5);
}
method CallerMeaning(i: int) returns (r: Status<int>)
{
var tmp: Status<int> := Callee(i);
if tmp.IsFailure() {
return tmp.PropagateFailure();
}
var x: int := tmp.Extract();
return Success(x + 5);
}
:-
is commonly known as the “elephant operator”
method Allocate<T>(size: int) returns (arr: array<T>) {
arr := new T[size];
}
new
is a RHS expression
method Aliasing() {
var arr := new int[100];
var brr := arr;
}
arr
as an array, but technically:
arr
is a reference to an array
arr
points to the array
brr
, and it refers to the same array as arr
predicate Null<T>() { null is array?<T> }
predicate IsNull<T>(arr: array?) { (arr == null) is bool }
array<T>
is the type of T
-array references
array?<T>
additionally contains the value null
predicate Indexing<T>(arr: array<T>, index: int) { arr[index] is T }
predicate Length<T>(arr: array<T>) { arr.Length is int }
predicate ToSequence<T>(arr: array<T>) { arr[..] is seq<T> }
method Update<T>(arr: array<T>, index: int, value: T) {
arr[index] := value;
}