fix: prefix-binop operand expands known-arity calls#332
Merged
Conversation
`wh >len q 0{...}` failed on first attempt for every persona because
`parse_prefix_binop` parsed both operands via `parse_operand` (atom-only).
`>len q 0` became `BinOp(>, Ref(len), Ref(q))`, leaving `0` orphaned and
the surrounding `wh` header tripping ILO-P003.
Add `parse_prefix_binop_operand` that dispatches to `parse_call_arg` when
the ident at the cursor is a known-arity function AND the next token can
start another operand. Falls back to `parse_operand` otherwise, so bare
locals that shadow user fn names (e.g. `&e f{...}` where `f` is a local)
still resolve via `Ref` instead of expanding into a zero-arg call.
Mirrors the prefix-`??` swap from #310 with the added shadow-safe guard
that vm_and_does_not_clobber_left_operand caught.
Covers the originating gis-analyst shape `wh >len q 0{...}` plus guard
form, let-RHS, prefix-ternary, equality, both-operands-as-calls, and the
parenthesised right operand. Pins the bare-local fallback so `wh >v 0`
keeps working (no regression for the existing prefix-binop shape from
wh-gt-condition).
Tree, VM, Cranelift.
In-context demo of `wh >len q 0`, guard form, prefix-ternary, and a two-list bound. Runs across tree and VM via examples_engines.rs so any future regression to the prefix-binop-call path gets caught by the existing example harness, not just the parser-specific regression file.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
7 tasks
danieljohnmorris
added a commit
that referenced
this pull request
May 17, 2026
The cascading parse-error these three tests asserted no longer fires: #332's parse_prefix_binop change (use parse_call_arg for both operands, mirroring #310's NilCoalesce precedent) made the previously-malformed shape parse cleanly. The program kc x:t>n;len x;body k:t>t;k;main>n;kws=["hi" "there"];fld (a:n k:t>n;+a kc body k) kws 0 now returns 7 on all engines. The diagnostic the tests checked is still present in the parser for shapes that do cascade, just not this one.
4 tasks
danieljohnmorris
added a commit
that referenced
this pull request
May 17, 2026
fifteen fixes since 0.11.5, all from rerun5/rerun6 personas plus standing asks: ListView foundation (#334), window-text-perf reshape via ListView (#336), inner-flt predicate inlining (#340), double-minus trap ILO-P021 (#331), bare-ident bang silent-nil regression (#324), Cranelift JIT span plumbing (#335), bool-prefix ternary (#330), wh prefix-cond reparse (#332), --run-engine auto-pick main (#329), subcommand helper hyphens+non-ident (#328), runtime error spans (#335), persona-diagnostic batch 3 (#327), rgxall1+ct (#333), single-line body diagnostic (#322 carry), lambda type-var defensive test (#326), N-deep prefix arity error (#339), prefix-minus span column drift (#338), doc-sync (#337).
5 tasks
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.
Summary
Six-rerun P1 standing:
wh >len q 0{...}failed on first attempt across every persona (gis-analyst, nlp-engineer, others), forcing a bind-first or paren retry every time.Root cause:
parse_prefix_binopparsed both operands viaparse_operand(atom-only), so>len q 0parsed asBinOp(>, Ref(len), Ref(q))and orphaned0— fatal in awhheader where the surrounding context immediately expects{and emits ILO-P003.Fix mirrors the prefix-
??swap from #310: newparse_prefix_binop_operandhelper dispatches toparse_call_argwhen the ident at the cursor is a known-arity function AND the next token can start another operand. Falls back toparse_operandotherwise, so bare locals that shadow user fn names (&e f{...}wherefis a local) keep resolving viaRefinstead of expanding into a zero-arg call. The shadow-safety guard is what kept the FizzBuzz regression (vm_and_does_not_clobber_left_operand) green.Manifesto win: the natural shape parses on first try. No more "wh prefix-cond" retries chewing through tokens.
Repro
Before:
After:
What's in the diff
parse_prefix_binopnow delegates each operand toparse_prefix_binop_operand, which opts intoparse_call_argonly when the next ident is known-arity AND followed by an operand-startable token. Suppression set (records, field access, zero-arg paren, postfix-bang) mirrors the existing one inparse_call_arg.tests/regression_prefix_binop_call.rscovers the originatingwh >len q 0shape plus guard form, let-RHS, prefix-ternary, equality, both-operands-as-calls, parenthesised right operand. Pins the bare-local fallback (wh >v 0keeps working). Tree + VM + Cranelift.examples/wh-prefix-call.iloruns throughexamples_engines.rsso the working shapes are in-context examples for future agents and the example harness catches any regression independently of the parser-specific test.Notable behaviour change
+f gwherefis a 1-arity user fn now parses asBinOp(+, Call(f, [g]), <next>)instead ofBinOp(+, Ref(f), Ref(g)). The bare-ref shape was never meaningful (you can't add two function references), so this is a clean upgrade — consistent with the same trade??made in #310. If a real program breaks it's an easy bind-first rewrite.Test plan
cargo test --release --features cranelift— full suite greenregression_prefix_binop_calltree/VM/Cranelift all passexamples_enginesharness picks up the new examplevm_and_does_not_clobber_left_operand(FizzBuzz fixture) still green after the shadow-safe guardregression_wh_gt_trap(existingwh >v 0cases) still greenregression_prefix_nil_coalesce(the precedent fix from fix: prefix??accepts call expression as value side #310) still greenFollow-ups
None for this PR. The analogous prefix-ternary (
?>len q 0 a b) is already covered by the new tests and works because prefix-ternary's condition flows throughparse_prefix_binop.parse_prefix_ternary's then/else operands still useparse_operand— that's a separate trade-off worth opening only if a persona hits it.