22. Grammar

file        = [ stylesheet ] { drawn }              # setup block, then drawn statements in source order
stylesheet  = "{" { setup_item } "}"                # the root's setup block; omit when empty
setup_item  = decl | vardecl | binding | rule | define | comment | newline
drawn       = node | text | link | comment | newline   # instances and links interleave; a sequence reads order as time (SPEC 13)

decl        = ident ":" values ";"                  # ';' optional before '}'
vardecl     = css_var ":" values ";"                # --name : value ;
binding     = ident [ "(" [ ident { "," ident } ] ")" ] "=" value ";"  # my_r = 5 ; scale(n) = () ;
rule        = selector style                        # |box| { } , |table| |box| { } , .hot { } , #hero { }
define      = "|" ident "::" ident "|" body         # name :: base, optional children

node        = ident_bars [ string ] [ classes ] [ style ] [ children ]
text        = string [ classes ] [ style ]          # bare content; a styleable leaf, never a box
ident_bars  = "|" ( type [ "#" ident ] | "#" ident ) "|"   # |type| , |type#id| , |#id|
type        = ident
classes     = "." ident { "." ident }               # a worn class chain — .hot, .hot.loud

style       = "{" { decl } "}"                       # declarations only
children    = "[" { node | text | link } "]"         # nodes, text, links — in source order
body        = [ style ] [ children ]                 # define / container body

link        = endpoints op [ endpoints ] { op endpoints }
              [ string ] [ classes ] [ style ] [ label_block ]   # the node tail, on a link head
op          = link_op | draw_op
draw_op     = "||" | "(-)" | "(o)" | "(<)"          # mate, linear, round, angle (SPEC 15)
selector    = sel_unit { sel_unit }                 # whitespace-separated = descendant
sel_unit    = ident_bars | "|-|" | "(-)" | "." ident | "#" ident  # a type(+id), the link type, the dimension type, a class, or an id
endpoints   = endpoint { "&" endpoint }
endpoint    = ( ident | ident_bars ) { "." ident } [ "." index ] [ ":" point ]   # a capsule declares (SPEC 9)
index       = digit+                                 # a 1-based pattern copy — drawing
                                                     #   scope only (SPEC 15.4)
point       = "top" | "bottom" | "left" | "right"    # + corners, center, authored segments
                                                     #   in a drawing scope (SPEC 15.2)
pen_item    = call [ ":" ident ]                     # a draw: item — a pen call, optionally
                                                     #   naming its product (point(): a station)

label_block = "[" { text | node } "]"                # canonical labelsstyleable text leaves;
                                                     #   a node among them is a drawing
                                                     #   annotation (SPEC 15.9)

values      = value_group { "," value_group }        # comma only between list items
value_group = value { value }                        # space-separated scalars
value       = number | percent | string | hex | ident | css_var | call | group
call        = ident "(" [ expr { "," expr } ] ")"    # a call; each argument is an expr
group       = "(" expr ")"                           # a math group — a number or point (SPEC 10.7)
css_var     = "--" ident { "-" ident }
expr        = { ident "=" expr ";" } value_expr [ "," value_expr ]  # locals, then a value or a point
value_expr  = operators, math library, a ternary, calls, groups — the grammar of SPEC 10.7

link_op     = [ start_marker ] line [ end_marker ]
line        = "-" | "--" | "---" | "~"
start_marker = "<" | ">" | "*" | "<>" | card_start
end_marker  = "<" | ">" | "*" | "<>" | card_end   # ER cardinality, either side (SPEC 9)
card_end    = [ "o" | "+" ] ( "+" | "<" )         # [min][max] — min (o/+) hugs the line, max (+/<) outer
card_start  = ( "+" | ">" ) [ "o" | "+" ]         # the mirror — max (+/>) outer, min (o/+) hugs the line

ident       = ( letter | "_" ) { letter | digit | "_" | "-" }
number      = [ "+" | "-" ] ( digit+ [ "." digit+ ] | "." digit+ )
percent     = number "%"                             # colour components only
hex         = "#" hexdigit { hexdigit }              # 3, 4, 6, or 8 hex digits
hexdigit    = digit | "a"…"f" | "A""F"
string      = '"' { char | escape } '"'
escape      = "\" ( '"' | "\" | "n" | "t" )
comment     = "//" { not-newline } newline

Single-pass LL(1). The stylesheet-first rule plus the bracket-and-bars vocabulary make one token of lookahead enough — the first token of every statement tells its kind (a leading capsule resolves node-vs-link on the single token after its closing bar, SPEC 1): in the stylesheet, |…| → a rule or (with an inner ::) a define, .name → a class rule, #name → an id rule, --name : → a variable, ident : → a root declaration, ident = or ident (…) = → a binding; after it, a drawn statement is a node (|…|), text ("…"), or — when a bare ident is followed by a link-op, &, or a . path — a link. A declaration ends with ; (its value may span lines); a statement ends at a newline or ;.

The comma law rides values. value_group { "," value_group } is the whole mechanism: a comma between repeated list items, a space between one item’s components, pipelines (draw:, mirror:) one space-separated group (SPEC 2). The parser preserves the shape; each list reader enforces it with a targeted correction — a space-separated list errors as `data` takes comma-separated values — `data: 9, 15, 24`.

Adjacency tells a .class from a path; a : tells a side. A space before the . makes it a worn class (a .hot), no space an endpoint path (a.b); the first class is spaced from the identity, the rest of the chain glues (.hot.loud); a : after an endpoint forces a side (a:left), distinct from the declaration : by position.

Every layout reuses this grammar; drawing extends it, schematic adds one relaxation. Charts and sequences add no lexer or parser grammar — they are nodes, declarations, and children, distinguished by type name and by the scope’s layout (SPEC 13, SPEC 14). The drawing layout (SPEC 15) adds exactly seven things:

  1. the four draw_op tokens — glued, like every link op; || is resolved in the parser from two adjacent pipes at operator position only, so bars stay paired and selectors are untouched;
  2. the one-ended relaxation — the right-hand endpoints may be omitted for <-, *-, >-, (<), and must be for the unary-only (o); the binary (-) and || require both ends. (Meaning only in a schematic scope, the wire ops - -> -< -<> -* may stand one-ended before a string or capsule — the label wire of SPEC 16.5.) One token of lookahead decides: after the op, an ident or a | opens an endpoint (bars: a capsule); a string, ., {, [, or end-of-statement is the tail;
  3. the widened endpoint point set in drawing scope;
  4. the numeric copy index in an endpoint path — plate.bolt.2; the lexer glues . + digits in endpoint position only, so 1.5 in value position stays a number;
  5. the (-) dimension-family sel_unit at a stylesheet statement head — a leading ( there is unambiguous, calls and groups appearing only in value position;
  6. the annotation node among a label_block’s labels — parsed everywhere, meaning only on a drawing’s dimensions and leaders: a core routed link’s [ ] stays text-only and a node there errors at resolve, SPEC 15.9;
  7. the pen_item form inside a draw: value.

A call’s ( glues to its name; a free-standing (…) is a math group and a free-standing (-), (o), or (<) an op (SPEC 2). The pen calls, grid / radial, and hatch are call names, contextual before ( like rgb / repeat (SPEC 23).