Skip to content
Leon Starr edited this page Jul 30, 2026 · 1 revision

Inside the braces of a row you supply one value per header column, separated by spaces. There are two kinds:

val = at_val / brace_val
at_val = '@' word
brace_val = '[' ival ']'
ival = r'[^\t\n\r\f\v\[\]]+'

A brace_val is a literal value in square brackets. An at_val is a reference to another instance by its alias. This row from Shaft uses both:

{ [S1] [true] @L }

Why the brackets

The brackets exist so that a value can contain spaces without the parser losing track of where one value ends and the next begins. Everything between [ and ] is taken literally, spaces and punctuation included:

{ [Lower Floors] [4.0] }
{ [Pigsley Central] [3.2] }

Commas, colons, slashes and periods are all just characters inside the brackets, so a value like [a, b: c/d! 3.2] is a single value.

What you cannot put in a value is a [ or ] character — there is no escape for them — nor a tab or a newline. A value also cannot be empty. [] is a parse error, so a class whose scenario leaves an attribute without a meaningful starting value still needs something written in that position.

Everything is a string

The parser does no typing or conversion whatsoever. All of these come back as strings:

{ [Penthouse] [4.0] [25] [false] [.up] }

4.0 does not become a float, 25 does not become an int, false does not become a boolean, and the leading dot on .up carries no meaning to the parser. Interpreting each value against the attribute's type in the xcm class model is the consumer's job, which is also why the parser cannot tell you that you put a floor height where a boolean belongs.

Values are positional

A value is bound to its attribute by position alone, counting from the first column of the Column header. Nothing in a row names the attribute it is filling, so reordering a header without reordering every row beneath it will silently produce a wrong population.

Clone this wiki locally