Repository navigation
Eliminate all Menhir parser conflicts (75 S/R + 10 R/R) — dedicated grammar-conflict workstream #215
Description
Goal
Eliminate the Menhir parser conflicts that print on every dune build
(currently 75 shift/reduce + 10 reduce/reduce, post-#214) — they
alarm users into thinking the toolchain is broken when it is not. This
Menhir (20260209) has no %expect/--expect mechanism (verified
via --help), so there is no honest "declare reviewed" silencer: the
warnings can only be removed by genuinely resolving each conflict
(explicit precedence / token de-overload / left-factoring) with
provably-identical parse behaviour — the full dune test --force
gate (257/257) must stay green at every step.
Dedicated, rigorous, one-conflict-family-per-PR effort. STAGE-C (#160)
is paused until this lands or is explicitly de-prioritised.
Root-cause families (from menhir --explain)
- A. Effect-row
MINUSoverload —type_expr_arrowlegacy form
T -{ E }-> U(parser.mly:410) shares theMINUStoken with
arithmetic minus; conflicts with the base reduce
type_expr_arrow -> type_expr_primary(parser.mly:419) on lookahead
MINUS/ARROW(state 41). A newer SLASH-based effect-row form
(parser.mly:219/230) already exists specifically to avoid this. Menhir's
shift is always correct (no subtraction in type position). - B. Bare
return <expr>greedy operator-continuation —return e
whereecan continue with a binary operator; single root, many
operator-token states (217/220/226/228/236/247…). - C.
{block-vs-record-literal aftertry/match/if/return/
resume(states 101/108/109/134/206…). - D. Block-expression vs block-statement reduce/reduce (states
185/362/369/401).
Ordered plan (one PR per family; 257-gate + conflict-count delta each)
- PR-1 — Family A (effect-row MINUS) (user-prioritised first):
make the state-41 resolution explicit via precedence that codifies
the already-correct shift; behaviour-preserving, no surface-syntax
change. If precedence cannot fully clear it, escalate the legacy
-{E}->removal as a surface-syntax design fork (separate decision). - PR-2 — Family B (return greedy): restructure
return/resume
to take a fullexprso the operator-continuation is unambiguous. - PR-3 — Family C (
{block/record): left-factor the
block-vs-record decision (the well-known Rust/Swift-style fix). - PR-4 — Family D (block expr/stmt R/R): resolve the residual
reduce/reduce. - PR-5 — sweep: re-run
menhir --explain, confirm zero conflicts,
remove now-stale conflict commentary, document indocs/.
Each PR: rigorous root-cause (not suppression), dune test --force
257/257 before commit, conflict-count reported in the PR body, and a
note if any generated-automaton behaviour could differ (it must not).
Done when
dune build prints zero conflict warnings, gate green, no
surface-syntax regression (or any surface change explicitly approved).
🤖 Generated with Claude Code
Activity
fix(parser): resolve state-41 effect-row MINUS/ARROW S/R conflict via…
hyperpolymath commentedon May 18, 2026
PR-1 (family A, effect-row MINUS) done — #216 merged. S/R 75→72, conflict states 25→23, state 41 eliminated, R/R untouched, 257/257 green, zero new conflicts.
New finding → add family F:
menhir --explainalso emits 16 pre-existingprecedence/%prec ... never usefulwarnings (BANG, DOT, EQ, LBRACKET, LPAREN, MINUSEQ, PLUSEQ, SLASHEQ, STAREQ, TILDE, UDEREF, UMINUS, UREF + 3 %prec). These are dead precedence declarations — provably behaviour-neutral to remove (Menhir itself certifies they never resolve any conflict). Verified byte-for-byte unchanged across #214→#216, so not regressions. Good low-risk cleanup PR for the sweep phase. Updated plan: PR-2 family B (return greedy), PR-3 family C ({ block/record), PR-4 family D (block expr/stmt R/R), PR-5 family F (dead precedence) + sweep.fix(parser): remove dead precedence declarations [#215 family F] (#217)
feat(#218)!: complete Rust-like record migration — 257/257 green
hyperpolymath commentedon May 18, 2026
Disposition (ADR-012) — residual conflicts: intentional won't-fix
Per ADR-012 (docs/specs/SETTLED-DECISIONS.adoc + .machine_readable/6a2/META.a2ml; PR #224):
Done & merged on correctness grounds (not conflict-count grounds):
[T]removed · fix(parser): resolve state-41 effect-row MINUS/ARROW S/R conflict (75→72) [#215 family A] #216 family A (effect-row MINUS) · fix(parser): remove 16 dead precedence declarations [#215 family F] #217 family F (dead precedence) · fix(parse): map Token.EXTERN in Parse.parse_file token bridge (runtime Match_failure on all extern code) #219 EXTERN parse bug · feat!: Rust-like record syntax — bare { = block, records use #{ } (#218; resolves #215 C+D) #222#{ }records (families C+D, structural) · docs: document Rust-like #{ } record syntax (#218 step 5) #223#{docs.return/resumehoisted to diverging-prefix position (family B) — a correctness change (return : Never, not an operand;(return a)+bnow needs parens). Proven conflict-count-neutral (21→21 S/R states); shipped for semantic honesty, explicitly NOT as a conflict fix.Residual ~68 S/R + small R/R (incl. state 401, block trailing-expr-vs-statement boundary): WON'T-FIX, intentional. These are inherent LALR(1) artefacts Menhir resolves correctly — the 257-case gate proves every accepted parse is the intended one. Eliminating them requires systemic precedence/left-factoring surgery with estate-wide parse blast radius for a cosmetic-only payoff — exactly the grammar contortion ADR-012 forbids. The earlier 'family B = ~12 eliminable S/R' framing was a misread (those were the generic expr-cascade ambiguity surfacing via return-reachable states; relocation re-attributes, does not remove).
Noise concern resolved by disclosure, not silence:
just buildmasks the benign notices but prints the masked count + correctness proof + ADR pointer +just build-loudreveal;dune buildstays fully transparent. Masking is disclosure, not concealment.This issue's residual-conflict scope is settled. Recommend closing #215 as completed once #224 merges (its remaining work is the documented won't-fix above).
feat(grammar)!: return/resume as diverging prefix exprs + ADR-012 + c…
docs: Stage B properly CLOSED — truth the #215 ADR-012 won't-fix (Refs …