In SNAKES, one cannot use `Expression`

objects as input arcs
annotations, moreover, all the variables used in a transition guard
and output arcs should be bound through (at least) one of the input
arcs. These restrictions can be explained.

When calling method `t.modes()`

, the available modes are computed by
matching the input arcs annotations with respect to the available
tokens in the corresponding input places. This is a simple task when
input arcs are only annotated with values, variables or tuples of such
objects. But it may become impossible if expressions would be used.
Indeed, imagine a place `p`

and an arc from `p`

to `t`

labelled by
`Expression("f(x)")`

where `f`

is a Python function. For each token
value `v`

in `p`

, `t.modes()`

would need to solve `f(x)==v`

wrt `x`

in
order to find all possible bindings of `x`

. This is simply not
possible in general because SNAKES allow `f`

to be an arbitrary Python
function. (There's not magic: there must be a price to pay for such a
level of generality.) Fortunately, this is not a limitation in
practice.

So, SNAKES simply forbids expressions on input arcs. This could be
relaxed a bit when, for instance, `x`

can be known from another input
arc. But this would lead to complex code and usually, there is another
way to achieve the same effect. In this instance, we can bind tokens
from `p`

using an annotation `Variable("y")`

and add `"y == f(x)"`

in
the guard of `t`

.

Now, given a `Transition`

instance `t`

and a `Substitution`

instance
`m`

, the execution of `t.fire(m)`

can be decomposed into several
steps:

- check if the guard evaluates to
`True`

; - check if the input places have enough tokens with respect to the evaluation of each input arc;
- check if the output places can accept the tokens corresponding to the evaluation of each output arc;
- consume tokens from the input place;
- produce tokens to the output places.

(The first and third steps are actually also performed by `t.modes()`

to ensure it returns a mode that actually allows firing and not just a
correct binding of the input variables.)

Considering that `m`

is a dict-like object, the evaluations above are
roughly equivalent to Python's `eval(expr, m)`

. If `m`

was discovered
using `t.modes()`

, it only binds variables that appear in at least one
input arc. So, if more variables are used (for instance in the guard)
a `NameError`

will occur.

This explains why it is recommended that all variables are bound
through input arcs. But this is not a requirement when modes are
user-provided instead of being computed using `t.modes()`

.