ADR-388: The Table a Grammar Interns Into Is the Caller’s, Handed to the Entry as words:

Status: Accepted Date: November 2, 2026 (supervisor’s decision under the owner’s rule of 2026-10-31 and 2026-11-01, docs/supervisor.md, Language decisions: the form the language already has for an entry’s values, and an extension nobody pays for who does not use it) Answers: the p557f report’s Found: (3) (#557 step 4c): ident hands back a winnow_grammar::Symbol interned into a table the entry makes and drops when it returns, so the caller of a Nikaia grammar cannot read the text of a single symbol in the tree it got back; the compiler’s Rust parser keeps its table in Parsed Related: ADR-296 D8 (identifiers are interned symbols), ADR-grammar-1 (an entry takes values after its input, Yml::block(text, 2; c: Ctx::BlockOut)), ADR-366 (ident over Bytes), ADR-001 (the string interner)

1. Question

Part II 10.6 says the parser interns an identifier into “a shared table” and yields a Symbol. It does not say whose table that is. Today each call of an entry makes its own and drops it at the return: the symbols in the result can be compared with each other, never read as text, and two parses (two files of one program) cannot share their spellings. The tools in std already take the table as a parameter (words: ref winnow_grammar::InternerContext, bounds_basic.nika, calls.nika). Where does a Nikaia program get the table its parse interned into?

2. Decision

D1 - The caller hands the table to the entry as words:

An entry of a grammar that interns (it reaches ident or intern(p)) takes the table as a value after its input, as every entry value does (ADR-grammar-1): ParserGrammar::file(source; words: table), with table a ref winnow_grammar::InternerContext. Every symbol the parse yields is interned there; table.resolve(sym) reads its text after the call. One table handed to several parses gives one spelling one symbol across all of them.

D2 - An entry whose result can hold a symbol must be handed the table

Where the entry’s result type holds a Symbol (in a field, an element, a variant, at any depth), a call without words: is refused at the check: the symbols would point into a table nobody can reach. An entry that interns only on the way (a Symbol read through text(..), compared in a guard) may be called without it and keeps a table of its own for the call, as today.

D3 - The table is made in std

winnow_grammar::InternerContext::new() makes an empty table; its ledger entry describes it beside resolve. The lowering passes the table into the ParseContext the generated entry builds (nikaia_std::grammar), not into winnow-grammar.

3. Why (the principles)

4. To build

#557 step 4c waits on it. The build is #1198 (ready); the refusal of D2 is NK1330 (reserved for that build).

This site is open source. Improve this page.