We present now the complete syntax of the input language of
NuSMV
. In the following, an atom
may be any sequence of
characters starting with a character in the set {A-Za-z_}
and
followed by a possibly empty sequence of characters belonging to the set
{A-Za-z0-9_\$#-}
.
A number
is any sequence of digits. A digit belongs to the set
{0-9}
.
All characters and case in a name are significant. Whitespace
characters are space (SPACE), tab (TAB) and
newline (RET). @cindex comments in NuSMV
language
Any string starting with two dashes (`--
') and
ending with a newline is a comment. Any other tokens recognized by the
parser are enclosed in quotes in the syntax expressions below.
Grammar productions enclosed in square brackets (`[]
') are
optional.
Expressions are constructed from variables, constants, and a collection of operators, including boolean connectives, integer arithmetic operators, case expressions and set expressions.
Simple expressions are expressions built only from current state variables. Simple expressions can be used to specify sets of states, e.g. the initial set of states. The syntax of simple expressions is as follows:
simple_expr :: atom ;; a symbolic constant | number ;; a numeric constant | "TRUE" ;; The boolean constant "TRUE" | "FALSE" ;; The boolean constant "FALSE" | var_id ;; a variable identifier | "(" simple_expr ")" | "!" simple_expr ;; logical not | simple_expr "&" simple_expr ;; logical and | simple_expr "|" simple_expr ;; logical or | simple_expr "->" simple_expr ;; logical implication | simple_expr "<->" simple_expr ;; logical equivalence | simple_expr "=" simple_expr ;; equality | simple_expr "<" simple_expr ;; less than | simple_expr ">" simple_expr ;; greater than | simple_expr "<=" simple_expr ;; less than or equal | simple_expr ">=" simple_expr ;; greater than or equal | simple_expr "+" simple_expr ;; integer addition | simple_expr "-" simple_expr ;; integer subtraction | simple_expr "*" simple_expr ;; integer multiplication | simple_expr "/" simple_expr ;; integer division | simple_expr "mod" simple_expr ;; integer remainder | set_simple_expr ;; a set simple_expression | case_simple_expr ;; a case expression
A var_id, (see section Identifiers) or identifier, is a symbol or
expression which identifies an object, such as a variable or a defined
symbol. Since a var_id
can be an atom
, there is a possible
ambiguity if a variable or defined symbol has the same name as a
symbolic constant. Such an ambiguity is flagged by the interpreter as an
error.
The order of parsing precedence for operators from high to low is:
*,/ +,- mod =,<,>,<=,>= ! & | ->,<->
Operators of equal precedence associate to the left. Parentheses may be used to group expressions.
A case expression has the following syntax:
case_simple_expr :: "case
" simple_expr ":" simple_expr ";" simple_expr ":" simple_expr ";" ... simple_expr ":" simple_expr ";" "esac
"
A case_simple_expr
returns the value of the first expression on
the right hand side of `:
', such that the corresponding
condition on the left hand side evaluates to 1
. Thus, if
simple_expr
on the left side is true, then the result is the
corresponding simple_expr
on the right side. If none of the
expressions on the left hand side evaluates to 1
, the result of
the case_expression
is the numeric value 1
. It is an error
for any expression on the left hand side to return a value other than
the truth values 0
or 1
.
A set expression has the following syntax:
set_elem :: atom ;; a symbolic constant | number ;; a numeric constant set_expr :: "{" set_elem "," ... "," set_elem "}" ;; set definition | simple_expr "in
" simple_expr ;; set inclusion predicate | simple_expr "union
" simple_expr ;; set union
A set can be defined by enumerating its elements inside
curly braces `{...}
'. The elements of the set can be
numbers or symbolic constants. The inclusion operator `in
'
tests a value for membership in a set. The union operator
`union
' takes the union of two sets. If either argument is a
number or a symbolic value instead of a set, it is coerced to a
singleton set.
While simple expressions can represent sets of states, next expressions relate current and next state variables to express transitions in the Kripke structure. The structure of next expressions is similar to the structure of simple expressions (See section Simple Expressions). The difference is that next expression allow to refer to next state variables. The grammar is depicted below.
next_expr :: atom ;; a symbolic constant | number ;; a numeric constant | "TRUE" ;; The boolean constant "TRUE" | "FALSE" ;; The boolean constant "FALSE" | var_id ;; a variable identifier | "(" next_expr ")" | "next" "(" simple_expr ")" ;; next value of an "expression" | "!" next_expr ;; logical not | next_expr "&" next_expr ;; logical and | next_expr "|" next_expr ;; logical or | next_expr "->" next_expr ;; logical implication | next_expr "<->" next_expr ;; logical equivalence | next_expr "=" next_expr ;; equality | next_expr "<" next_expr ;; less than | next_expr ">" next_expr ;; greater than | next_expr "<=" next_expr ;; less than or equal | next_expr ">=" next_expr ;; greater than or equal | next_expr "+" next_expr ;; integer addition | next_expr "-" next_expr ;; integer subtraction | next_expr "*" next_expr ;; integer multiplication | next_expr "/" next_expr ;; integer division | next_expr "mod" next_expr ;; integer remainder | set_next_expr ;; a set next_expression | case_next_expr ;; a case expression
set_next_expr
and case_next_expr
are the same
as set_simple_expr
(see section Set Expressions) and
case_simple_expr
(see section Case Expressions) respectively, with the
replacement of "simple
" with "next
".
The only additional production is "next" "(" simple_expr ")"
,
which allows to "shift" all the variables in simple_expr
to the
next state. The next
operator distributes on every
operator. For instance, the formula next((A & B) | C)
is a
shorthand for the formula (next(A) & next(B)) | next(C)
. It is
an error if in the scope of the next
operator occurs another
next
operator.
A state of the model is an assignment of values to a set of state variables. These variables (and also instances of modules) are declared by the notation:
var_declaration :: "VAR
"
atom ":" type ";"
atom ":" type ";"
...
The type associated with a variable declaration can be either a boolean, a scalar, a user defined module, or an array of any of these (including arrays of arrays).
A type specifier has the syntax:
type ::boolean
| "{" val "," val "," ... val "}" | number ".." number | "array
" number ".." number "of
" type | atom [ "(" simple_expr "," simple_expr "," ... ")" ] | "process
" atom [ "(" simple_expr "," ... "," simple_expr ")" ] val :: atom | number
A variable of type boolean
can take on the numerical values
0
and 1
(representing false and true, respectively). In
the case of a list of values enclosed in quotes (where atoms are taken
to be symbolic constants), the variable is a scalar which take any of
these values. In the case of an array
declaration, the first
simple_expr
is the lower bound on the subscript and the second
simple_expr
is the upper bound. Both of these expressions must
evaluate to integer constants. Finally, an atom optionally followed by
a list of expressions in parentheses indicates an instance of module
atom (See section MODULE
declarations). The keyword causes the module to
be instantiated as an asynchronous process (See section Processes).
A state of the model is an assignment of values to a set of state variables. These variables (and also instances of modules) are declared by the notation:
ivar_declaration :: "IVAR
"
atom ":" type ";"
atom ":" type ";"
...
The type associated with a variable declaration can be either a boolean, a scalar, a user defined module, or an array of any of these (including arrays of arrays) (See section State Variables).
ASSIGN
declarationsAn assignment has the form:
assign_declaration :: "ASSIGN
"
assign_body ";"
assign_body ";"
...
assign_body ::
atom ":=" simple_expr ;; normal assignment
| "init" "(" atom ")" ":=" simple_expr ;; init assignment
| "next" "(" atom ")" ":=" next_expr ;; next assignment
On the left hand side of the assignment, atom denotes the current value of a variable, `init(atom)' denotes its initial value, and `next(atom)' denotes its value in the next state. If the expression on the right hand side evaluates to an integer or symbolic constant, the assignment simply means that the left hand side is equal to the right hand side. On the other hand, if the expression evaluates to a set, then the assignment means that the left hand side is contained in that set. It is an error if the value of the expression is not contained in the range of the variable on the left hand side.
In order for a program to be implementable, there must be some order in which the assignments can be executed such that no variable is assigned after its value is referenced. This is not the case if there is a circular dependency among the assignments in any given process. Hence, such a condition is an error. In it is an error for a variable to be assigned a value more than once at any given time. More precisely, it is an error if:
TRANS
declarations
The transition relation R of the model is a set of current state/next
state pairs. Whether or not a given pair is in this set is determined by
a boolean valued expression T, introduced by the `TRANS' keyword. The
syntax of a TRANS
declaration is:
trans_declaration :: "TRANS
" trans_expr
trans_expr :: next_expr
It is an error for the expression to yield any value other than 0
or 1
. If there is more than one TRANS
declaration, the
transition relation is the conjunction of all of TRANS
declarations.
INIT
declarations
The set of initial states of the model is determined by a boolean expression
under the `INIT' keyword. The syntax of a INIT
declaration is:
init_declaration :: "INIT
" init_expr
init_expr :: simple_expr
It is an error for the expression to contain the next()
operator,
or to yield any value other than 0
or 1
. If there is more
than one INIT
declaration, the initial set is the conjunction of
all of the INIT
declarations.
INVAR
declarations
The set of invariant states (i.e. the analogous of normal assignments,
as described in section ASSIGN
declarations) can be specified using a
boolean expression under the `INVAR' keyword. The syntax of a
INVAR
declaration is:
invar_declaration :: simple_expr
It is an error for the expression to contain the next()
operator,
or to yield any value other than 0
or 1
. If there is more
than one INVAR
declaration, the invariant set is the conjunction of
all of the INVAR
declarations.
DEFINE
declarationsIn order to make descriptions more concise, a symbol can be associated with a commonly expression. The syntax for this kind of declaration is:
define_declaration :: "DEFINE
"
atom ":=" simple_expr ";"
atom ":=" simple_expr ";"
...
atom ":=" simple_expr ";"
Whenever an identifier referring to the symbol on the left hand side of
the `:=
' in a DEFINE
occurs in an expression, it is
replaced by the expression on the right hand side. The expression on the
right hand side is always evaluated in its context, however
(see section MODULE
declarations for an explanation of contexts). Forward
references to defined symbols are allowed, but circular definitions are not
allowed, and result in an error.
Defined symbols cannot be given values non-deterministically. Another difference between defined symbols and variables is that while variables are statically typed, definitions are not.
ISA
declarations
There are cases in which some parts of a module could be shared among
different modules, or could be used as a module themselves. In
NuSMV
it is possible to declare the common parts as separate
modules, and then use the ISA
declaration to import the common
parts inside a module declaration.
The syntax of an ISA
declaration is as follows:
isa_declaration :: "ISA
" atom
where atom
must be the name of a declared module.
The ISA
declaration can be thought as a simple macro expansion
command, because the body of the module referenced by an ISA
command is replaced to the ISA
declaration.
MODULE
declarationsA module is an encapsulated collection of declarations. Once defined, a module can be reused as many times as necessary. Modules can also be so that each instance of a module can refer to different data values. A module can contain instances of other modules, allowing a structural hierarchy to be built.
The syntax of a module declaration is as follows.
module ::
"MODULE
" atom [ "(" atom "," atom "," ... atom ")" ]
[ var_declaration ]
[ ivar_declaration ]
[ assign_declaration ]
[ trans_declaration ]
[ init_declaration ]
[ invar_declaration ]
[ spec_declaration ]
[ checkinvar_declaration ]
[ ltlspec_declaration ]
[ compute_declaration ]
[ fairness_declaration ]
[ define_declaration ]
[ isa_declaration ]
The atom immediately following the keyword "MODULE
" is
the name associated with the module. Module names are drawn from a
separate name space from other names in the program, and hence may
clash with names of variables and definitions. The optional list of
atoms in parentheses are the formal parameters of the module. Whenever
these parameters occur in expressions within the module, they are
replaced by the actual parameters which are supplied when the module
is instantiated (see below).
An instance of a module is created using the VAR
declaration (see section State Variables). This declaration supplies a name
for the instance, and also a list of actual parameters, which are
assigned to the formal parameters in the module definition. An actual
parameter can be any legal expression. It is an error if the number of
actual parameters is different from the number of formal
parameters. The semantic of module instantiation is similar to
call-by-reference. For example, in the following program fragment:
MODULE main ... VAR a : boolean; b : foo(a); ... MODULE foo(x) ASSIGN x := 1;
the variable a
is assigned the value 1
. This
distinguishes the call-by-reference mechanism from a call-by-value
scheme.
Now consider the following program:
MODULE main ... DEFINE a := 0; VAR b : bar(a); ... MODULE bar(x) DEFINE a := 1; y := x;
In this program, the value of y
is 0
. On the
other hand, using a call-by-name mechanism, the value of y
would
be 1
, since a
would be substituted as an expression for
x
.
Forward references to module names are allowed, but circular references are not, and result in an error.
An id
, or identifier, is an expression which references an
object. Objects are instances of modules, variables, and defined
symbols. The syntax of an identifier is as follows.
id :: atom | "self" | id "." atom | id "[" simple_expr "]"
An atom identifies the object of that name as defined in a
VAR
or DEFINE
declaration. If a
identifies an
instance of a module, then the expression `a.b' identifies
the component object named `b' of instance `a'. This
is precisely analogous to accessing a component of a structured data
type. Note that an actual parameter of module `a' can
identify another module instance `b', allowing `a'
to access components of `b', as in the following example:
... VAR a : foo(b); b : bar(a); ... MODULE foo(x) DEFINE c := x.p | x.q; MODULE bar(x) VAR p : boolean; q : boolean;
Here, the value of `c' is the logical or of `p' and `q'.
If `a' identifies an array, the expression `a[b]' identifies element `b' of array `a'. It is an error for the expression `b' to evaluate to a number outside the subscript bounds of array `a', or to a symbolic value.
It is possible to refer the name the current module has been
instantiated to by using the self
builtin identifier.
MODULE element(above, below, token) VAR Token : boolean; ASSIGN init(Token) := token; next(Token) := token-in; DEFINE above.token-in := Token; grant-out := below.grant-out; MODULE cell VAR e2 : element(self, e1, 0); e1 : element(e1 , self, 1); DEFINE e1.token-in := token-in; grant-out := grant-in & !e1.grant-out; MODULE main VAR c1 : cell;
In this example the name the cell
module has been instantiated to
is passed to the submodule element
. In the main
module,
declaring c1
to be an instance of module cell
and defining
above.token-in
in module e2
, really amounts to defining
the symbol c1.token-in
. When you, in the cell
module,
declare e1
to be an instance of module element
, and you
define grant-out
in module e1
to be
below.grant-out
, you are really defining it to be the symbol
c1.grant-out
.
main
module
The syntax of a NuSMV
program is:
program :: module_1 module_2 ... module_n
There must be one module with the name main
and no formal parameters.
The module main
is the one evaluated by the interpreter.
Processes are used to model interleaving concurrency. A process
is a module which is instantiated using the keyword `process'
(see section State Variables). The program executes a step by
non-deterministically choosing a process, then executing all of the
assignment statements in that process in parallel. It is implicit that
if a given variable is not assigned by the process, then its value
remains unchanged. Each instance of a process has a special boolean
variable associated with it called running
.
The value of this variable is 1
if and only if the process
instance is currently selected for execution.
A process may run only when its parent is running. In addition no two
processes with the same parents may be running at the same time.
FAIRNESS
declarationsA fairness constraint is a CTL formula which is assumed to be true infinitely often in all fair execution paths. When evaluating specifications, the model checker considers path quantifiers to apply only to fair paths. Fairness constraints are declared using the following syntax:
fairness_declaration :: "FAIRNESS
" fair_expr
fair_expr :: ctl_expr
A path is considered fair if and only if all constraints declared in this manner are true infinitely often.
The specifications to be checked on the Kripke structure can be expressed in the temporal logics CTL and LTL. Furthermore, it is possible to analyze quantitative characteristics of the model by specifying computations. Specifications can be positioned within modules. The specification is processed in such a way to rename the variables according to the containing context.
Specifications are evaluated by NuSMV
. In case of a CTL or LTL
false specification, a counterexample is printed out.
A CTL specification is given as a formula in the temporal logic CTL, introduced by the keyword `SPEC'. The syntax of this declaration is:
spec_declaration :: "SPEC
" spec_expr
spec_expr :: ctl_expr
The syntax of CTL formulas recognized by the NuSMV
parser is
as follows:
ctl_expr :: simple_expr ;; a simple boolean expression | "(" ctl_expr ")" | "!" ctl_expr ;; logical not | ctl_expr "&" ctl_expr ;; logical and | ctl_expr "|" ctl_expr ;; logical or | ctl_expr "->" ctl_expr ;; logical implies | ctl_expr "<->" ctl_expr ;; logical equivalence | "EG" ctl_expr ;; exists globally | "EX" ctl_expr ;; exists next state | "EF" ctl_expr ;; exists finally | "AG" ctl_expr ;; forall globally | "AX" ctl_expr ;; forall next state | "AF" ctl_expr ;; forall finally | "E" "[" ctl_expr "U" ctl_expr "]" ;; exists until | "A" "[" ctl_expr "U" ctl_expr "]" ;; forall until
It is an error for an expressions in a CTL formula to contain a
`next()' operator or to have non-boolean components, i.e.
subformulas which evaluate to a value other than 0
or 1
.
Also, simple_expr
in CTL formulas can not contain case statements.
It is also possible to specify invariants, i.e. propositional formulas which must hold invariantly in the model. The corresponding command is `INVARSPEC', with syntax:
checkinvar_declaration :: "INVARSPEC
" simple_expr ";"
This statement corresponds to
SPEC AG simple_expr ";"
but can be checked by a specialized algorithm during reachability analysis.
NuSMV
allows for specifications expressed in LTL. Model checking
of LTL specifications is based on the construction of a tableau
corresponding to the LTL formula and on CTL model checking, along the
lines described in [CGH97]. This construction is completely
transparent to the user.
LTL specifications are introduced by the keyword `LTLSPEC'. The syntax of this declaration is:
ltlspec_declaration :: "LTLSPEC
" ltl_expr
where
ltl_expr :: simple_expr ;; a simple boolean expression | "(" ltl_expr ")" | "!" ltl_expr ;; logical not | ltl_expr "&" ltl_expr ;; logical and | ltl_expr "|" ltl_expr ;; logical or | ltl_expr "->" ltl_expr ;; logical implies | ltl_expr "<->" ltl_expr ;; logical equivalence | "G" ltl_expr ;; globally | "X" ltl_expr ;; next state | "F" ltl_expr ;; finally | ltl_expr "U" ltl_expr ;; until
As for CTL formulas, simple_expr
can not contain case statements.
The counterexample generated to show the falsity of a LTL specification
may contain state variables which have been introduced by the tableau
construction procedure. Currently it is not possible to navigate the
counterexamples for LTL formulas.
NuSMV
allows for Real Time CTL specifications
[EMSS90]. NuSMV
assumes that each transition takes unit
time for execution. RTCTL extends the syntax of CTL path expressions
with the following bounded modalities:
rtctl_expr :: ctl_expr | "EBF" range rtctl_expr | "ABF" range rtctl_expr | "EBG" range rtctl_expr | "ABG" range rtctl_expr | "A" "[" rtctl_expr "BU" range rtctl_expr "]" | "E" "[" rtctl_expr "BU" range rtctl_expr "]" range :: number ".." number"
Intuitively, in the formula E [ a BU m..n b ]
m (n, resp.) represents the minimum (maximum) number of
permitted transition along a path of a structure before the eventuality
holds.
Real time CTL specifications can be defined with the following syntax, which extends the syntax for CTL specifications.
spec_declaration :: "SPEC
" rtctl_expr
With the `COMPUTE' statement, it is also possible to compute quantitative information on the Kripke structure. In particular, it is possible to compute the exact bound on the delay between two specified events, expressed as CTL formulas. The syntax is the following:
compute_declaration :: "COMPUTE
" compute_expr
where
compute_expr :: "MIN" "[" rtctl_expr "," rtctl_expr "]" | "MAX" "[" rtctl_expr "," rtctl_expr "]"
MIN [start , final]
computes the set of
states reachable from start. If at any point, we encounter a state
satisfying final, we return the number of steps taken to reach the
state. If a fixed point is reached and no states intersect final
then infinity is returned.
MAX [start , final]
returns the length of
the longest path from a state in start to a state in final. If
there exists an infinite path beginning in a state in start that
never reaches a state in final, then infinity is returned.
Go to the first, previous, next, last section, table of contents.