Most of the expression syntax of UPPAAL coincides with that of C, C++ and Java. For example assignments are done using the =
operator (the older :=
still works, but =
is preferred). Notice that assignments are expressions themselves, for example expression b = (a = 1) + 2
assigns 1
to a
, then adds 2
and assigns the result to b
.
The syntax of expressions is defined by the grammar for Expression
.
Expression = [ID]
| NAT
| Expression '[' Expression ']'
| Expression "'"
| '(' Expression ')'
| Expression '++' | '++' Expression
| Expression '--' | '--' Expression
| Expression Assign Expression
| Unary Expression
| Expression Binary Expression
| Expression '?' Expression ':' Expression
| Expression '.' ID
| Expression '(' Arguments ')'
| 'forall' '(' ID ':' Type ')' Expression
| 'exists' '(' ID ':' Type ')' Expression
| 'sum' '(' ID ':' Type ')' Expression
| 'deadlock' | 'true' | 'false';
Arguments = [ Expression ( ',' Expression )* ];
Assign = '=' | ':=' | '+=' | '-=' | '*=' | '/=' | '%='
| '|=' | '&=' | '^=' | '<<=' | '>>=';
Unary = '+' | '-' | '!' | 'not';
Binary = '<' | '<=' | '==' | '!=' | '>=' | '>'
| '+' | '-' | '*' | '/' | '%' | '&'
| '|' | '^' | '<<' | '>>' | '&&' | '||'
| '<?' | '>?' | 'or' | 'and' | 'imply';
Like in C++, assignment, preincrement and predecrement expressions evaluate to references to the first operand. The inline-if operator does in some cases (e.g. when both the true and false operands evaluate to compatible references) also evaluate to a reference, i.e., it is possible to use an inline-if on the left hand side of an assignment.
The use of the deadlock
keyword is restricted to the requirement specification language.
See rail road diagram for the entire Expression syntax.
Boolean values are type compatible with integers. An integer value of 0 (zero) is evaluated to false and any other integer value is evaluated to true. The boolean value true
evaluates to the integer value 1 and the boolean value false
evaluates to the integer value 0. Notice: A comparison like 5 == true
evaluates to false, since true
evaluates to the integer value 1. This is consistent with C++.
UPPAAL operators have the following associativity and precedence, listed from the highest to lowest. Operators borrowed from C keep the same precedence relationship with each other.
Associativity | Operator |
---|---|
left | () [] . |
right | ! not ++ -- unary - unary + |
left | * / % |
left | binary - binary + |
left | << >> |
left | <? >? |
left | < <= >= > |
left | == != |
left | & |
left | ^ |
left | | |
left | && and |
left | || or imply |
right | ?: |
right | = := += -= *= /= %= &= |= <<= >>= ^= |
left | forall exists sum |
Anybody familiar with the operators in C, C++, Java or Perl should immediately feel comfortable with the operators in UPPAAL. Here we summarise the meaning of each operator.
Operator | Description | Example |
---|---|---|
() | Parenthesis alter the evaluation order | 3 * (5 - 2) → 9 |
[] | Array lookup | my_array[0] |
. | Infix lookup operator to access process or structure type scope | my_struct.field1 |
! | Logical negation | ! true → false |
not | Logical negation | not true → false |
++ | Increment (can be used as both prefix and postfix operator) | ++counter counter++ |
-- | Decrement (can be used as both prefix and postfix operator) | --counter counter-- |
- | Integer subtraction (can also be used as unary negation:-7 ) | 5 - 3 → 2 |
+ | Integer addition (can also be used as unary plus: +7 ) | 5 + 3 → 8 |
* | Integer multiplication | 5 * 3 → 15 |
/ | Integer division | 5 / 3 → 1 |
% | Modulo | 5 % 3 → 2 |
<< | Left bitshift | 1 << 4 → 16 |
>> | Right bitshift | 16 >> 4 → 1 |
<? | Minimum | 8 <? 12 → 8 |
>? | Maximum | 8 >? 12 → 12 |
< | Less than | 5 < 3 → true |
<= | Less than or equal to | 5 <= 3 → true |
== | Equality operator | 5 == 3 → false |
!= | Inequality operator | 5 != 3 → true |
>= | Greater than or equal to | 5 >= 3 → true |
> | Greater than | 5 > 3 → true |
& | Bitwise and | 3 & 2 → 2 |
^ | Bitwise xor | 1 ^ 3 → 2 |
| | Bitwise or | 1 | 3 → 3 |
&& | Logical and (conjunction) | true && false → false |
and | Logical and (conjunction) | true and false → false |
|| | Logical or (disjunction) | true || false → true |
or | Logical or (disjunction) | true or false → true |
?: | Inline if-then-else (ternary) operator | true ? 8 : 12 → 8 |
imply | Logical implication | true imply false → false |
forall | Forall quantifier | forall (i : int[2, 10]) i*10 > 10 → true |
exists | Exists quantifier | exists (i : int[2, 10]) i*10 > 90 → true |
sum | Sum expression | sum (i : int[2, 10]) i*10 → 540 |
Notice that the keywords not
, and
and or
behave the same as the !
, &&
, and ||
operators.
An integer expression used in logical expression is converted into boolean value by comparing the integer expression with zero. For example, x ? 1 : 2
is interpreted as (x != 0) ? 1 : 2
.
A few binary operators can be syntactically combined with assignment to produce a compact assignment expression:
Operator | Assignment | Example | Meaning |
---|---|---|---|
+ | += | x += y | x = x + y |
- | -= | x -= y | x = x - y |
* | *= | x *= y | x = x * y |
/ | /= | x /= y | x = x / y |
% | %= | x %= y | x = x % y |
& | &= | x &= y | x = x & y |
^ | ^= | x ^= y | x = x ^ y |
| | |= | x |= y | x = x | y |
<< | <<= | x <<= y | x = x << y |
>> | >>= | x >>= y | x = x >> y |
When involving clocks, the actual expression syntax is restricted by the type checker. Expressions involving clocks are divided into three categories: Invariants, guards, and constraints:
In addition, any of the three expressions can contain expressions (including disjunctions) over integers, as long as invariants and guards are still conjunctions at the top-level. The full constraint language is only allowed in the requirement specification language.
An evaluation of an expression is invalid if out of range errors occur during evalution. This happens in the following situations:
In case an invalid evaluation occurs during the computation of a successor, i.e., in the evaluation of a guard, synchronisation, assignment, or invariant, then the verification is aborted.
An expression forall (ID : Type) Expr
evaluates to true if Expr
evaluates to true for all values ID
of the type Type
. An expression exists (ID : Type) Expr
evaluates to true if Expr
evaluates to true for some value ID
of the type Type
. In both cases, the scope of ID
is the inner expression Expr
, and Type
must be a bounded integer or a scalar set.
The following function can be used to check if all elements of the boolean array a
have the value true
.
bool alltrue(bool a[5])
{
return forall (i : int[0,4]) a[i];
}
An expression sum (ID : Type) Expr
evaluates to an integer and is equal to the sum of the expressions evaluated with ID
ranging over the given type argument. Boolean or state predicates (in TCTL expressions only) are accepted but not clock constraints. The expressions must be side-effect free. The type must be a bounded integer or a scalar set.
Statistical model checking (SMC) supports the full logic of weighted metric interval temporal logic (MITL). The syntax of MITL expressions is defined by the grammar for MITLExpression
.
MITLExpression =
BExpr
| (MITLExpression && MITLExpression)
| (MITLExpression || MITLExpression)
| (MITLExpression 'U' '[' NAT ',' NAT ']' MITLExpression)
| (MITLExpression 'R' '[' NAT ',' NAT ']' MITLExpression)
| ('X' MITLExpression)
| ('<>' '[' NAT ',' NAT ']' MITLExpression)
| ('[]' '[' NAT ',' NAT ']' MITLExpression);
BExpr
See rail road diagram of entire MITLExpression syntax:
Statistical model checking (SMC) supports double precision floating point type double
. The clock variables also have floating point values in SMC. Symbolic and statistical model checking can be applied on the same model provided that double
and hybrid clock
type variables do not influencing the model logic, i.e. they cannot be used in guard and invariant constraints (but can be used in ODE expressions).
The following is the list of builtin floating point functions (mostly imported from C math library, hence the C math manual can be consulted for more details):
int abs(int)
double fabs(double)
double fmod(double x, double y)
double fma(double x, double y, double z)
double fmax(double x, double y)
double fmin(double x, double y)
double exp(double x)
double exp2(double x)
double expm1(double x)
double ln(double x)
double log(double x)
double log10(double x)
double log2(double x)
double log1p(double x)
double pow(double x, int y)
double pow(double x, double y)
double sqrt(double x)
double cbrt(double x)
double hypot(double x, double y)
double sin(double x)
double cos(double x)
double tan(double x)
double asin(double x)
double acos(double x)
double atan(double x)
double atan2(double y, double x)
double sinh(double x)
double cosh(double x)
double tanh(double x)
double asinh(double x)
double acosh(double x)
double atanh(double x)
double erf(double x)
double erfc(double x)
double tgamma(double x)
double lgamma(double x)
double ceil(double x)
double floor(double x)
double trunc(double x)
double round(double x)
int fint(double x)
double ldexp(double x, int y)
int ilogb(double x)
double logb(double x)
double nextafter(double from, double to)
double copysign(double x, double y)
bool signbit(double x)
double random(double max)
[0, max)
.double normal(double mean, double sd)
double random_normal(double mean, double sd)
double random_poisson(double lambda)
double random_arcsine(double from, double till)
[from, till]
(since Stratego-10).double random_beta(double alpha, double beta)
double random_gamma(double shape, double scale)
double random_tri(double from, double mode, double till)
[from, till]
with the given mode (since Stratego-10).double random_weibull(double shape, double scale)
A few common constants and types can be declared as follows (built-in since Stratego-10):
const int INT8_MIN = -128;
const int INT8_MAX = 127;
const int UINT8_MAX = 256;
const int INT16_MIN = -32768;
const int INT16_MAX = 32767;
const int UINT16_MAX = 65535;
const int INT32_MIN = -2147483648;
const int INT32_MAX = 2147483647;
typedef int[INT8_MIN, INT8_MAX] int8_t;
typedef int[0, UINT8_MAX] uint8_t;
typedef int[INT16_MIN, INT16_MAX] int16_t;
typedef int[0, UINT16_MAX] uint16_t;
typedef int[INT32_MIN, INT32_MAX] int32_t;
const double FLT_MIN = 1.1754943508222875079687365372222456778186655567720875e-38;
const double FLT_MAX = 340282346638528859811704183484516925440.0;
const double DBL_MIN = 2.2250738585072013830902327173324040642192159804623318e-308;
const double DBL_MAX = 1.79769313486231570814527423731704356798070567525845e+308;
const double M_PI = 3.141592653589793115997963468544185161590576171875; // pi
const double M_PI_2 = 1.5707963267948965579989817342720925807952880859375; // pi/2
const double M_PI_4 = 0.78539816339744827899949086713604629039764404296875; // pi/4
const double M_E = 2.718281828459045090795598298427648842334747314453125; // e
const double M_LOG2E = 1.442695040888963387004650940070860087871551513671875; // log_2(e)
const double M_LOG10E = 0.43429448190325181666793241674895398318767547607421875; // log_10(e)
const double M_LN2 = 0.69314718055994528622676398299518041312694549560546875; // log_e(2)
const double M_LN10 = 2.30258509299404590109361379290930926799774169921875; // log_e(10)
const double M_1_PI = 0.31830988618379069121644420192751567810773849487304688; // 1/pi
const double M_2_PI = 0.63661977236758138243288840385503135621547698974609375; // 2/pi
const double M_2_SQRTPI = 1.1283791670955125585606992899556644260883331298828125; // 2/sqrt(pi)
const double M_SQRT2 = 1.4142135623730951454746218587388284504413604736328125; // sqrt(2)
const double M_SQRT1_2 = 0.70710678118654757273731092936941422522068023681640625; // sqrt(1/2)