In Enif, most functionalities which access network data are not implemented by accessing network attributes directly, as is the case in EMME/2. Rather, wherever possible, the available functionalities (such as selecting subnetworks, plotting or listing network related data) are based on expressions.
In general, an expression is made up of an arbitrary combination of operands, operators and calls to intrinsic functions, using syntax rules compatible with those used by the expressions in EMME/2. In practice, however, an expression is often as simple as a single constant value (e.g. ``1'') or an attribute name (e.g. ``volau'').
In contrast to EMME/2, where expressions are limited to handle exclusively numerical values, Enif expressions support both numerical and string values. A strict distinction is made between numerical and string values. Each operator has well defined operand types and each intrinsic function requires its arguments to be of the specified types. Special intrinsic functions can be used to convert strings to numbers and vice versa, should this become necessary.
Each operand of an expression must correspond to one of the following:
A list of all available operators is shown in Table 2, grouped
in the order of increasing operator precedence.
It also shows the required operand types for each operator, as well as
the type of the result of the operation. All operators are binary
operators, with the exception of the ``
+'' and ``
these can be used as unary operators at the beginning of subexpressions.
Note that all logical and comparison operators return 0 for FALSE and 1 for TRUE. Operands of logical operators are assumed TRUE for all non-zero values, FALSE for zero values.
The available intrinsic functions are shown in Table 3. Note that some functions allow for a variable number of arguments. For technical reasons, the number of arguments for these functions is currently limited to a maximum of 30. Two noteworthy functions of this type are the lookup() and the which() functions. The function lookup( i, v1, v2, v3,...) takes an index i as first argument and returns vi, the i-th of the following values. The function which( v, v1, v2, v3,...) takes a value v as first argument and compares it with the following values vi. It returns the index i of the first match v = vi found, or 0 otherwise.
In addition to operands and operators, an expression may also contain
comments enclosed in brackets,
[auto network without connectors] isAuto && not(isConnector)''.
This is useful to explain the meaning of an expression to those users who
are not (yet) Enif experts.
Depending on the context, an expression may allow only a numerical result or also accepts a string result. But even expressions which are limited to numerical results may include string valued subexpressions.
In certain contexts it is possible to specify an expression returning
more than a single value. This is done by simply specifying
several subexpressions, separated by commas. The maximum
allowed number of expressions is given by the particular context.
E.g. the expression
provides two values, the auto volumes minus the additional volumes as first value and the additional volumes as second value.
A special case is the empty expression, i.e. an expression with no operands at all. Empty expression will always return a zero value as result.
While the expressions in Enif are much more powerful than those in EMME/2, they remain essentially compatible with EMME/2 expressions. All attributes, operators and intrinsic functions which are available in EMME/2 are also available in Enif.
Expressions are always stored as normal strings, which also implies that they can easily be saved to files and read back when needed. However, when expressions are actually used to provide values, their string representation is automatically compiled into an efficient internal RPN token list. Special cases, such as empty expressions, constants or expressions consisting of a single attribute are recognized as such, so that they can be handled with less overhead. These features allow for a very efficient evaluation of the same expression for large numbers of network elements.
Since the user can enter and modify expressions at any time, it may happen that he or she enters an invalid expression. For this reason, expressions entered or modified interactively by the user are compiled immediately after the return key is pressed. If the expression is found to be invalid, the background of the expression field becomes red (or to be more precise: changes to a special color which is configurable in the user preferences) and the cursor is moved to the place in the expression where the error was detected. If this is not enough to reveal the cause of the error to the user, he can also check on the diagnostic window, where he will find a detailed description of the error.