SNL Reference for Version 2.2
=============================

This chapter gives a detailed reference for the SNL syntax and semantics and
for the built-in functions of SNL in version 2.2. The `documentation for
version 2.1`_ is and will remain available, too.

Formal syntax is given in `BNF`_. Multiple rules for the same
`nonterminal symbol`_ mean that any of the given rules may apply. `Terminal
symbols`_ are enclosed in double quotes.

.. _BNF: http://en.wikipedia.org/wiki/Backus-Naur_Form
.. _nonterminal symbol: http://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols#Nonterminal_symbols
.. _terminal symbols: http://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols#Terminal_symbols
.. _documentation for version 2.1: http://www-csr.bessy.de/control/SoftDist/sequencer-2-1/Reference.html

Lexical Syntax
--------------

The lexical syntax is not specified formally in this document, since it is
almost identical to that of C. There are only two exceptions:

* `Embedded C Code`_, and

* more reserved words.

We use an informal notation with explanatory text in agle brackets standing
in for the formal definition.

Comments
^^^^^^^^

.. productionlist::
   comment: "/*" <anything> "*/"
   comment: "//" <anything until end of line>

C-style comments may be placed anywhere in the program. They are
treated as white space. As in C, comments cannot be nested.

.. versionadded:: 2.2

C++ style comments are allowed, too.

Identifiers
^^^^^^^^^^^

.. productionlist::
   identifier: <same as in C>

Identifiers follow the same rules as in C. They are used for variables
(including foreign variables and event flags), the program name,
states, state sets, and options.

Literals
^^^^^^^^

.. productionlist::
   integer_literal: <same as in C>
   floating_point_literal: <same as in C>
   string_literal: <same as in C>

The lexical syntax of identifiers, as well as numeric and string
literals is exactly as in C, including automatic string
concatenation, character literals, and octal, decimal, and
hexadecimal integer literals.

Embedded C Code
^^^^^^^^^^^^^^^

.. productionlist::
   embedded_c_code: "%{" <anything> "}%"
   embedded_c_code: "%%" <anything> "\n"

A sequence of characters enclosed between "%{" and "}%" is used
literally and without further parsing as if it were a complete
declaration or statement, depending on where it appears.

A sequence of characters enclosed between "%%" and the next line ending
is treated similarly, except that it is stripped of leading and
trailing whitespace and inserted in the output with the current
indentation.

Note that embedded C code fragments are *not* allowed to appear in
expressions.

See `Escape to C Code` for examples and rationale.

Embedded C code fragments are the cause for one of the two conflicts in the
grammar. The reason is that the parser cannot always decide whether such a
fragment is a declaration or a statement. (The other conflict is due to the
infamous "dangling else".)

Line Markers
^^^^^^^^^^^^

.. productionlist::
   line_marker: "#" line_number "\n"
   line_marker: "#" line_number file_name "\n"
   line_number: <non-empty sequence of decimals>
   file_name: <like string_literal, without automatic string concatenation>

Line markers are interpreted exactly as in C, i.e. they indicate
that the following symbols are really located in the given source file (if
any) at the given line.

.. note::

   `line_number` may only contain decimal numbers, and
   `file_name` must be a single string (no automatic string
   concatenation).

Line markers are typically generated by preprocessors, such as `cpp`.

Program
-------

.. productionlist::
   program: "program" `identifier` `program_param` `initial_defns` `entry` `state_sets` `exit` `final_defns`

This is the overall structure of an SNL program. After the keyword
"program" comes the name of the program, followed by an optional program
parameter, initial global definitions, an optional entry block, the state sets, an
optional exit block, and then again global definitions.

Program Name and Parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^

The program name is an identifier. It is used as the name of the global
variable which contains or points to all the program data structures
(the address of this global variable is passed to the ``seq`` function when
creating the run-time sequencer). It is also used as the base for the state
set thread names unless overridden via the *name* parameter (see `run
time parameters`).

.. productionlist::
   program_param: "(" `string` ")"
   program_param: 

The program name may be followed by an optional string enclosed in
parentheses. The string content must be a list of comma-separated
parameters in the same form as if specified on invocation (see
`run time parameters`).

.. note::

   Parameters specified on invocation override those specified in the
   program.

.. productionlist::
   initial_defns: `initial_defns` `initial_defn`
   initial_defns: 
   initial_defn: `assign`
   initial_defn: `monitor`
   initial_defn: `sync`
   initial_defn: `syncq`
   initial_defn: `declaration`
   initial_defn: `option`
   initial_defn: `funcdef`
   initial_defn: `structdef`
   initial_defn: `c_code`

.. productionlist::
   final_defns: `final_defns` `final_defn`
   final_defns: 
   final_defn: `funcdef`
   final_defn: `structdef`
   final_defn: `c_code`

Global (top-level) definitions, see `Definitions`_ for details. Note that
in the final definition section only function definitions and literal code
blocks are allowed.

.. _GlobalEntryExit:

Global Entry and Exit Blocks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. productionlist::
   entry: "entry" `block`
   entry: 
   exit: "exit" `block`
   exit: 

An SNL program may specify optional entry code to run prior to running the
state sets, and exit code to run after all state sets have exited. Both are
run in the context of the first state set thread.

(Global entry and exit blocks should not be confused with
`state entry and exit blocks`, which have the same syntax but are
executed at each `transition` from/to a new state.)

The entry or exit code is a regular SNL code block and thus can contain local
variable declarations. Global entry code is always executed after initiating
connections to (named) PVs, and exit code is executed before connections are
shut down. Furthermore, if the global `+c` option is in effect, the
entry code is executed only after all channels are connected and monitored
channels have received their first monitor event. All built-in PV functions
can be expected to work in global entry and exit blocks.

Note that global entry and exit blocks operate as if executed as part of the
first state set. This means that in `safe mode` changes to global
variables made from inside the entry block are not visible to state sets
other than the first unless explicitly communicated (e.g. by calling pvPut).
The fact that the first state set plays a special role is a historical
accident, rather than conscious design. I might be tempted to redesign this
aspect in a future version, for instance by giving entry and exit blocks
their own dedicated virtual state set.


Final C Code Block
^^^^^^^^^^^^^^^^^^

.. productionlist::
   c_code: `embedded_c_code`

Any number of embedded C code blocks may appear after state sets and the
optional exit block. See `Escape to C Code`.


.. _Definitions:

Definitions
-----------

Function Definitions
^^^^^^^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. productionlist::
   funcdef: `basetype` `declarator` `block`

Function definitions are very much like those in C.

The most important difference is that functions declared in SNL get passed
certain implicit parameters. This makes it possible to call built-in
functions as if the code were a normal action block. Note, however, that
there is currently no way to *pass* channel ("assigned") variables to SNL
functions in such a way that you can call built-in functions on them. The
"assigned" status of such a variable is lost and it gets passes to the
function as a normal C value. This means you can call built-in functions
that expect such a variable only if the variable was declared at the
top-level. Lifting this limitation is planned for a future release.

Another difference from C is that SNL functions automatically scope over the
whole program, no matter where they are defined. It is not necessary (but
allowed) to repeat the function declaration (without the defining code
block, commonly referred to as a "function prototype") at the top of the
program.


Type Definitions
^^^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. productionlist::
   structdef: "struct" `identifier` `members` ";"
   members: "{" `member_decls` "}"
   member_decls: `member_decl`
   member_decls: `member_decls` `member_decl`
   member_decl: `basetype` `declarator` ";"
   member_decl: `c_code`

