dsl block names its entry (dsl Json::value { … }) unless the grammar marks one main entry rule (ADR-299 D21)Open and answer this issue on GitHub.
compiler, grammar, ready · opened 2026-10-07 · updated 2026-10-09 · 3 comments
Found while working on #505 (develop, 0.0.616).
What the specification says. Part II says a grammar the program declares is named bare: dsl Json { … } eod (ADR-299 D20). Part II’s block #2 shows the Json grammar run at build time on a dsl block’s body.
What the compiler does. The checker accepts the block. The emitter refuses it:
grammar Num {
entry rule n -> i64 = d:dec[i64](digit+) { d }
}
fn main() {
let x = dsl Num { 42 } eod
println(f"{x}")
}
error: `dsl Num { … }` has no `:name` holes, and `Num` isn't a language the compiler knows.
Add `:name` holes for the parameters, or use `dsl html`.
emit::template knows two shapes: dsl html, and a deferred statement with :name holes. A block over the program’s own grammar is neither.
To build
dsl G { … } eod over a grammar G of this program runs G’s entry rule on the body while the program is built, as a comptime grammar call does (ADR-177, ADR-321). The value is built into the program, and its type is the entry rule’s result. That is also #505’s remaining step 1: the checker gives the block that type.entry rules: which one a block uses has to be stated. The specification does not say yet. Ask before building, or refuse with a message that names the entries.Tests: the program above prints 42; let b: bool = dsl Num { 42 } eod is NK1103 (i64); and dsl Num { x } eod is refused at the block while the program is built.
On step 2 (which entry a block uses): ADR-296 D25 says no rule is picked as the grammar’s main one - such a pick chooses wrong silently. So for a grammar with several entries, a dsl G { … } block has no entry to use by that rule. The two readings are:
entry, and a grammar with several is refused with their names;dsl G::value { … }), which D20’s path syntax already parses.That is the owner’s call. Step 1 can be built for the one-entry case without it.
Generated by Claude Code
Steps 1 and 3 built in 0.0.620 (a7b798f), for a grammar with one entry.
comptime of G::entry("<the block's text>"). It goes through comptime_binding, so the evaluator, its bounds and its refusals are the same as for any comptime. The value is recorded under the statement, and the block’s type is the entry’s result.{ const __NIKAIA_DSL: i64 = 42; __NIKAIA_DSL }.NK1178 at the block, in the parser’s words.crates/nikaia/tests/dsl_grammar.rs: dsl Num { 42 } eod + 1 prints 43, let b: bool = … is NK1103 (i64), and dsl Num { x } is NK1178.dsl Json blocks are fragments around the grammar of another block.Open, and the owner’s (now labelled decision). Step 2, a grammar with several entries. Today such a block stays as it was: the lowering refuses it, and there is no silent pick, per ADR-296 D25. The options are in my comment above:
dsl G::value { … }, which the path syntax already parses. Today that path would read as package G, grammar value (D20), so this collides with D20.Also found: one statement with two such blocks shares one recorded value, because the record is keyed by the statement, as a comptime’s is. That is rare, and each block’s own key would fix it.
Generated by Claude Code
main entryThe question. A dsl block over a grammar with several entry rules. ADR-296 D25 forbids the compiler picking one, so the choice was between refusing such grammars (A) and naming the entry where needed (B).
The decision. B: a block names its entry, written as the call is (Json::value(text) → dsl Json::value { … } eod; from a package, dsl json::Json::value { … } eod). The owner added that a grammar with several entries may mark one main entry rule, and a bare dsl Json { … } eod runs it. This is the grammar author’s decision, not a compiler pick, so D25’s reason stands. D25 now carries a Changed by note.
Where it is written: ADR-299 D21, Part II 10.2 and 10.5, and Part III C.3 (0.0.719).
main: no name needed.main: NK1242. The help names the entries, dsl G::entry { … }, and main entry rule.main entries: NK1243.A::B is a rule where A is a grammar, and a grammar where a is a package. One name denotes one thing, so the path is never read both ways.main entry rule. Test: a grammar with two entries, one main, parses; two main entries are NK1243.dsl G::r { … } runs entry r of the program’s grammar G;dsl pkg::G::r { … } runs it from a package;dsl G { … } runs the single entry or the main one;main is NK1242.Tests: one per case, and dsl G::helper { … } on a rule that is not an entry is refused with the existing message.
dsl blocks in one statement share one recorded value. Key each block’s value by the block, not by the statement. Test: let p = (dsl Num { 1 } eod, dsl Num { 2 } eod) holds 1 and 2.Generated by Claude Code