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 labels — styleable 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:
- the four
draw_optokens — 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; - 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; - the widened endpoint
pointset in drawing scope; - the numeric copy
indexin an endpoint path —plate.bolt.2; the lexer glues.+ digits in endpoint position only, so1.5in value position stays a number; - the
(-)dimension-familysel_unitat a stylesheet statement head — a leading(there is unambiguous, calls and groups appearing only in value position; - 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; - the
pen_itemform inside adraw: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).