Type definitions are currently limited to struct definitions and may appear
only at the top level. They are translated to the equivalent struct
definition in C. Differences to C are that bit fields are not supported, and
that member types are limited to SNL types (built-in or user defined).


Variable Declarations
^^^^^^^^^^^^^^^^^^^^^

.. productionlist::
   declaration: `basetype` `init_declarators` ";"
   init_declarators: `init_declarator`
   init_declarators: `init_declarators` "," `init_declarator`
   init_declarator: `declarator`
   init_declarator: `declarator` "=" `init_expr`
   declarator: `variable`
   declarator: "(" `declarator` ")"
   declarator: `declarator` `subscript`
   declarator: `declarator` "(" `param_decls` ")"
   declarator: "*" `declarator`
   declarator: "const" `declarator`
   variable: `identifier`
   init_expr: "{" `init_exprs` "}"
   init_expr: "(" `type_expr` ")" "{" `init_exprs` "}"
   init_expr: `expr`
   init_exprs: `init_exprs` "," `init_expr`
   init_exprs: `init_expr`
   init_exprs: 

.. versionadded:: 2.1

You can declare more than one variable in a single declaration (comma
separated) and add pointer and array markers (subscripts) ad libitum as
well as initializers.

.. versionchanged:: 2.2

   Function declarations and initializers with type casts.

As in C, some combinations of type operators are not allowed, for
instance because they are redundant (like declaring a function or array
`const`_). This is not specified in the SNL grammar. There are some
checks that warn about obviously wrong combinations, but as with type
checking, most of the work is off-loaded to the C compiler.

The also remain some limitations:

* arrays must have a defined size: the expression inside the subscript
  brackets is not optional as in C; it must also be an integer literal,
  not a constant expression as in C.
* you cannot declare new types or type synonyms
* the "volatile" type qualifier is not supported
* neither are storage specifiers ("static", "extern", "auto", register")
* only certain base types are allowed, see `below <Types>`)

Some of these restrictions may be lifted in future versions.

const
~~~~~

.. versionadded:: 2.2

As in C, declarations may involve the "const" keyword to make parts of
the thing declared constant i.e. immutable. The SNL syntax treats
"const" as a prefix type operator, exactly like the pointer "*"
operator. 

.. note::

   This correctly specifies a large subset of the C syntax, but there
   are some limitations. For instance, in C you can write ::

      const int x;   /* attention: invalid in SNL */

   as an alternative notation for ::

      int const x;

   This is not allowed in SNL to keep the parser simple and orthogonal.

Otherwise, "const" behaves like in C. For instance, ::

   char const *p;

declares a mutable pointer to constant char (possibly more than one),
while ::

   char *const p;

declares a constant pointer to one or more mutable chars, and ::

   char *const *p;

a constant pointer to constant chars.

As in C, declarations (and similarly `Type Expressions`_) should be read
inside out, starting with the identifier and working outwards,
respecting the precedences (postfix binds stronger than prefix). This
works for "const" exactly as for array subscripts, pointers, and
parameter lists.


.. _foreign entities:

Foreign Declarations
~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 2.1

.. deprecated:: 2.2

.. productionlist::
   declaration: "foreign" `variables` ";"
   variables: `variable`
   variables: `variables` "," `variable`

Foreign declarations are used to let the SNL compiler know about the
existence of C variables or C preprocessor macros (without parameters)
that have been  defined outside the SNL program or in escaped C code. No
warning will be issued if such a variable or macro is used in the
program even if warnings are enabled.

.. versionchanged:: 2.2

It is no longer needed to declare struct or union members as foreign
entities, since the parser no longer confuses them with variables. See
section `Postfix Operators`_ below.

Also, warnings for undefined entities are now disabled by default. You
can use the "extra warnings" option `+W` to enable them. In
contrast to older versions, this will also warn about foreign functions
when called from SNL code. Note that enabling extra warnings is normally
not needed: if a variable or function is actually undefined in the
generated C code, the C compiler will issue a warning (or an error)
anyway.


.. _Types:

Basic Types
~~~~~~~~~~~

These are the allowed base types, of which you can declare variables,
or pointers or arrays etc.

.. productionlist::
   basetype: `prim_type`
   basetype: "evflag"

.. versionadded:: 2.2

.. productionlist::
   basetype: "enum" `identifier`
   basetype: "struct" `identifier`
   basetype: "union" `identifier`
   basetype: "typename" `identifier`
   basetype: "void"

You can use enumerations, structs, unions and typedefs in declarations
and type expressions. The "void" type is also allowed, subject to the
same restrictions as in C.

.. note::

   Any use of a type alias (defined using "typedef" in C) *must*
   be preceded by the "typename" keyword. This keyword has been borrowed
   from C++, but in contrast to C++ using "typename" is *not* optional.

.. todo::

   Add some type ("enumeration"?) to make it easier and more robust to
   interact with process variables of type ``DBF_ENUM`` or ``DBF_MENU``.

.. productionlist::
   prim_type: "char"
   prim_type: "short"
   prim_type: "int"
   prim_type: "long"
   prim_type: "unsigned" "char"
   prim_type: "unsigned" "short"
   prim_type: "unsigned" "int"
   prim_type: "unsigned" "long"
   prim_type: "int8_t"
   prim_type: "uint8_t"
   prim_type: "int16_t"
   prim_type: "uint16_t"
   prim_type: "int32_t"
   prim_type: "uint32_t"
   prim_type: "float"
   prim_type: "double"
   prim_type: "string"

Primitive types are those base types that correspond directly to some
(scalar) EPICS type. They have the same semantics as in C, except "string"
that does not exist in C. See subsection `Strings`_ below.

.. versionadded:: 2.1

Since the standard numeric types in C have implementation defined size, fixed
size integral types that correspond to the ones in the C99 standard have
been added. However, int64_t and uint64_t are not supported because
primitive types must correspond to the primitive EPICS types, and EPICS does
not yet support 64 bit integers.

Type Expressions
~~~~~~~~~~~~~~~~

.. versionadded:: 2.2

.. productionlist::
   type_expr: `basetype`
   type_expr: `basetype` `abs_decl`
   abs_decl: "(" `abs_decl` ")"
   abs_decl: `subscript`
   abs_decl: `abs_decl` `subscript`
   abs_decl: "(" `param_decls` ")"
   abs_decl: `abs_decl` "(" `param_decls` ")"
   abs_decl: "*"
   abs_decl: "*" `abs_decl`
   abs_decl: "const"
   abs_decl: "const" `abs_decl`
   param_decls: 
   param_decls: `param_decl`
   param_decls: `param_decls` "," `param_decl`
   param_decl: `basetype` `declarator`
   param_decl: `type_expr`

Type expressions closely follow declaration syntax just as in C. They
are used for parameter declarations as well as type casts and the
special sizeof operator (see `Prefix Operators`_).

Using "const" in type expressions is subject to the same restrictions as
in declarations (see `Variable Declarations`_).

Strings
~~~~~~~

The type ``string`` is defined in C as::

   typedef char string[MAX_STRING_SIZE];

where ``MAX_STRING_SIZE`` is a constant defined in one of the
included header files from EPICS base. I know of no EPICS version
where it is different from 40, but to be on the safe side I
recommend not to rely too much on the numeric value. (You can use
``sizeof(string)`` in SNL expressions.)

.. note::

   In contrast to C, in SNL ``string s`` is *not* a synonym for
   ``char s[MAX_STRING_SIZE]``, since variables of type ``string``
   are treated differently when it comes to interacting with PVs: the
   former gets requested with type DBR_STRING and a count of one, while
   the latter gets requested with type DBR_CHAR and a count of
   ``MAX_STRING_SIZE``.

