Repository navigation
dag grammar: node let-sugar guard is its whole head (fix forward #13056) - #13133
Merged
Merged
Conversation
… | =) A bare node statement followed on the next line by a name-led statement (the next match arm, an if) began 'node name', which the two-token guard excluded from the expression alternative while the sugar, lacking its ':' or '=', refused too: parse_g0_tokens_remain on five corpus files, among them src/v2/std/node.dag. Fixes forward #13056. Route controls: the reported fixtures a1/a3 take the expression route, and the sugar keeps the let route. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…al-deer-478 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ispositions it) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 3, 2026
Conflicts: main (#13133) added one hand warm row (bns_verdicts, three readers: derived as shared) to the roster this PR deletes; and main (#12526) deleted v2.std.collection List in favour of std.types List, so this PR's two conflicted files take that import (and any from v2.std.algebra, as main's roster file does). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 3, 2026
…_node_statement_route Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 4, 2026
…rim two arms The node-then-if control is the same fixture as #13133's enrolled v2.test.parse.bare_node_statement_route bare_node_statement_before_an_if_takes_the_expression_route; repeating it here was duplicated evidence (and the claim the floor priced highest, 95797 steps). The bool-literal and contextual pattern-binder controls now end their single arm in '=> 0'. The floor's own measurement on 49d1886 put only these three over the 72300 new-witness budget. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes forward #13056, which merged with a parse regression reported by tidy-raven-393 and calm-boar-904.
Defect
dag_grammar_let_sugar_head_exprguarded the expression statement with!(binding_name "=" | "node" binding_name). The node let-sugar isnode name (: | =) expr, so the guard was one token short. Statements of the following shapes start withnode <name>without being sugar:nodestatement followed on the next line by a statement that starts with a name;=> nodefollowed by the next arm;nodefollowed byif.The guard excluded these from the expression alternative, and the sugar also refused them. The result was
parse_g0_tokens_remain.Fix
The guard is now the sugar's whole head:
"node" binding_name (":" | "="). That is three tokens, inside the lookahead cap of 3, so the choice-overlap proof still reads it and the roster stays at 0.Controls
v2.test.parse.bare_node_statement_route. Each claim is a route claim reading the stored Bool from the warmbns_verdicts:=> nodebefore the next arm) and a3 (nodebeforeif), verbatim. Each must parse with zero let_expr nodes, i.e. it takes the expression route.node y = 1must still take the let route (exactly one let_expr).These are red-first: the two fixture claims fail on main and pass here. The sugar claim passes on both. (Red/green for these claims: from this PR's CI floor run.)
Corpus
A scan of
dagandsrc/v2for a statementnodeat end of line followed by a name-led line finds 6 files. Each is parsed whole with the dag grammar, on main and on this branch:src/v2/std/node.dag(N7 closure)dag/gunbc/scm/object_store.dagsrc/v2/workflow/realization_runner.dagsrc/v2/test/claim/wrap_decision_predicate_test.dagsrc/v2/test/claim/verilog_interlock_emission_test.dagsrc/v2/std/model_core.dagMeasured (one remote dispatch, same binary, each file's whole text parsed as a module with the dag grammar): all 6 refuse on main, all 6 parse on this branch.
Why #13056's evidence missed it
The 32-file parse was a sample, not the corpus. It selected the files carrying the sites the change was meant to touch. None of the six files above was in it: a whole-file parse of any of them refuses at the guarded head. A whole-corpus parse would have caught this; the sample could not.
The census enumerated sites whose meaning the change was meant to alter. It never enumerated what each new guard excludes. A guard must not exclude any input the sibling alternative fails to accept: if it does, it removes inputs rather than disambiguating them. Here the guard excluded
node namewhile the sibling only acceptsnode name (: | =).What evidence would have caught it:
I'll propose the first as a check in the prefix-table PR that follows, since it reads the same per-production prefix table.
🤖 Generated with Claude Code