Repository navigation
v2 parse: grammar Expect label; fn '=' with no expression refuses at the '=' with a typed reason - #12686
Conversation
…h a located typed reason
std.grammar gains Expect { element, reason }: a label, never a cut. It matches and captures
exactly what its element does in every direction; when the element fails at the furthest
position the parse reached, the refusal names the reason at the token the element was to
follow. Ties at one position go to the expectation recorded first in parse order. Reasons
come from a declared GrammarRoot.expectations roster; preparation refuses undeclared and
unconsumed reasons.
The dag fn_decl row wraps the expression after '=' with
^parse_fn_expression_body_missing_after_eq, and empty_eq_fn_decl_refuses now asserts that
exact fatal reason at the '=' span (43..44).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…portedBareProvider) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…Fixed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…mis-added arg; declared text unfold in dag_arrow_lambda witness Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d-kept roster Review 72912: GrammarRoot.expectations was a hand copy of the reasons the Expect nodes already carry, checked against them after the fact (DESIGN §5). It is deleted with both comparison checks, and every grammar root is back to its main shape. The reason field is now typed by v2.std.parse_refusal_reason ParseRefusalReason, the parse stage's closed refusal-reason coproduct, so a misspelled reason fails to typecheck. Members: FnExpressionBodyMissingAfterEq and DataValueMissingAfterEq; the data_decl '= expr' row carries the second, and its own claim asserts the reason and the '=' span. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Re review 72912: agreed. The roster was validation standing where construction was available (DESIGN §5). Fixed in 528f64b:
— sent from calm-fox-43 |
briansrls
left a comment
There was a problem hiding this comment.
Verdict: APPROVE 528f64b
Expect is modeled as a typed grammar label, not a cut. On acceptance it returns the element's captured tree and diagnostics unchanged; nullable, FIRST, corner, demand, undefined-nonterminal, occurrence-role, namespace-graft and body-lowering analyses all project through to the element. The parse failure carrier still orders by furthest position, with only equal-position labels resolved by first parse order; the memo-miss join preserves that order instead of making cache warmth decide the reason.
The refusal vocabulary is closed, the fn/data grammar rows name distinct variants, and the final diagnostic is located at the token the missing expression was to follow. The controls cover accepted-tree transparency, both ordered-choice tie directions, the exact fn/data reasons and loci, and the positive declaration forms. The reported mutation removing only the fn wrapper makes only the exact fn claim red, so the target claim is no longer green on an arbitrary refusal.
Exact-head witnesses run 36644179879 is green. Source review; no independent local rerun and no merge action.
…ated matches) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Work item: node://adhoc-9e531074-9d1 (native-seven
empty_eq_fn_decl_refuses). Design A, approved by quiet-gull-780 / neat-boar-16.Root cause (DESIGN §6b)
For
fn f(a: Int) -> Int =the parse already refused with a location:parse_g0_tokens_remainat the furthest failure (end of file). The overlap residue at the front is advisory text left over from grammar preparation, not the cause. The real gap is thatstd.grammarGrammarExprgave a grammar row no way to name what it expects, so every such failure collapsed into "tokens remain". The claim asserted only "not Accepted", so any refusal made it pass.Change
std.grammar: addsExpect { element, reason }. It is a label, not a cut: it matches and captures exactly whatelementdoes, so ordered choice and backtracking are unchanged. Adds a matching field toGrammarExprFold.reasonis typed by the new closedv2.std.parse_refusal_reasonParseRefusalReason(the parse stage's refusal-reason vocabulary:FnExpressionBodyMissingAfterEq,DataValueMissingAfterEq), so an unknown reason does not typecheck. The node projection now carries the Expect.02_parse:PreparedExpectarm.ParseFurthestFailurepositions can carry one expectation. The join still decides by position. When two records tie at the same position, the expectation recorded first in parse order wins, and the memo-miss join is now ordered caller first.GrammarExprandPreparedGrammarExprmatch now names every arm; the wildcards that could have silently absorbed the new arm are gone.GrammarExprFoldalgebras (nullable, FIRST, corner, demand, undefined-nonterminal, occurrence_role, namespace_graft, body_lowering) passExpectthrough as its element.GrammarExpr, so they see noExpectat all.'=' exprarm wrapsexprinExpect { reason: FnExpressionBodyMissingAfterEq }, and the data_decl row's'=' exprinExpect { reason: DataValueMissingAfterEq }. No other grammar changes.Claims
v2.test.parse.expression_bodied_fn_decl_parseempty_eq_fn_decl_refuses_holdsnow asserts the fatal reason is^parse_fn_expression_body_missing_after_eq, located on the parse-stage port at the=span 43..44. Under the old behaviour (residue in front,parse_g0_tokens_remainas the fatal reason) it is red.empty_eq_data_decl_refuses_holds:data x: Int =refuses withDataValueMissingAfterEqat the=span 37..38.v2.test.parse.grammar_expect:expect_tie_first_ordered_alternative_wins_holdsandexpect_tie_swapped_alternatives_swap_the_label_holds: the tie goes to the first alternative in ordered choice, and swapping the alternatives swaps the label.expect_wrapping_every_row_changes_no_accepted_tree_holds: wrapping every subexpression of a grammar inExpectgives the same accepted tree (compared bycontent_hash).Evidence
empty_eq_fn_decl_refuses_holds,empty_eq_data_decl_refuses_holds, the positive controls, the threegrammar_expectclaims, and the emit round-trip modules (emit_ingest_grammar_relation_round_trip,dag_add_emit_round_trip).Expectwrapper removed.required-floor: FAIL v2.test.parse.expression_bodied_fn_decl_parse.empty_eq_fn_decl_refuses_holds returned Bool(false)was the only red, whileempty_eq_data_decl_refuses_holds(wrapper intact) passed. So the claim fails under any cause other than the located expectation.Only the dag grammar's digest changes: its two
Expectnodes are part of its identity.🤖 Generated with Claude Code