Event Flags
~~~~~~~~~~~

Event flags are values of an abstract data type with four operations
defined on them: `efSet`, `efClear`, `efTest`,
and `efTestAndClear`.

An event flag ``e`` can act as a binary semaphore, allowing exactly one
state set to continue, if ``when(efTestAndClear(e))`` is used to wait
and ``efSet(e)`` to signal. Event flags can be coupled to changes of a
PV using the `sync` clause, so that the flag gets set whenever
an event happens.

You cannot declare arrays of or pointers to event flags, since
event flags are not translated to C variables in your program.

See `EventFlags`.

.. _variable scope:

Variable Scope and Life Time
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Variables are statically scoped, i.e. they are visible and accessible to the
program only inside the smallest `block` enclosing the declaration;
if declared outside of any block, i.e. at the top level, they are visible
everywhere in the program.

This is quite similar to C and other statically scoped programming
languages. However, there are two differences to C:

First, global variables are always local to the program, similar to
variables that have been declared "static" in C.

Second, in C, a variable's life time is defined by its scope: when the scope
is left, the variable becomes undefined. That is, life time (also called
dynamic scope) follows static scope. This is generally the same in SNL, with
two notable *exceptions*: A variable declared local to a state set or local
to a state continues to exist *as long as the program runs*, just as if it
were declared at the top level, i.e. before any state sets. We say that such
variables have  *global life time*.

Only variables with global life time can be assigned to a process variable,
monitored, synced etc. If they have an initializer, they will be initialized
only once when the program starts.

Variables declared local to an action block or a compound statement behave
exactly as in C. They can *not* be assigned to a PV; and (if they have an
initializer) they become re-initialized each time the block is entered.

The rationale for this is that, while bringing a normal variable into and
out of scope is a very cheap operation, establishing a connection to a PV is
not.

Variable Initialization
^^^^^^^^^^^^^^^^^^^^^^^

Initializers for variables of global life time (i.e. globals as well as
state set and state local ones) must respect the usual C rules for static
variable initializers. Particularly, they must be *constant expressions* in
the C sense, i.e. calculable at compile time, which also means they must not
refer to other variables.

Variables of global life time are initialized before *any* other code is
executed.

Initializers for other variables (i.e. those with local lifetime) behave
exactly as regular local C variables. Particularly, an initializer for such
a variable may refer to other variables that have been declared (and,
possibly, initialized) in an outer scope, and also to those that have
previously been declared (and, possibly, initialized) in the same scope.

Process Variables
^^^^^^^^^^^^^^^^^

The syntactic constructs in this section allow program variables to be
connected to externally defined process variables.

assign
~~~~~~

.. productionlist::
   assign: "assign" `variable` `to` `string` ";"
   assign: "assign" `variable` `subscript` `to` `string` ";"
   assign: "assign" `variable` `to` "{" `strings` "}" ";"
   assign: "assign" `variable` ";"
   to: "to"
   to: 
   strings: `strings` "," `string`
   strings: `string`
   strings: 
   subscript: "[" `integer_literal` "]"

This assigns or connects program variables to named or anonymous process
variables.

There are four variants of the `assign` statement. The first one
assigns a (scalar or array) variable to a single process variable.
The second one assigns a single element of an array variable to a
single process variable. The third one assigns elements of an array
variable to separate process variables.

For the third variant, if the number of PV names does not match the number
of elements in the array, then the rule is that

* missing PV names default to "", and

* excess PV names are discarded.

.. versionadded:: 2.1

The fourth variant serves as an abbreviation for
the first variant, in the special case where the PV name is empty (``""``).

Assigned variables must be of global life time, see
`variable scope`. Assigned variables, or separately assigned
elements of an array, can be used as argument to built-in ``pvXXX``
procedures (see `Built-in Functions`_). This is the primary means of
interacting with process variables from within an SNL program.

Only certain types of variables can be assigned to a PV: allowed
are numeric types (char, short, int, long, and their unsigned variants)
and strings (these are sometimes referred to as *scalar types*), as well
as one or two dimensional arrays of these.

Process variable names are subject to *parameter expansion*: substrings
of the form ::

    {parametername}

(i.e. an identifier enclosed in curly braces) are expanded to the value of
the program parameter *if* a corresponding parameter is defined (either
inside the program or as an extra argument on invocation, see `seq`);
otherwise no expansion takes place.

If the process variable name (after expansion) is an empty string, then no
actual assignment to any process variable is performed, but the variable
is marked for potential (dynamic) assignment with `pvAssign`.

.. note:: An `assign` clause using an empty string for the PV name is
   interpreted differently in `safe mode`, see `anonymous pvs`.

An array variable assigned wholesale to one process variable (using
the first syntactic variant above) or an element of a
two-dimensional variable assigned to an array process variable
(using the second syntactic variant) will use either the length of
the array or the native count for the underlying process
variable, whichever is smaller, when communicating with the
underlying process variable. The native count is determined when a
connection is established. For anonymous PVs, the length of the array is
used.

Pointer types may not be assigned to process variables.


monitor
~~~~~~~

.. productionlist::
   monitor: "monitor" `variable` `opt_subscript` ";"
   opt_subscript: `subscript`
   opt_subscript: 

This sets up a monitor for an assigned variable or array element.

Monitored variables are automatically updated whenever the underlying
process variable changes its value. Note, however, that this depends on
the configuration of the underlying PV: some PVs post an update event
only if the value changes by at least a certain amount. Also, events may
be posted, even if no actual change happens, i.e. the value remains the
same. The details can be found in the `EPICS Record Reference Manual`_.

.. _EPICS Record Reference Manual: http://www.aps.anl.gov/epics/wiki/index.php/RRM_3-14


sync
~~~~

.. productionlist::
   sync: "sync" `variable` `opt_subscript` `to` `event_flag` ";"
   event_flag: `identifier`

This declares a variable to be synchronized with an event flag.

When a monitor is posted on any of the process variables associated
with the event flag (and these are `monitor`\ed), or when an
asynchronous get or put operation completes, the corresponding event
flag is set.

The variable must be `assign`\ed and `monitor`\ed.
A variable can be mentioned in at most one sync clause, but an event
flag may appear in more than one such clause. The variable may be an
array, and as such may be associated with multiple process variables.

.. versionchanged:: 2.1

*  It is now allowed to sync an event flag to more than one variable.
*  There is now a run-time equivalent to the sync clause, see the
   built-in function `pvSync`.

syncQ
~~~~~

.. productionlist::
   syncq: "syncq" `variable` `opt_subscript` `to` `event_flag` `syncq_size` ";"
   syncq: "syncq" `variable` `opt_subscript` `syncq_size` ";"
   syncq_size: `integer_literal`
   syncq_size: 

This declares a variable to be queued.

When a monitor is posted on any of the process variables associated
with the given program variable, the new value is written to the end
of the queue. If the queue is already full, the last (youngest) entry
is overwritten. The `pvGetQ` function reads items from the
queue.

The variable must be `assign`\ed and `monitor`\ed.
Specifying a size (number of elements) for the queue is optional. If
a size is given, it must be a positive decimal number, denoting the
maximum number of elements that can be stored in the queue. A
missing size means that it defaults to 100 elements. The variable can
be an array, and may be associated with multiple process variables, in
which case there is still only one queue, but any change in any of the
involved PVs will add an entry to the queue.

.. versionadded:: 2.1

