2. Lexical Syntax
| Property | Value |
|---|---|
| Extension | .lini |
| Encoding | UTF-8 (BOM ignored) |
| Line endings | LF or CRLF (normalized on read) |
| Comments | // … to end of line. No block comments. |
| Statement end | A node/link/text statement ends at a newline or ;. A declaration ends at ; — its value runs to that ; (or a closing }), so a value may span lines. |
| Identifier | [a-zA-Z_][a-zA-Z0-9_-]* — case-sensitive, ASCII, dash-case |
Whitespace is insignificant except as a token separator and where a rule below says otherwise:
| Form | Whitespace rule |
|---|---|
|…| | Identity in bars: a type, an optional #id (|box#cat|), or an id alone (|#cat|). :: is the define operator (|cat::oval|). Bars are paired; surrounding space at the boundary is not allowed. |
#id | Inside the bars it names the node’s id; at a rule’s head it is an id selector (#cat { }). A # followed by hex digits in a value is a colour (#f80); the two never meet — one heads a statement or sits in bars, the other is a value. |
key: value | : separates name and value; surrounding space optional, canonical is one space after (radius: 5). |
a:side | A : after a link endpoint forces a side (a:left). Distinct from the declaration : by position — it follows an endpoint, never opens a value. |
.name (class) | At a rule head it is a class selector / definition (.hot { … }). On an instance, link, or text leaf it is a worn class, following the identity — spaced off it (|box| .hot, a -> b .loud, "x" .quiet), the rest of the chain glued (.hot.loud). |
id.child | No space — an endpoint path into a child (kitchen.bowl). |
--name | A variable, in a value or at a statement start to declare one. |
| link op | [marker?] line [marker?], glued, no internal space (->, --->, <->). |
[ … ] | A content list. Paired; whitespace inside is insignificant. |
Strings — double-quoted UTF-8: "…". Escapes: \", \\, \n, \t. A
double-quoted string is always text; leading and trailing whitespace in its value is
trimmed (" ABC " is “ABC”, and a spaces-only " " becomes ""), so source
spacing never leaks into the render.
Single quotes are not strings (reserved, SPEC 23).
A bare word is an identifier, never a string. In a value, an unquoted word is
always an identifier — a keyword, a colour or symbol name, a font-family, or an id
reference — so literal text is always quoted: a string-valued property (title,
hint, href, src, path) takes a "…" even with no spaces. The one hybrid is a name that
may contain spaces — font-family — bare or quoted, quoted only when needed
(font-family: "SF Mono"), as in CSS. Numbers and (…) expressions are bare too;
only text is quoted.
Expressions — a parenthesized region (…) is a compile-time math expression,
folded to a literal number (or a point); parentheses are the only place operators
appear, a call’s own parens count (up(5 * r, 10)), and groups may span lines
(SPEC 10.7).
Numbers — integer or decimal, optional sign, no units (px for lengths, degrees
for angles, 0–1 for opacities/fractions). 10, -5, 0.25, +3. A trailing %
makes a percentage (50%), valid only in colour components.
The comma law — CSS’s rule, stated once. A comma separates repeated
list items: data: 2, 3, 4 · columns: 80, 140, auto · points: 0 0, 10 10 ·
categories: "Q1", "Q2" · along: 0.2, 0.5, 0.8 · align: start, center, end.
A space separates the components of one item, tuple, interval, or
shorthand: padding: 5 2 5 5 · shadow: 2 2 4 #0003 · translate: 10 -4 ·
range: 0 100 · cell: 2 1 — and so data: 10 20, 30 40 is two x y points,
and a lone data: 10 20 is one point, never two values (a value list is
comma-separated: data: 9, 15, 24). A pipeline of calls that folds into one
value stays space-separated, like CSS transform — draw: move(0,0) up(8) fillet(2), and mirror:, whose items each reflect the union so far.
Functions use parentheses and sit in value position —
rgb(…), hsl(…), repeat(…), the math library, and any you bind
(SPEC 10). A call’s ( glues to its name
(rgb(…), never rgb (…)); a free-standing (…) is a math group, and a free-standing
(-), (o), or (<) a measuring op (SPEC 15.6) — which is how
move(-2, 5), (8 * 2), and pin (o) never meet.
Colours — #fff, #f80c, #ffaa00, #ffaa00cc (3/4/6/8 hex digits; the 4-
and 8-digit forms carry alpha), CSS names (red, cornflowerblue), rgb(…),
rgba(…), hsl(…), hsla(…) (percentages allowed — hsl(200, 50%, 50%)),
oklch(L, C, H[, A]) (the palette’s own space — L/A in 0–1, C the chroma, H in
degrees; folded to a hex at compile time, so it renders in every target), a
--name variable reference, or none. Out-of-range channels are an error. Beyond
a flat colour, a paint (fill / stroke / gap-fill) may be a gradient —
gradient(…), linear-gradient(…), or radial-gradient(…) — reached, like the
built-in hue palette, through the colour system (SPEC 10).