Issue #557: Build ADR-339: the grammar language gets span(p), here, same_line; then the compiler’s own grammar (parser/mod.rs) moves to Nikaia

Open and answer this issue on GitHub.

language, selfhosting, grammar, ready · opened 2026-10-10 · updated 2026-11-02 · 3 comments

How it came up

The parser self-hosting session (2026-10-10, #125) moved everything around the compiler’s grammar! block (numbers, parse errors, tree building, the interpreter) and stopped at the block itself: 3 185 lines, 318 rules, written in Rust. It uses three things Part II 10.8 does not offer: the span a rule matched (@=, 52 rules, 78 uses), a declared parser state (state Trivia: newline before a token, /// docs) and hand-written rules (extern rule).

Decided (2026-10-10, 0.0.756)

ADR-339 and Part II 10.8, on the owner’s acceptance of the proposal:

Not built yet; the specification text is ahead of the compiler.

To build

  1. Check first: an action that fails inside an alternative behaves as 10.8’s cut says, and its message survives in ParseError. Test: rule n -> i64 = t:text(digit+) { parse(t) } where parse fails, inside a | b.
  2. span(p) and here: grammar front end, checker, lowering. Tests: span(p) equals the range the Rust _span gives for the same rule on examples/json input; here consumes nothing. Span is declared in std::text and tools/ast.nika uses that one.
  3. same_line: the generated parser records by position what its last WS skip held. Tests: a\n[0] and f\n(x) are not an index and not a call (ADR-307 D4); a [0] is.
  4. Port in chunks of about 40 rules into a .nika grammar, running tests/ and tests/errors/ after each; /// as a rule (ADR-135 D3’s cases and a stray /// as a parse error); the number rules become lexical rules calling parse_numbers.
  5. Remove the Rust grammar! block and the Trivia state. The commit that does so says Fixes #557.

Comments

keywan-ghadami, 2026-10-28

Node ids decided (owner, 2026-10-28): ADR-383, built by #903. The grammar draws no ids from Trivia; a numbering pass right after the parse gives them and writes the span table. What ADR-383 leaves to this issue: semi, ran, comment/block_comment (ADR-337 D2’s comment list) and the trailing-trivia cut PrTrivia::cut that at applies to a span - step 2 (span(p)) must end a span at its last token for the span table to stay byte-identical.


Generated by Claude Code

keywan-ghadami, 2026-11-02

The order for steps 4-5 is decided in #1188 (owner, 2026-11-02):


Generated by Claude Code

keywan-ghadami, 2026-11-02

Step 4a is answered: no. The Rust grammar! block cannot call a rule of another grammar! block, including a lowered Nikaia grammar, at winnow-grammar 4f8e6691.

Calling across grammars chunk by chunk would need a sub-rule export in winnow-grammar: a pub inner rule generic over the caller’s error type, with no entry, no trailing skip and no end-of-input check. That would be a change to the owner’s crate, so I am not making it.

So, per #1188, the plan is:

  1. Port the rules side by side.
  2. Compare them on every tests/compiler and tests/errors program.
  3. Switch over in one commit at the end.

Step 4b, the helpers, starts now, beginning with the error conversion and the findings.


Generated by Claude Code

Back to Open Issues