You can use "syncq" (all lower case) as keyword instead of "syncQ". The
latter may be deprecated in a future version.

.. versionchanged:: 2.1

Not giving a queue size (thus relying on the default of 100 elements)
is now *deprecated* and the compiler will issue a warning. The reason
for this is that queues are now statically allocated, which can result
in a large memory overhead especially if the variable is an array
associated with a single PV. (A default queue size of 1 would be much
more useful, but for compatibility I kept it at 100 as in previous
versions.)

.. versionchanged:: 2.1

A queued variable no longer needs to be associated with an event flag.
The first form of the `syncq` clause is now merely an
abbreviation for a `sync` clause together with a `syncq`
of the second form, i.e. ::

   syncq var to ef qsize;

is equivalent to ::

   sync var to ef;
   syncq var qsize;

Forcing the association with an event flag was never really necessary,
since `pvGetQ` already checks and returns whether the queue is
empty or not; and any state set that mentions a variable in a
`transition` clause automatically gets woken up whenever the variable
changes due to a monitor event. On the other hand, relying on the event
flag being set as an indication that the queue is non-empty has always
been unreliable since another `pvGetQ` might have intervened and emptied
the queue between the two calls.

Note that `pvGetQ` clears an event flag associated with the variable if
the queue becomes empty after removing the head element.


.. _option definition:

Option
^^^^^^

.. productionlist::
   option: "option" `option_value` `identifier` ";"
   option_value: "+"
   option_value: "-"

The option name is any combination
of option letters. Multiple options can be clobbered into a single
option clause, but only a single option value is allowed.
Option value "+" turns the given options on, a"-" turns them off.

Examples::

   option +r; /* make code reentrant */
   option -ca; /* synchronous pvGet and don't wait for channels to connect */

Unknown option letters cause a warning to be issued, but are otherwise
ignored.

The same syntax is used for global options and state options. The
interpretation, however, is different:

Global (top-level) options are interpreted as if the corresponding
compiler option had been given on the command line (see
`CompilerOptions`). Global option definitions take precedence over
options given to the compiler on the command line.

State options occur inside the state construct and affect only the
state in which they are defined, see `State Option`_.

State Set
---------

.. productionlist::
   state_sets: `state_sets` `state_set`
   state_sets: `state_set`
   state_set: "ss" `identifier` "{" `ss_defns` `states` "}"
   ss_defns: `ss_defns` `ss_defn`
   ss_defns: 

A program contains one or more state sets. Each state set is
defined by the keyword "ss", followed by the name of the state set (an
identifier). After that comes an opening brace, optionally state set
local definitions, a list of states, and then a closing brace.

State set names must be unique in the program.

State Set Local Definition
^^^^^^^^^^^^^^^^^^^^^^^^^^

.. productionlist::
   ss_defn: `assign`
   ss_defn: `monitor`
   ss_defn: `sync`
   ss_defn: `syncq`
   ss_defn: `declaration`

Inside state sets are allowed variable declarations and process variable
definitions (`assign`, `monitor`, `sync`, and
`syncq`).

See `variable scope`_ for details on what local definitions mean.

State
^^^^^

.. productionlist::
   states: `states` `state`
   states: `state`
   state: "state" `identifier` "{" `state_defns` `entry` `transitions` `exit` "}"
   state_defns: `state_defns` `state_defn`
   state_defns: 

A state set contains one or more states. Each state is defined by the
keyword "state", followed by the name of the state (and identifier),
followed by an opening brace, optionally state local definitions, an
optional entry block, a list of `transitions`, an optional exit block,
and finally a closing brace.

State names must be unique in the sate set to which they belong.

State Local Definition
~~~~~~~~~~~~~~~~~~~~~~

.. productionlist::
   state_defn: `assign`
   state_defn: `monitor`
   state_defn: `sync`
   state_defn: `syncq`
   state_defn: `declaration`
   state_defn: `option`

.. _StateOption:

State Option
~~~~~~~~~~~~

The syntax for a state option is the same as for global options
(see `option definition`).

The state options are:

.. _state option +t:

+t
   Reset delay timers each time the state is entered, even if entered
   from the same state. This is the default.

.. _state option -t:

-t
   Don't reset delay timers when entering from the same state. In other
   words, the `delay` function will return whether the specified
   time has elapsed from the moment the current state was entered from a
   different state, rather than from when it was entered for the current
   iteration.

.. _state option +e:

+e
   Execute `entry` blocks only if the previous state was
   not the same as the current state. This is the default.

.. _state option -e:

-e
   Execute `entry` blocks even if the previous state was the same
   as the current state.

.. _state option +x:

+x
   Execute `exit` blocks only if the next state is not the same as
   the current state. This is the default.

.. _state option -x:

-x
   Execute `exit` blocks even if the next state is the same as
   the current state.

For example::

   state low {
      option -e; /* Do entry{} every time ... */
      option +x; /* but only do exit{} when really leaving */
      entry { ... }
      ...when ()...
      exit { ... }
   }

.. _state entry and exit blocks:

State Entry and Exit Blocks
~~~~~~~~~~~~~~~~~~~~~~~~~~~

The syntax is the same as for `Global Entry and Exit Blocks`_.

Entry blocks are executed when the state is entered, before any of the
`condition`\s for state `transitions` are evaluated.

Exit blocks are executed when the state is left, after the `transition`
block that determines the next state.

Transitions
~~~~~~~~~~~

.. productionlist::
   transitions: `transitions` `transition`
   transitions: `transition`
   transition: "when" "(" `condition` ")" `block` "state" `identifier`
   condition: `opt_expr`

A state transition starts with the keyword "when", followed by a condition
(in parentheses), followed by a block, and finally the keyword "state" and
then the name of the target state (which must be a state of the same state
set).

The condition must be a valid boolean expression (see `Expressions`_). If
there is no condition given, it defaults to `TRUE <seqBool>`.

Conditions are evaluated

#. when the state is entered (after entry block execution), and

#. when an event happens (see below).

Evaluation proceeds in the order in which the `transitions` appear in the
program. If one of the `condition`\s evaluates to `true`, the
corresponding action block is executed, any exit block of the state is
executed, and the state changes to the specified new state. Otherwise, the
state set waits until an event happens.

There are five types of event:

-  a process variable monitor is posted
-  an asynchronous `pvGet` or `pvPut` completes
-  a `delay` timer expires
-  an event flag is set or cleared
-  a process variable connects or disconnects

While the sequencer's runtime system and the compiler work together to
minimize evaluation of conditions, there is no guarantee whatsoever about
the number of times conditions might be evaluated before one of them returns
`true`.

Conditions are usually written so that they have no side-effects. This
ensures that it does not matter how often they are evaluated or in which
order.

.. note::

   If a condition has a side-effect, care should be taken to ensure that the
   effect is limited to the case when the whole(!) condition fires (returns
   `true`), so that it will occur at most once. Otherwise you will have no
   control over when and how often the effect happens.

   Built-in functions that have side-effects and are suitable for use in
   conditions, notably `efTestAndClear` and `pvGetQ`, meet this criterion,
   but only of used by themselves alone. If used as part of a larger
   expression, the whole condition might no longer meet the citerion.

.. versionadded:: 2.1

.. productionlist::
   transition: "when" "(" `condition` ")" `block` "exit"

Instead of declaring which should be the next state, one can use the single
keyword "exit". This has the same effect as a call to the `seqStop`
shell command, that is, at this point all state sets should terminate (after
completing any action block in progress) and execution proceed with the
global exit block (if any). Afterwards all channels are disconnected, all
allocated memory is freed, and the program terminates.

