fix(parser): prev of binds unary-or-tighter, rejects non-name operand (#634)#667
Merged
Merged
Conversation
…perand (#634) prev of was the only `of` in the language that did not bind unary-or-tighter. The operand parser called parse_expression where every other `of` uses parse_unary (the OF handler's documented rule), so `prev of x + 1` parsed as `prev of (x + 1)` and silently returned null — the whole trailing expression was swallowed. It is now `(prev of x) + 1`, matching SPEC.md's Application rule (item 2), which prev was the lone exception to. Every occurrence in the suite was written parenthesized — `(prev of x)` — so the unparenthesized form the spec's own rule requires was never exercised. Secondary silent-tolerance, from the parser's own comment ("only IDENT operands are meaningful"): a non-name operand — a literal, an index/dot, or a parenthesised expression — has no assignment history to look back through, yet returned null rather than erroring. It is now a clean parse error, so the program does not run instead of propagating a null. Interactions verified unchanged: the `at` suffix (`prev of x at L`), the bare `prev` identifier fallback with postfix (`prev[0]`, `prev.k`), and test_temporal / test_soft_keyword_idents all still pass. parse_unary needed a forward declaration before parse_primary. Tests: SK19/SK19b/SK19c in test_soft_keyword_idents.eigs assert the unparenthesized +/*/- forms (fail before the fix, which returned null); a new [70b] runner block asserts `prev of (x+1)` is a parse error and does not run. SPEC.md's temporal section documents the operand rule. Also backfills the CHANGELOG [Unreleased] Fixed entries for #630, #632, #631, which merged without them. Suite 3086/3086 release, 3090/3090 ASan+UBSan (detect_leaks=1, tally 0); doc examples 71/71, doc-drift clean.
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.
Closes #634
prev ofwas the onlyofin the language that didn't bind unary-or-tighter. The operand parser calledparse_expressionwhere every otherofusesparse_unary, soprev of x + 1parsed asprev of (x + 1)and silently returned null — the whole trailing expression was swallowed.It's now
(prev of x) + 1, matching SPEC.md's Application rule (item 2: "ofbinds tighter than arithmetic:f of x + 1is(f of x) + 1") — whichprevwas the lone exception to. Every occurrence in the suite was written parenthesized, so the form the spec's own rule requires was never tested — textbook "works only because the examples are too easy."Secondary silent-tolerance
The parser's own comment says "only IDENT operands are meaningful", yet a non-name operand returned null rather than erroring.
prevlooks back through a variable's assignment history — a literal, index/dot, or parenthesised expression has no trajectory. It's now a clean parse error, so the program doesn't run instead of propagating a null:Interactions verified unchanged
This was the design-sensitive part —
previs a soft keyword with special forms:atsuffix:prev of x at Lstill works (the suffix is parsed after the operand).prevas a variable with postfix —prev[0],prev.k— untouched (different parse branch).test_temporal.eigsandtest_soft_keyword_idents.eigsboth still pass.(
parse_unaryneeded a forward declaration beforeparse_primary.)Tests
test_soft_keyword_idents.eigsSK19/b/c: the unparenthesized+/*/-forms — each returned null before the fix.[70b]:prev of (x+1)is a parse error and the program doesn't run.Also backfills the
[Unreleased] FixedCHANGELOG entries for #630, #632, #631, which merged without them.Gates
detect_leaks=13090/3090, leak tally 0🤖 Generated with Claude Code