Issue #518: A 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

  1. A 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.
  2. A grammar with several 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.
  3. A body that the grammar refuses is a build error, in the grammar’s words, at the block.

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.

Comments

keywan-ghadami, 2026-10-07

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:

That is the owner’s call. Step 1 can be built for the one-entry case without it.


Generated by Claude Code

keywan-ghadami, 2026-10-07

Steps 1 and 3 built in 0.0.620 (a7b798f), for a grammar with one entry.

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:

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

keywan-ghadami, 2026-10-09

Decided (owner, 2026-10-09): B, plus a main entry

The 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).

To build

  1. Parser: main entry rule. Test: a grammar with two entries, one main, parses; two main entries are NK1243.
  2. Block resolution:
    • dsl G::r { … } runs entry r of the program’s grammar G;
    • dsl pkg::G::r { … } runs it from a package;
    • bare dsl G { … } runs the single entry or the main one;
    • several entries and no 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.

  3. The bug from the comment above: two 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

Back to Open Issues