.. note::

   If the program has been started under an ioc shell, then only the SNL
   program is terminated, not the whole ioc. If terminating the ioc
   shell is required, you should call the ``exit()`` function from the
   standard C library. This call can conveniently be placed in the SNL
   program's global exit block.


Block
^^^^^

.. productionlist::
   block: "{" `block_defns` `statements` "}"
   block_defns: `block_defns` `block_defn`
   block_defns: 
   block_defn: `declaration`
   block_defn: `c_code`

Blocks are enclosed in matching (curly) braces. They may contain any
number of block definitions and afterwards any number of statements.

Block definitions are: declarations and embedded C code.


Statements and Expressions
--------------------------

Statements
^^^^^^^^^^

.. productionlist::
   statements: `statements` `statement`
   statements: 
   statement: "break" ";"
   statement: "continue" ";"
   statement: `c_code`
   statement: `block`
   statement: "if" "(" `comma_expr` ")" `statement`
   statement: "if" "(" `comma_expr` ")" `statement` "else" `statement`
   statement: "while" "(" `comma_expr` ")" `statement`
   statement: `for_statement`
   statement: `opt_expr` ";"
   for_statement: "for" "(" `opt_expr` ";" `opt_expr` ";" `opt_expr` ")" `statement`

As can be seen, most C statements are supported. Not supported is
the switch/case statement.

.. versionadded:: 2.1

.. productionlist::
   statement: "state" `identifier` ";"

The *state change statement* is not borrowed from C; it is only available
in the action block of a state `transition` and has the effect
of immediately jumping out of the action block, overriding the statically
specified new state (given after the block) with its state argument.

.. versionadded:: 2.2

.. productionlist::
   statement: "return" `opt_expr` ";"

The return statement can be used only inside `Function Definitions`_.

Expressions
^^^^^^^^^^^

Formation rules for expressions are listed in groups of descending order
of precedence. Precedence is determined by the standard C operator
precedence table.

Primary Expressions
~~~~~~~~~~~~~~~~~~~

.. productionlist::
   expr: `integer_literal`
   expr: `floating_point_literal`
   expr: `string`
   expr: `variable`
   string: `string_literal`
   expr: "(" `comma_expr` ")"

These are literals, variables, and parenthesized expressions.

Postfix Operators
~~~~~~~~~~~~~~~~~

.. productionlist::
   expr: `expr` "(" `args` ")"
   expr: "exit" "(" `args` ")"
   expr: `expr` "[" `expr` "]"
   expr: `expr` "." `member`
   expr: `expr` "->" `member`
   expr: `expr` "++"
   expr: `expr` "--"
   member: `identifier`

These are all left associative.

.. note::

   ``exit`` must be listed explicitly because in SNL it is a keyword,
   not an identifier. The extra rule allows calls to the standard
   library function ``exit``.

Prefix Operators
~~~~~~~~~~~~~~~~

.. productionlist::
   expr: "+" `expr`
   expr: "-" `expr`
   expr: "*" `expr`
   expr: "&" `expr`
   expr: "!" `expr`
   expr: "~" `expr`
   expr: "++" `expr`
   expr: "--" `expr`

Prefix operators are right-associative.

.. versionadded:: 2.2 Type casts and sizeof operator:

.. productionlist::
   expr: "(" `type_expr` ")" `expr`
   expr: "sizeof" "(" `type_expr` ")"
   expr: "sizeof" `expr`

Binary Operators
~~~~~~~~~~~~~~~~

.. productionlist::
   expr: `expr` "-" `expr`
   expr: `expr` "+" `expr`

.. productionlist::
   expr: `expr` "<<" `expr`
   expr: `expr` ">>" `expr`

.. productionlist::
   expr: `expr` "*" `expr`
   expr: `expr` "/" `expr`

.. productionlist::
   expr: `expr` ">" `expr`
   expr: `expr` ">=" `expr`
   expr: `expr` "<=" `expr`
   expr: `expr` "<" `expr`

.. productionlist::
   expr: `expr` "==" `expr`
   expr: `expr` "!=" `expr`

.. productionlist::
   expr: `expr` "&" `expr`

.. productionlist::
   expr: `expr` "^" `expr`

.. productionlist::
   expr: `expr` "|" `expr`

.. productionlist::
   expr: `expr` "||" `expr`

.. productionlist::
   expr: `expr` "&&" `expr`

.. productionlist::
   expr: `expr` "%" `expr`

All binary operators associate to the left.

.. note::

   Like in most programming languages, including C, evaluation of
   conditional expressions using ``&&`` and ``||`` is done *lazily*: the
   second operand is not evaluated if evaluation of the first already
   determines the result. This particularly applies to the boolean
   expressions in `transition` clauses. See the built-in function
   `pvGetQ` for an extended discussion.

Ternary Operator
~~~~~~~~~~~~~~~~

.. productionlist::
   expr: `expr` "?" `expr` ":" `expr`

The ternary operator (there is only one) is right-associative.

.. _Assignment Operators:

Assignment Operators
~~~~~~~~~~~~~~~~~~~~

.. productionlist::
   expr: `expr` "=" `expr`
   expr: `expr` "+=" `expr`
   expr: `expr` "-=" `expr`
   expr: `expr` "&=" `expr`
   expr: `expr` "|=" `expr`
   expr: `expr` "/=" `expr`
   expr: `expr` "*=" `expr`
   expr: `expr` "%=" `expr`
   expr: `expr` "<<=" `expr`
   expr: `expr` ">>=" `expr`
   expr: `expr` "^=" `expr`

These operators are right-associative.

Comma Operator
~~~~~~~~~~~~~~

.. productionlist::
   comma_expr: `comma_expr` "," `expr`
   comma_expr: `expr`
   opt_expr: `comma_expr`
   opt_expr: 

The comma operator is left associative. An `opt_expr` is
an optional `comma_expr`; it appears, for instance, inside
a `for_statement`.

Argument List
~~~~~~~~~~~~~

.. productionlist::
   args: `args` "," `expr`
   args: `expr`
   args: 

Function argument lists look exactly like chained application of the
comma operator, which is why application of the comma operator in an
argument list must be grouped by parentheses.


Built-in Constants
------------------

Some of the built-in functions use (i.e. accept or return) values of
certain enumeration types and constants. These are also available to C
code and defined in the header files pvAlarm.h and seqCom.h.

.. versionchanged:: 2.1.6

The pvStat and pvSevr constants are now known to the compiler, so you
no longer have to declare them as ``foreign`` in the SNL code.

.. c:type:: pvStat

An enumeration for status values.

::

   typedef enum {
       /* generic OK and error statuses */
       pvStatOK           = 0,
       pvStatERROR        = -1,
       pvStatDISCONN      = -2,

       /* correspond to EPICS statuses */
       pvStatREAD         = 1,
       pvStatWRITE        = 2,
       pvStatHIHI         = 3,
       pvStatHIGH         = 4,
       pvStatLOLO         = 5,
       pvStatLOW          = 6,
       pvStatSTATE        = 7,
       pvStatCOS          = 8,
       pvStatCOMM         = 9,
       pvStatTIMEOUT      = 10,
       pvStatHW_LIMIT     = 11,
       pvStatCALC         = 12,
       pvStatSCAN         = 13,
       pvStatLINK         = 14,
       pvStatSOFT         = 15,
       pvStatBAD_SUB      = 16,
       pvStatUDF          = 17,
       pvStatDISABLE      = 18,
       pvStatSIMM         = 19,
       pvStatREAD_ACCESS  = 20,
       pvStatWRITE_ACCESS = 21
   } pvStat;

