Syntax and informal semantics | Contents |
<program> | ::= | [typedef <type_def>+] | enumerated types definition |
<procedure_def>* | definition of procedures | ||
[var <vars_decl> ;] | local variables of the main procedure | ||
begin <statement>+ end | body of the main procedure | ||
<type_def> | ::= | id = enum { id (, id)* } ; | |
<procedure_def> | ::= | proc id ( <vars_decl> ) returns ( <vars_decl> ) | signature, with names of |
input and output formal parameters | |||
[var <vars_decl> ;] | (other) local variables | ||
begin <statement>+ end | body |
A procedure definition first declares a list of formal input parameters, then a list of formal output parameters (which are local variables that defines the tuple of returned values), then a list of local variables, and then a body. A procedure ends with its last statement.
<vars_decl> | ::= | eps | id : <type> (, id : <type>)* | |
<type> | ::= | bool | Boolean type |
| | uint[<integer>] | unsigned integers encoded with <integer> bits | |
| | sint[<integer>] | signed integers encoded with <integer> bits | |
| | id | Name of an enumerated type | |
| | int | real | numerical types |
Variables are either of finite-type, or unbounded integer or real type. Finite types includes Boolean, bounded integers encoded with the given numbe rof bits, and enumerated types. Real variables can behave as floating-point variables by modifying the exact semantics of arithmetic operations (see below).
<statement> | ::= | skip ; |
| | halt ; | |
| | fail ; | |
| | assume <expr> ; | |
| | id = random ; | |
| | id = <expr> ; | |
| | id = id ( <var_list> ) ; | |
| | ( <var_list> ) = id ( <var_list> ) ; | |
| | if <bexpr> then <statement>+ [else <statement>+ ] endif ; | |
| | while <bexpr> do <statement>+ done ; | |
<var_list> | ::= | eps | id (, id)* |
The skip operation does nothing. The halt operation halts the program execution. The fail has exactly the same operational semantics, but, in addition, indicates a final control point for backward analysis (see below).
The assume expr instruction is equivalent to if expr then skip; else halt; . It may be used to abstract non-deterministic assignements. For instance, the non-deterministic assignement of a value between 0 and 2 to a variable x can be written as
x = random; assume x>=0 and x<=2;
A call to a procedure with a single return value can be written either as x = P(...) or (x)=P(...). A call to a procedure with multiple return values is written as (x,y) = P(a,b). Input parameters are passed by value. Formal input parameters in a procedure are assumed immutable.
Conditional and iteration constructions have the usual semantics.
<expr> | ::= | random | random value (type deduced from the context) |
| | <bexpr> | Boolean expression | |
| | <iexpr> | bounded integer expression | |
| | <eexpr> | enumerated type expression | |
| | <nexpr> | numerical expression |
<bexpr> | ::= | true | false | |
| | id | variable | |
| | <constraint> | ||
| | not <bexpr> | ||
| | <bexpr> (or | and) <bexpr> | ||
| | ( <bexpr> ) | ||
| | if <bexpr> then <bexpr> else <bexpr> | ||
<constraint> | ::= | id == <expr> | |
<iexpr> (>= | > | <= | <) <iexpr> | |||
<nexpr> (>= | > | <= | <) <nexpr> |
Atoms of Boolean expressions are Boolean constants or equality constraints (on any type) or inequality constraints over bounded integer or numerical variables.
<iexpr> | ::= | uint[<integer>](<integer>) | constant (second integer) of given type |
| | sint[<integer>](<integer>) | constant (second integer) of given type | |
| | id | variable | |
| | <iexpr> (+|-|*) <iexpr> | ||
| | ( <iexpr> ) | ||
| | if <bexpr> then <iexpr> else <iexpr> | ||
<eexpr> | ::= | id | variable or constant (label) |
| | ( <eexpr> ) | ||
| | if <bexpr> then <eexpr> else <eexpr> |
Example:
/* Illustrate enumerated and bounded integer types */ typedef t = enum a,b,c,d,e ; var x:sint[4],y:sint[4], z:t; begin x = sint[4](2); y = random; z = a; if x==y then x = x + x; x = x-sint[4](3)*y; z = b; endif; if x<=y then z = e; endif; if z==d then z=c; endif; end
<nexpr> | ::= | <coeff> | |
| | id | variable | |
| | <unop> <nexpr> | <nexpr> <binop> <nexpr> | ||
| | ( <nexpr> ) | ||
| | if <bexpr> then <nexpr> else nexpr | ||
<binop> | ::= | (+|-|*|/|%)[_(i|f|d|l|q)[,(n|0|+oo|-oo)]] | |
<unop> | ::= | - | |
| | (cast|sqrt)[_(i|f|d|l|q)[,(n|0|+oo|-oo)]] | ||
<coeff> | ::= | <float> | <rational> | |
<float> | ::= | C/OCaml floating-point number syntax | |
<rational> | ::= | <integer> | <integer>/<integer> |
The syntax of numerical expressions is the same as the one used in the APRON interactive toplevel (in OCaml language).
The priorities between (Boolean and arithmetic) operators is { *,/ } > { +,- } > { <, <=, =, >=,> } > { not } > { and } > { or }
By default, numerical expressions are evaluated using exact arithmetic on real numbers (even if some involved variables are integer), and when assigned to a variable, filtered by the variable domain (when it is integers). For instance, the assignement
x = 3/4;
is equivalent to the halt instruction if x is declared as integer, because the rational 3/4 is not an integer value. The problem here is that the instruction is not well-typed (even if this is not checked). If one want to emulate the semantics of the integer division operation, with rounding to zero, one should write
x = 3 /_i,0 4;
Indeed, most arithmetic operators have optional qualifiers that allows to modify their semantics. The first qualifier indicates a numerical type, the second one the rounding mode.
i | integer semantics |
f | IEEE754 32 bits floating-point semantics |
d | IEEE754 64 bits floating-point semantics |
l | IEEE754 80 bits extended floating-point semantics |
q | IEEE754 128 bits floating-point semantics |
n | nearest |
0 | towards zero |
+oo | towards infinity |
-oo | towards minus infinity |
We illustrate the syntax and (approximated) semantics of numerival expressions with the following program, annotated by InterprocStack using the command
interprocstack.opt -domain polka -colorize false examples/numerical.spl
proc integer () returns () var a : int, b : int, c : int; begin /* (L5 C5) top */ a = 5; /* (L6 C8) [|a-5=0|] */ b = 2; /* (L7 C8) [|b-2=0; a-5=0|] */ if brandom then /* (L8 C17) [|b-2=0; a-5=0|] */ c = a / b; /* (L9 C14) bottom */ else /* (L10 C6) [|b-2=0; a-5=0|] */ c = a /_i,? b; /* (L11 C16) [|b-2=0; a-5=0; -c+3>=0; c-2>=0|] */ c = a /_i,0 b; /* (L12 C18) [|c-2=0; b-2=0; a-5=0|] */ c = a /_i,-oo b; /* (L13 C20) [|c-2=0; b-2=0; a-5=0|] */ c = a /_i,+oo b; /* (L14 C20) [|c-3=0; b-2=0; a-5=0|] */ c = a /_i,n b; /* (L15 C18) [|b-2=0; a-5=0; -c+3>=0; c-2>=0|] */ c = a %_i,? b; /* (L16 C16) [|c-1=0; b-2=0; a-5=0|] */ endif; /* (L17 C8) [|c-1=0; b-2=0; a-5=0|] */ end proc exact () returns (z : real) var x : real, y : real; begin /* (L22 C5) top */ x = 5; /* (L23 C8) [|x-5=0|] */ y = 2; /* (L24 C8) [|y-2=0; x-5=0|] */ z = x / y; /* (L25 C10) [|2z-5=0; y-2=0; x-5=0|] */ y = 0.1; /* (L26 C10) [|2z-5=0; 36028797018963968y-3602879701896397=0; x-5=0|] */ z = x + y; /* (L27 C10) [|36028797018963968z-183746864796716237=0; 36028797018963968y-3602879701896397=0; x-5=0|] */ z = z - y; /* (L28 C10) [|z-5=0; 36028797018963968y-3602879701896397=0; x-5=0|] */ end proc floating () returns (z : real) var x : real, y : real; begin /* (L33 C5) top */ x = 5; /* (L34 C8) [|x-5=0|] */ y = 2; /* (L35 C8) [|y-2=0; x-5=0|] */ z = x /_f,? y; /* (L36 C13) [|y-2=0; x-5=0; -713623846352979940529142984724747568191373312z +1784059828558929176909397126420998565333565441>=0; 713623846352979940529142984724747568191373312z -1784059403205970525736317797202739275623301119>=0|] */ y = 0.1; /* (L37 C10) [|36028797018963968y-3602879701896397=0; x-5=0; -713623846352979940529142984724747568191373312z +1784059828558929176909397126420998565333565441>=0; 713623846352979940529142984724747568191373312z -1784059403205970525736317797202739275623301119>=0|] */ z = x +_f,? y; /* (L38 C14) [|36028797018963968y-3602879701896397=0; x-5=0; -713623846352979940529142984724747568191373312z +3639482050260215524856578735848702239922192385>=0; 713623846352979940529142984724747568191373312z -3639481182540179876463495959770156714984210431>=0|] */ z = z -_f,? y; /* (L39 C14) [|36028797018963968y-3602879701896397=0; x-5=0; -5986310706507378352962293074805895248510699696029696z +29931560882862943060522688904967984086376852368654337>=0; 5986310706507378352962293074805895248510699696029696z -29931546182211708189135890236173744477275669529624577>=0|] */ end var z : real begin /* (L43 C5) top */ () = integer(); /* (L44 C17) top */ z = exact(); /* (L45 C14) [z-5=0|] */ z = floating(); /* (L46 C17) [|-5986310706507378352962293074805895248510699696029696z +29931560882862943060522688904967984086376852368654337>=0; 5986310706507378352962293074805895248510699696029696z -29931546182211708189135890236173744477275669529624577>=0|] */ end
Syntax and informal semantics | Contents |