-
Notifications
You must be signed in to change notification settings - Fork 0
State block grammar
The bulk of an xsm file is a sequence of state blocks, one per state, each one closed by an unindented --.
Here are two states from the Door lifecycle so we can look at the structure:
state OPENING
activity
transitions
Door opened > OPEN
Passenger close > CLOSING
ignore
Passenger open
The passenger can hit the open button all they like at this point,
but it will be ignored since the door is, in fact, opening now.
can't happen
Door closed
Door is opening, so it should not detect ‘closed’.
--
state OPEN
activity
my Bank .= /R4/R2/R1/Bank
Blocked? delay = my Bank.Block clear time :
delay = my Bank.Passenger load time
Time to close -> me @delay
transitions
Passenger close > Cancel open delay
Time to close > HOLDING OPEN
--
The relevant grammar is:
// State block
state_block = state_header activity transitions? ignores? cant_happens? block_end
state_header = "state" SP state_name signature? (SP DELETION)? EOL*
signature = '()' / '(' SP? parameter_set SP? ')'
parameter_set = parameter (',' SP parameter)*
parameter = parameter_name SP? ':' SP? type_name
parameter_name = name
type_name = name
DELETION = r'!\*'
activity = activity_header body_line* // A block of activity text (not parsed further)
activity_header = "activity" EOL
transitions = transition_header transition*
transition_header = "transitions" EOL*
transition = INDENT event_name (SP '>' SP state_name)? EOL*
state_name = name
// Non transition event responses
ignores = ignore_header response*
ignore_header = "ignore" EOL*
cant_happens = cant_happen_header response*
cant_happen_header = "can't happen" EOL*
response = INDENT event_name (SP tag)? EOL* explanation?
explanation = expl_line+
expl_line = INDENT INDENT tag? expl_text EOL
expl_text = r'[^\n]*'
tag = '<' r'[^>\n]+' '>' SP*
So a state block is six things in a fixed order:
- A state header — the unindented
statekeyword, the state name, an optional signature and an optional!*deletion marker. See State names, Signatures and Deletion states. - An activity — the unindented
activitykeyword followed by the action language body. Required, even when the body is empty. See Activities. -
Transitions — optional. The unindented
transitionskeyword followed by indented transition lines. See Transitions. -
Ignores — optional. The unindented
ignorekeyword followed by the events accepted and discarded here, each with its explanation. See Ignore. -
Can't happens — optional. The unindented
can't happenkeyword followed by the events that cannot legitimately arrive here, each with its explanation. See Can't Happen. - The block end — an unindented
--.
Only the last of these sections is followed by --. The three response sections are parts of one state
block, so one block_end closes all of them together, and the order is fixed: an ignore section may not
precede transitions, and can't happen always comes last. Explanations belong to
Reason tags.
This trips people up, so it's worth stating plainly: state_block = state_header activity transitions? ignores? cant_happens? block_end has no ? after activity. Every state must have the activity line
even if there is nothing underneath it.
The OPENING state above does exactly that, and so does MOVING in the Cabin lifecycle. Leave the keyword
out and the parse fails with a complaint about expecting 'activity'.
The other three sections really are optional. A state with no transitions is a final state — CANNOT CLOSE
in the Door lifecycle is one, and it still carries a can't happen section, since a final state can be sent
events just like any other.
The state blocks may appear in any order and the parser preserves the order you wrote them in. There's no significance to which one comes first; the starting state is established by Initial transitions, not by position in the file.
A list of StateBlock_a named tuples in the states field of StateModel_a. Each one has a state (a
StateSpec_a with name, deletion and signature), an activity (a list of raw text lines), a
transitions list of Transition_a named tuples, and ignores and cant_happens lists of Response_a
named tuples carrying event, tag and explanation.
Events left out of all three response sections do not appear in the parse result at all. They default to can't happen — see Can't Happen for what that default does and does not tell you.
Copyright 2023-2026 © Leon Starr under MIT Open Source License