.. c:type:: pvSevr

::

   typedef enum {
       /* generic OK and error severities */
       pvSevrOK      = 0,
       pvSevrERROR   = -1,

       /* correspond to EPICS severities */
       pvSevrNONE    = 0,
       pvSevrMINOR   = 1,
       pvSevrMAJOR   = 2,
       pvSevrINVALID = 3
   } pvSevr;

.. c:type:: compType

::

   enum compType {
       DEFAULT,
       ASYNC,
       SYNC
   };

.. note::

   Only `SYNC <compType>` and `ASYNC <compType>` are SNL built-in constants
   (i.e. known to `snc`). The constant `DEFAULT <compType>` is for use in C
   code (to represent a missing optional argument).

.. c:type:: seqBool

::

   typedef int seqBool;

   #define TRUE   1
   #define FALSE  0

.. c:macro:: NOEVFLAG

::

   #define NOEVFLAG 0

This can be given as second argument to `pvSync` (instead of
an event flag) to cancel the sync.


.. _BuiltinFunctions:

Built-in Functions
------------------

The following special functions are built into the language. In most cases
the compiler performs some special interpretation of the arguments to these
functions. Using their C equivalents in escaped C code is possible but
subject to special rules.

For documentation purposes only, we assign special pseudo-types to some of
the parameters of built-in functions. Note that these are not valid SNL types.

.. c:type:: channel

A parameter with this type means the function expects an SNL variable or
array element that is assigned to a single (possibly anonymous) process
variable.

A parameter with type ``channel[]`` means the function expects a channel
array, that is, an array where the elements are assigned to separate
(possibly anonymous) process variables. By convention, functions accepting a
`channel` array have a name that starts with "pvArray".

Several of these functions are intended to be called only from
`transition` clauses or only from action code. If the compiler allows
it, it is safe to call them in another context, but the effect might not be
what you expect.


delay
^^^^^

.. c:function::
   seqBool delay(double delay_in_seconds)

Returns whether the specified time has elapsed since entering the
state. It should be used only within a `transition` expression.

The "t" `StateOption` controls whether the
delay is measured from when the current state was entered from a different
state (`-t <state option -t>`) or from any state, including itself (`+t <state option +t>`,
the default).

.. versionchanged:: 2.2

It is no longer allowed to call this function outside the `condition` of a
`transition` clause.


pvPut
^^^^^

.. versionchanged: 2.2 optional timeout parameter

.. c:function::
   pvStat pvPut(channel ch, compType mode = DEFAULT, double timeout = 10.0)

Puts (or writes) the value of ``ch`` to the process variable it has been
assigned to. Returns the status from the PV layer (e.g. `pvStatOK <pvStat>`
for success).

By default, i.e. with no optional arguments, `pvPut` is un-confirmed "fire
and forget"; completion must be inferred by other means. An optional second
argument can change this default:

* `SYNC <compType>` causes it to block the state set until completion. This
  mode is called *synchronous*.

* `ASYNC <compType>` allows the state set to continue but still check for
  completion via a subsequent call to `pvPutComplete` (typically in a
  `condition`). This mode is called *asnchronous*.

A timeout value may be specified after the `SYNC <compType>` argument. This
should be a positive floating point number, specifying the number of seconds
before the request times out. This value overrides the default timeout of 10
seconds.

Note that SNL allows only one pending `pvPut` per variable and state set
to be active. As long as a ``pvPut(var,ASYNC)`` is pending completion,
further calls to ``pvPut(var,ASYNC)`` from the same state set immediately
fail and an error message is printed; whereas further calls to ``pvPut(var,SYNC)``
are *delayed* until the previous operation completes. Thus ::

   pvPut(var,ASYNC);
   pvPut(var,SYNC);

and ::

   pvPut(var,SYNC);
   pvPut(var,SYNC);

are equivalent. Whereas in ::

   pvPut(var,ASYNC);
   pvPut(var,ASYNC);

the second pvPut may fail if it is a named PV located on another IOC (since
the first one is still awaiting completion), whereas for local named PVs and
anonymous PVs it will succeed (since completion is immediate in these cases).

In `safe mode`, `pvPut` can be used with anonymous PVs (variables assigned
to "") to communicate between state sets. This makes sense only with global
variables as only those can be referenced in more than one state set. The
behaviour for anonymous PVs exactly mirrors that of named PVs, including the fact
that the new value will not be seen by other state sets until they issue a pvGet,
or, if the variable is monitored, until they wait for an event in a `condition`.
Note that for anonymous PVs completion is always immediate, so the `ASYNC <compType>` option
is not very useful.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.

.. todo:: pvArrayPut


pvPutComplete
^^^^^^^^^^^^^

.. c:function::
   seqBool pvPutComplete(channel ch)

Returns whether the last asynchronous `pvPut` to this process variable has
completed.

Always returns `true` for anonymous PVs.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error. In previous versions this was the one built-in pv
function that correctly worked on channel arrays. The corresponding C
function still has the extra arguments for compatibility, but in SNL code
you must now use `pvArrayPutComplete` for channel arrays.


pvArrayPutComplete
^^^^^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   seqBool pvArrayPutComplete(channel ch[], unsigned int length, seqBool any = FALSE, seqBool *complete = NULL)

Like `pvPutComplete`, but returns whether all of the ``length`` first
elements of the array have completed their last asynchronous `pvPut`. The
``length`` argument should not be larger than the number of elements in
the array.

If the optional ``any`` argument is `true`, then instead it returns whether
*any* of these channels have completed.

If the optional ``complete`` argument is not ``NULL``, it must point to an
array of `seqBool`\s with at least  ``length`` elements; the will then
write the individual results for the array elements into this array.


pvPutCancel
^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   void pvPutCancel(channel ch)

Cancel a pending (asynchronous) `pvPut`.


pvArrayPutCancel
^^^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   void pvArrayPutCancel(channel ch[], unsigned int length)

Like `pvPutCancel` but all pending asynchronous `pvPut`\s to the first
``length`` elements of the array are cancelled.


pvGet
^^^^^

.. c:function::
   pvStat pvGet(channel ch, compType ct = DEFAULT, double timeout = 10.0)

Gets (or reads) the value of ``ch`` from the process variable it has been
assigned to. Returns the status from the PV layer (e.g. `pvStatOK <pvStat>`
for success).

With no optional arguments, the completion type is determined by the value
of a program option: if `-a` is in effect (the default), then the state set
will block until the read operation is complete or until the default timeout
of 10 seconds has expired; if `+a` is in effect, the the call completes
immediately and completion can be checked with subsequent calls to
`pvGetComplete` (typically in a `condition`).

An optional second argument overrides the program option: `SYNC <compType>`
makes the call block and `ASYNC <compType>` makes the call return
immediately, regardless of whether `-a` or `+a` is in effect.

In `safe mode`, if `ASYNC <compType>` is specified and the variable is not
monitored, then the state set local copy of the variable will not be updated
until a call to pvGetComplete is made and returns `true` or, if the
variable is monitored, until the state set waits for events in a `transition`
clause. Note that anonymous PVs behave exactly in the same way.

Like for `pvPut`, only one pending `pvGet` per channel and state set can be
active.

.. versionchanged:: 2.2

A timeout value may be specified after the `SYNC <compType>` argument. This should be
a positive floating point number, specifying the number of seconds before
the request times out. This value overrides the default timeout of 10 seconds.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.

