Skip to content

1. Hand-written recursive-descent parser, not Lezer/tree-sitter

  • Status: Accepted
  • Date: 2026-06 (v1.0)

Context

ArchLang needs a parser that (a) stays zero-dependency, (b) produces precise byte spans for diagnostics and the LSP, (c) recovers from errors instead of bailing on the first one, and (d) feeds both the compiler and the editor grammars. A common alternative is a parser generator such as Lezer (the CodeMirror parser system) or tree-sitter, driven by a declarative grammar.

Decision

Keep the hand-written lexer (src/lexer.ts) + recursive-descent parser (src/parser.ts). A single grammar token table (src/grammar/tokens.ts) is the source of truth for keywords/operators; scripts/gen-grammars.ts generates the TextMate and CodeMirror grammars from it, and a drift test asserts they stay in sync. Element syntax is parsed through the open element registry, so adding an element is one module — no grammar regeneration.

Consequences

Pros. Zero runtime dependency. Full control over error recovery (the parser emits ErrorNodes and resynchronises at statement starts, so one typo doesn't sink the rest of the file). Byte-accurate spans for every node, which the LSP and the framed diagnostics rely on. The registry-driven element parsing is an extension point a fixed generated grammar could not offer as cleanly.

Cons. We hand-maintain the parser rather than a declarative grammar, and the editor grammars are generated from a separate token table (kept honest by the drift test) rather than sharing the parser's own grammar. For a language this size (a handful of element kinds + a small expression grammar) the maintenance cost is low and the control is worth it.

Amended 2026-08-13. This read "~7 element kinds", the count at the time of the decision; BUILTIN_DEFS (src/elements/defs.ts) now registers 11. The exact number is deliberately not restated here — it is a registry that grows, and the argument turns on the order of magnitude, not the figure. If anything the growth is evidence for the decision: each of those kinds arrived as one ElementDef module through the registry, which is the extension point the Pros above claim a fixed generated grammar could not offer as cleanly.