Skip to content

Activities

Leon Starr edited this page Jul 24, 2026 · 3 revisions

Each state has an activity: the work performed on arrival in that state. It's the second element of every state block and, unlike the transitions section, it is required.

state LOCKED
activity
    // Tell the cabin that we’re ready to go
    Lock requested.unset
    Doors secure -> /R4/Cabin
transitions
    Unlock > OPENING
--

The grammar is short because most of the work isn't done here:

activity = activity_header body_line* // A block of activity text (not parsed further)
activity_header = "activity" EOL
body_line = '\n' / (INDENT r'.*\n') // An unstructured line of text for capturing action language to be parsed elsewhere

An unindented activity keyword, then any number of body lines. A body line is either completely blank or indented by four spaces, after which anything goes until the end of the line.

The body is not parsed here

That comment on the body_line rule is the important part. The xsm parser deliberately does not interpret the activity body. It collects the lines as raw text and hands them off, because the body is written in an action language — Scrall in my case — with a grammar of its own and its own parser.

This separation is why an xsm file will happily parse even if the action language inside it is nonsense. A typo in Doors secure -> /R4/Cabin won't be caught until a downstream module parses the activity. Don't read a clean xsm parse as a clean state model.

It also means the xsm language doesn't constrain your choice of action language. If you write activities in something other than Scrall, the xsm grammar won't object.

Empty activities

A state whose activity does nothing still needs the keyword:

state OPENING
activity
transitions
    Door opened > OPEN
    Passenger close > CLOSING
--

That's a real state from the Door lifecycle, and it's the most common thing people get wrong when writing xsm by hand. state_block = state_header activity transitions? block_end — no ? after activity. Leave the keyword out and you get a parse error complaining that it expected 'activity'.

Waiting states frequently have empty activities. That's the whole point of a waiting state: arrive, do nothing, wait for an event. Some modelers drop in a comment rather than leave it bare, which is why you see things like this in the R53 assigner:

state NO TRANSFER
activity
    // Waiting for service request
transitions
    Service requested > Search for new destination
--

Comments in the body are just body text as far as this grammar is concerned.

Indentation

Every non-blank body line must start with the four space INDENT. Beyond those four spaces you can indent as deeply as you like — the action language may well have its own block structure, and the R53 and Door activities do use deeper indentation for continuation and nesting:

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

What the parser gives you

A list of strings in the activity field of StateBlock_a, one per body line. The leading four space INDENT is stripped and the trailing newline is retained; any deeper indentation is preserved, since the action language parser is going to need it. A state with an empty activity gets an empty list.

Clone this wiki locally