.. todo:: pvArrayGet


pvGetComplete
^^^^^^^^^^^^^

.. c:function::
   seqBool pvGetComplete(channel ch)

Returns whether the last asynchronous `pvGet` for ``ch`` has completed.

In `safe mode`, the the state set local copy of the variable will be
updated with the value from the PV layer as a side effect of this call
(if `true` is returned).

Always returns `true` for anonymous PVs.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error. For use with multi-PV arrays there is now
`pvArrayGetComplete`.


pvArrayGetComplete
^^^^^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   seqBool pvArrayGetComplete(channel ch[], unsigned int length, seqBool any = FALSE, seqBool *complete = NULL)

Like `pvGetComplete` but works on all channels of a multi-PV array. The
optional arguments have the same meaning as for `pvArrayPutComplete`.


pvGetCancel
^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   void pvGetCancel(channel ch)

Cancel a pending (asynchronous) `pvGet`.


pvArrayGetCancel
^^^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   void pvArrayGetCancel(channel ch[], unsigned int length)

Cancel pending (asynchronous) `pvGet` operations for the first ``length``
elements of the array.


pvGetQ
^^^^^^

.. c:function::
   seqBool pvGetQ(channel ch)

The channel argument must have been associated with a queue using the
`syncq` clause.

If the queue is not empty, remove its first (oldest) value and update the
variable with it. Returns whether there was an element in the queue (and the
variable got updated). If an element gets removed, and the queue becomes
empty as a result, then any event flag `sync`\ed to the variable will be
cleared.

Note that since pvGetQ may have a *side-effect* you should be careful when
combining a call to pvGetQ with other `condition`\s in the same
`transition` clause, e.g. ::

        when (pvGetQ(msg) && other_condition) {
           printf(msg);
        } state ...

would remove the head from the queue every time the `condition` gets
evaluated, regardless of whether ``other_condition`` is `true`. This is
most probably not the desired effect, as you would lose an unknown number of
messages. (Of course it *could* be exactly what you want, for instance if
``other_condition`` were something like ``!suppress_output``.) Whereas ::

        when (other_condition && pvGetQ(msg)) {
           printf(msg);
        } state ...

is "safe", in the sense that no messages will be lost. BTW, If you combine
with a disjunction ("||") it is the other way around, i.e. pvGetQ should
appear as the first operand. This is all merely a result of the evaluation
order imposed by the C language (and thus by SNL); similar remarks apply
whenever you want to use an expression inside a `transition` clause
that potentially has a side-effect.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvFreeQ
^^^^^^^

.. c:function::
   void pvFreeQ(queued_channel)

Deletes all entries from a queued variable's queue and
clears the associated event flag.

.. versionchanged:: 2.1

Queue elements are no longer dynamically allocated, so this
is now an alias for pvFlushQ.


pvFlushQ
^^^^^^^^

.. c:function::
   void pvFlushQ(queued_channel)

.. versionadded:: 2.1

Flush the queue associated with this variable, so it is empty afterwards.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvAssign
^^^^^^^^

.. c:function::
   pvStat pvAssign(channel ch, char *pv_name)

Assigns or re-assigns ``ch`` to a process variable with name ``pv_name``. If
``pv_name`` is an empty string or ``NULL``, then ``ch`` is de-assigned (not
associated with any process variable); in `safe mode` it causes assignment
to an anonymous PV.

See also `assign` clause.

Note that pvAsssign is *asynchronous*: it sends a request to search for and
connect to the given ``pv_name``, but it does not wait for a
response, similar to ``pvGet(var,ASYNC)``. Calling pvAssign *does* have one
immediate effect, namely de-assigning the variable from any PV it currently
is assigned to. In order to make sure that it has connected to the new PV,
you can use the `pvConnected` built-in function inside a `transition`
clause.

Like for all other operations, completion is immediate for anonymous PVs.

.. todo::

   Add an optional argument, so users can make pvAssign wait for the
   connection to be established (or some timeout).

.. note::

   pvAssign can only be called on variables (or array elements)
   that have been statically marked as process variables using the
   `assign` syntax. An empty string may be used for the initial
   assignment, or (from version 2.1. onward) the simplified form
   ``assign var``.

.. warning::

   If a variable gets de-assigned from a non-empty to an empty
   name, the corresponding channel is destroyed, which means that
   dynamically allocated memory gets freed. If your
   system cannot handle dynamic memory allocation without fragmentation,
   care should be taken that assignment and de-assignment do not
   alternate too often.

.. note::

   If you want to assign array elements to separate PVs, you cannot
   currently do this with a single call (in contrast to doing it in an
   `assign` clause. Instead, you must call ``pvAssign``
   for each array element individually.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvAssignSubst
^^^^^^^^^^^^^

.. c:function::
   pvStat pvAssignSubst(channel ch, char *pv_name)

Like `pvAssign`, except that it substitutes program parameters, like the
`assign` clause.


pvMonitor
^^^^^^^^^

.. c:function::
   pvStat pvMonitor(channel ch)

Initiates a monitor on the process variable that ``ch`` was assigned to.

See `monitor` clause.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvArrayMonitor
^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   pvStat pvArrayMonitor(channel ch[], unsigned int length)

Like `pvMonitor` but turns on monitors for the first ``length`` elements of
a channel array.

See `monitor` clause.


pvStopMonitor
^^^^^^^^^^^^^

.. c:function::
   pvStat pvStopMonitor(channel ch)

Terminates a monitor on the underlying process variable.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvArrayStopMonitor
^^^^^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   pvStat pvArrayStopMonitor(channel ch[], unsigned int length)

Like `pvStopMonitor` but turns off monitors for the first ``length``
elements of a channel array.


pvSync
^^^^^^

.. c:function::
   void pvSync(channel ch, evflag ef)

Synchronizes a variable with an event flag, or removes such a
synchronization if ``ef`` is `NOEVFLAG`.

See `sync` clause.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvArraySync
^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   void pvArraySync(channel ch[], unsigned int length, evflag ef)

Synchronizes the first ``length`` elements of a channel array with an event
flag, or removes such a synchronization if ``ef`` is `NOEVFLAG`.

See `sync` clause.


pvCount
^^^^^^^

.. c:function::
   unsigned pvCount(channel ch)

Returns the element count associated with the (single) process variable.
This value is independent of the array size given in the declaration, it can
be smaller or larger.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvStatus
^^^^^^^^

.. c:function::
   pvStat pvStatus(channel ch)

Returns the current alarm status of the underlying PV or indicates failure
of a previous PV operation.

The status, severity, and message returned by `pvStatus`,
`pvSeverity`, and `pvMessage` reflect either the underlying
PV's properties (if a `pvPut` or `pvGet` operation completed, or the variable is
`monitor`\ed), or else indicate a failure to initiate one of these operations.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvSeverity
^^^^^^^^^^

.. c:function::
   pvSevr pvSeverity(channel ch)

Returns the current alarm severity (e.g. pvSevrMAJOR) of the underlying PV
or indicates failure of a previous PV operation.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvMessage
^^^^^^^^^

.. c:function::
   const char *pvMessage(channel ch)

Returns the current error message of the variable, or "" (the empty string)
if none is available.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvTimeStamp
^^^^^^^^^^^

.. c:function::
   struct epicsTimeStamp pvTimeStamp(channel ch)

Returns the time stamp for the last `pvGet` completion or monitor
event for this variable.

.. versionadded:: 2.2

You can declare variables of type ``struct epicsTimeStamp`` directly in SNL
code.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvAssigned
^^^^^^^^^^

