Internals · Grammar
Grammar
Every line of a Program is read against a small grammar. This page is that grammar in one place: the vocabulary each keyword is written in, before parsing ever nests the lines together into a tree. The grammar is deliberately tiny: a handful of keyword rules over a handful of shared inner rules, bottoming out in a few primitives.
Full syntax
Every keyword's Format section reuses the same handful of inner rules. This is the whole
language in one grammar, in three parts: the keyword rules first, then the
common inner rules they draw on, and finally the base rules for the
primitive tokens everything bottoms out in. A rule written in capitals (like FIELD) is a
keyword; a lowercase rule (like type) is an inner rule shared across keywords. Because
every rule is defined once here, the definitions on the individual
keyword pages stay consistent with one another.
Keywords
One rule per keyword. Each of these corresponds to a node in the parsed tree.
MODULE ::= "MODULE" identifier
FUNCTION ::= "FUNCTION" [ type ] identifier
INPUT ::= "INPUT" [ "BINDING" ] type identifier [ "=" constantExpression ]
HOLE ::= "HOLE" [ type ] identifier
NATIVE ::= "NATIVE" target string
TYPE ::= "TYPE" identifier
FIELD ::= "FIELD" type identifier [ "=" constantExpression ]
OPTION ::= "OPTION" type identifier
LET ::= "LET" [ type ] identifier "=" ( expression | constantExpression )
STATE ::= "STATE" [ "LOCKED" ] type identifier "=" expression
REVERSE ::= "REVERSE" type identifier
SET ::= "SET" identifier "=" expression
WITH ::= "WITH" module "=" replacement
Common
The shared inner rules the keyword rules draw on. Each is defined once and reused everywhere.
type ::= name [ "(" type { " " type } ")" ]
expression ::= name { " " argument }
constantExpression ::= constant | name { " " constant }
argument ::= name | constant
constant ::= string | integer | number
module ::= name
replacement ::= name
target ::= "dart" | "swift" | "js" | "nodejs" | "ts" | "go" | "rust" | "zig" | "glsl" | "php"
name ::= identifier { "." identifier }
identifier ::= letter { letter | digit }
Base
The primitive tokens every other rule bottoms out in: strings, numbers, and the letters and digits they are built from.
string ::= "\"" { character } "\""
integer ::= [ "-" ] digit { digit }
number ::= integer [ "." digit { digit } ]
letter ::= "A" … "Z" | "a" … "z"
digit ::= "0" … "9"
character — any single source code point —
which string is built from.From rules to nodes
Each keyword rule above corresponds to a node in the parsed tree.
A MODULE becomes a module node; a FUNCTION becomes a function node; a
LET becomes a let node. How these individual lines are nested together into that tree
— and checked as they go — is parsing. The canonical
JSON each one is eventually lowered to is listed under
Structure.