The fsm
section of a grammar description consists of a list of
state descriptions. Each state description defines a list of transitions that
are supported for leaving that state. Each transition in turn specifies which
input event and optional guard condition combination would cause that
transition, any action that must be executed when that event-guard pair occur in
that state, and the next state that the state machine will jump to.
As an example, we shall consider a rule from the pedestrian crossing controller state machine whose complete grammar has been seen previously here:
// Once the lights have gone green, there is a
// minimum interval they must remain green before
// the controller will respond to the next button press.
TimedGreen:
TIMERTICK[ButtonWasPressed] Yellow
{ SetLightYellow(); SetYellowTimer(); }
| BUTTONPRESS TimedGreen
{ RecordButtonPressed(); }
| TIMERTICK[!ButtonWasPressed] Green
;
Notice the basic syntax for a state description. The name of a state that is being defined is followed by a colon. This is then followed by a list of transition descriptions, separated from each other by the vertical bar character. The state description is then ended by a semi-colon.
Each transition description consists of the name of an input event, as previously listed in the events or tokens section of the grammar. The input event name may optionally be followed by guard condition in square brackets if a guard condition should also be true for the event when it occurs as an input to the state machine. The input event and optional guard are then followed by the name of the next state to jump to.
For example, in the excerpt above, the state TimedGreen
has a
transition that says: if a TIMERTICK
event should occur, and the
guard function ButtonWasPressed
returns true
, jump to
state Yellow
.
After each transition rule there can appear an optional block of C# code
enclosed in an outer set of curly braces. This
indicates to the state machine generator that when this transition is recognised
and takes place, that block of code should be executed as the action function.
Hence in the above example, when
transiting from TimedGreen
to Yellow
, the functions
SetLightYellow
followed by SetYellowTimer
will be
executed.
Sometimes when designing a state machine we discover that all transitions out
of a state have to execute some action code regardless of which state we are
transiting to, or which event occurs. This typically might be some clean-up
operation necessary before exiting the state. Rather than add this action
code at the beginning of each action for every transition away
from the state, we can refactor this by declaring an exit action for the
state. This is written as a special rule after the state name and its colon have
begun a state description. The reserved word exit
or onexit
is followed by the code block to be executed on leaving the state by any
transition.
Similarly, an action to be taken on arriving at a new
state via any transition can be introduced by using the reserved word
entry
or onentry
.
When exit actions, actions on transitions and entry actions all are implied by a particular transition, the order of execution of these actions is clearly defined. Exit actions for the previous state are executed first. Transition actions are executed next. Lastly the entry actions of the new state to which the machine has transferred are executed.
There are no examples of these entry or exit actions in the pedestrian
crossing example as it stands, but imagine there was a panic button that could
be pressed to set all lights to red (the AllRed
) state if something hazardous
had happened on the crossing that warranted stopping the traffic for an
indefinite period of time. In this case, regardless of which state we were
arriving at the AllRed
state from, we would want to execute the action function
SetLightRed
. The AllRed
state description might now be rewritten as follows:
AllRed:
entry
{ SetLightRed(); }
TIMERTICK RedAndYellow
{ SetLightRedAndYellow(); SetRedAndYellowTimer(); }
| BUTTONPRESS AllRed
{ RecordButtonPressed(); }
;
Note that the Yellow
state's transition to the AllRed
state on a TIMERTICK
event would now no longer need the
SetLightRed
action as it is implied by the entry
action
above. Moreover, the developer of the state machine should convince him/herself
that it is acceptable to move the SetLightRed
action along the list
of actions to be executed so that it becomes the last action rather than the
first.
Note that the layout used here for rules is not mandated, as white space and newlines are merely separators. It is however a common layout so that the separate rules and rule groups are easily identified.