.. c:function::
   seqBool pvAssigned(channel ch)

Returns whether the SNL variable is currently assigned to a process variable.
Note that this function returns ``FALSE`` for anonymous PVs.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvConnected
^^^^^^^^^^^

.. c:function::
   seqBool pvConnected(channel ch)

Returns whether the underlying process variable is currently connected.

.. versionchanged:: 2.2

Always returns `true` for anonymous PVs.

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvArrayConnected
^^^^^^^^^^^^^^^^

.. versionadded:: 2.2

.. c:function::
   seqBool pvArrayConnected(channel ch[], unsigned int length)

Returns whether elements of a channel array are currently connected.


pvIndex
^^^^^^^

.. c:function::
   unsigned pvIndex(channel ch)

Returns the index associated with a variable. See
`Calling PV Functions from C` for how to use this function.

.. versionchanged:: 2.2

Calling this function with a multi-PV array is no longer allowed and results
in a compile-time error.


pvFlush
^^^^^^^

.. c:function::
   void pvFlush()

Causes the PV layer to flush its send buffer. This is only
necessary if you need to make sure that CA operations are
started *before* the action block finishes. The buffer is
always automatically flushed before the sequencer waits
for events, that is, after a state's entry block is executed
(or on entry to the state if there is no entry block).
The buffer is also flushed after initiating a synchronous
operation that waits for a callback (i.e. ``pvPut(var,SYNC)``
and ``pvGet(var,SYNC)``).


pvChannelCount
^^^^^^^^^^^^^^

.. c:function::
   unsigned pvChannelCount()

Returns the total number of process variables
associated with the program.


pvAssignCount
^^^^^^^^^^^^^

.. c:function::
   unsigned pvAssignCount()

Returns the total number of SNL variables in this
program that are assigned to underlying process variables.

For instance, if all SNL variables are assigned then the following
expression is `true`::

   pvAssignCount() == pvChannelCount()

Each element of an SNL array counts as variable for the purposes of
`pvAssignCount`.


pvConnectCount
^^^^^^^^^^^^^^

.. c:function::
   unsigned pvConnectCount()

Returns the total number of underlying process
variables that are connected.

For instance, if all assigned variables are connected then the following
expression is `true`::

   pvConnectCount() == pvAssignCount()


efSet
^^^^^

.. c:function::
   void efSet(evflag ef)

Sets the event flag and causes evaluation of the `transition`
clauses for all state sets that are pending on this event flag.


efClear
^^^^^^^

.. c:function::
   seqBool efClear(evflag ef)

Clears the event flag and causes evaluation of the `transition`
clauses for all state sets that are pending on this event flag.


efTest
^^^^^^

.. c:function::
   seqBool efTest(evflag ef)

Returns whether the event flag was set.

.. note::

   In safe mode, this function is a synchronization point for all
   variables (channels) that are `sync`\ed with it, i.e. the
   state set local copy of these variables will be updated from their
   current globally visible value.


efTestAndClear
^^^^^^^^^^^^^^

.. c:function::
   seqBool efTestAndClear(evflag ef)

Clears the event flag and returns whether the event
flag was set. It is intended for use within a `transition` clause.

.. note::

   In safe mode, this function is a synchronization point for all
   variables (channels) that are `sync`\ed with it, i.e. the
   state set local copy of these variables will be updated from their
   current globally visible value.


macValueGet
^^^^^^^^^^^

.. c:function::
   char* macValueGet(char *parameter_name)

Returns a pointer to the value of the specified program parameter, if
it exists, else ``NULL``. See `Program Name and Parameters`_.


optGet
^^^^^^

.. c:function::
   seqBool optGet(char *option_name)

Returns whether the program option with the given name is in effect or not.
The option name is a string consisting of a single letter such as "a" for
the program option `+a`/`-a`.

.. _safe mode:

Safe Mode
---------

.. versionadded:: 2.1

SNL code can be interpreted in safe mode by using the program option `+s`,
see `safe mode tutorial` for a less dense introduction. Safe mode implies
reentrant mode, see `reentrant option`.

In safe mode, global variables are treated as if they were local to the
state set. Changing such a variable inside an action block does not have any
immediate effect on other state sets, nor do external events, such as
monitors or pvGet completions, change the variables as seen by a state set,
except at well defined `Synchronization Points`. In other words, each state
set works on its own copy ("view") of all variables. The `Channel Access`
layer (and the part of the run time system that implements `anonymous
channels`) acts on yet another copy, the "world view". Information is only
ever exchanged between the world view and the state set local view. All
communication between state sets must be explicit: apart from using event
flags, the only way information can flow *out* of a state set is using
`pvPut`, the only way *in* is via `pvGet` or monitors.

An important (and welcome) side-effect of safe mode is that in the action
block after a condition you can rely on the `condition` to be `true` (and
remain `true` -- unless, of course, the action block itself invalidates it
by modifying the variables involved).

.. _Synchronization Points:

Synchronization Points
^^^^^^^^^^^^^^^^^^^^^^

At certain points in the program a state set's view of the variables is
updated from the world view. These are:

* in each state, immediately before `condition`\s are evaluated (for
  monitored channels), and

* in calls to some of the built-in functions: `efTest`, `efTestAndClear`,
  `pvGet` (in SYNC mode), and `pvGetComplete` (when it signals completion).

Note that there is no point at which variables modified by a state set are
automatically "published". For this you have to use `pvPut` explicitly,
which updates the world view as a side-effect.

.. _anonymous channels:
.. _anonymous pvs:

Anonymous Channels
^^^^^^^^^^^^^^^^^^

It would be impractical if the programmer had to create a real PV for each
communication channel inside a program. Instead, in safe mode you can use an
*anonymous channel* for this purpose. Anonymous channels are created with
the usual `assign` clause, but use an empty string (``""``) for the PV name
(which in traditional mode is interpreted as "not assigned"). You can
imagine such channels to be connected to "virtual" PVs that are implemented
inside the sequencer's run time system.

Anonymous channels should work exactly like channels assigned to a "real" PV
-- they can be `monitor`\ed, `sync`\ed to event flags etc. -- except that
they are always connected and the operations on them always complete
immediately.

Event Flags in Safe Mode
^^^^^^^^^^^^^^^^^^^^^^^^

Apart from the `sync` and `syncq` features, and apart from the atomicity of
`efTestAndClear`, an event flag behaves like an anonymous PV of boolean type
with a monitor. In safe mode, `efTest` and `efTestAndClear` have the
additional side-effect of acting as a synchronization point for all
variables that are `sync`\ed with the event flag.


Foreign Variables and Functions
-------------------------------

You can use (reference, call) any exported C variable or function that is
linked to your program. For instance, you may link a C library to the SNL
program and simply call functions from it directly in SNL code. We call such
entities or objects "foreign".

It is advisable to take care that the C code generated from your SNL program
contains (directly or via ``#include``) a valid declaration for foreign
entities, otherwise the C compiler will not be able to catch type errors
(the SNL compiler does not do any type-checking by itself). For libraries,
this is usually done by adding a line ::

   %%#include "api-of-your-library.h"

(See also `Preprocessor Directives`.)

Note that when passing channel variables (i.e. ones assigned to one or more
PVs using an `assign` clause) to a C function, only the *value* is
passed. The special assigned" status of the variable gets lost as soon as
the function is entered (same as for functions defined in SNL).

.. versionchanged:: 2.2

The SNL compiler no longer complains about "undeclared identifiers" when
using foreign variables unless you supply the `+W` (extra warnings)
option.
