fix: give ^ higher precedence than unary - and + - #17
Merged
Conversation
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.14.1 to 3.14.2. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](nodeca/js-yaml@3.14.1...3.14.2) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 3.14.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Regenerate the parser for the power precedence fix and add a Vitest suite that pins down operator grouping. Precedence in an ANTLR left-recursive rule comes from the order of the alternatives, so reordering them in Expr.g4 changes how expressions evaluate without causing a parse error. The tests render each parse tree as a fully parenthesized infix string so the expectations read as grouping rather than as ANTLR internals. Checked against the pre-fix parser: exactly the five power-vs-unary-sign cases fail and the other 17 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
chrispcampbell
changed the base branch from
dependabot/npm_and_yarn/js-yaml-3.14.2
to
main
August 7, 2026 22:55
chrispcampbell
approved these changes
Aug 8, 2026
chrispcampbell
left a comment
Contributor
There was a problem hiding this comment.
This looks good to me. I moved the lock file and vitest changes out into separate issues/PRs that have already been merged to main, and this branch was updated to be based on the latest main, so it only has the minimal set of changes.
I locally linked this package in my copy of SDE and verified that the compile package tests and test models are still passing after this change.
Merged
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 #16
Problem
In
grammar/Expr.g4, theNegativeandPositivealternatives were listed abovePower. In an ANTLR left-recursive rule, alternative order sets precedence, so unary-bound tighter than^.That makes
-x^2parse as(-x)^2instead of-(x^2), which is a silent numerical error rather than a parse failure. The Gaussian kernel is the case that surfaced it:parsed as
EXP(((-x)^2)/2)— the exponent lost its sign, so the expression evaluated asEXP(x^2/2), growing instead of decaying.Change
Move the
Poweralternative aboveNegativeandPositive:One line moved; no other grammar changes. This matches standard mathematical convention and Vensim's own operator precedence, where
^outranks unary sign.parser/ModelParser.jsis regenerated with ANTLR 4.12.0. The generated diff is confined to theexprrule: thePowerprecedence predicate moves from 12 to 14 and its right operand fromexpr(13)toexpr(15), whileNegativedrops fromexpr(14)toexpr(13)andPositivefromexpr(13)toexpr(12).Effect
Measured by parsing each expression with the pre-fix and post-fix parsers:
-x^2(-x)^2-(x^2)+x^2(+x)^2+(x^2)-2^24-4EXP(-x^2/2)EXP(x^2/2)EXP(-(x^2)/2)-x^2*y((-x)^2)*y(-(x^2))*yx^-2x^(-2)x^(-2)(unchanged)(-x)^2(-x)^2(-x)^2(unchanged)-x*y(-x)*y(-x)*y(unchanged)2^3^2(2^3)^2(2^3)^2(unchanged)x^-2still parses because ANTLR emits prefix-unary alternatives in the rule's primary section with no precedence predicate — only the left-recursive loop is guarded. RaisingPoweraboveNegativetherefore changes which operator wins when both could apply to the left, without making a negated right operand unreachable.^remains left-associative, consistent with Vensim. That is pre-existing behavior, unchanged here, and now covered by a test so it stays that way.Tests
This adds the repository's first test suite, using Vitest.
tests/parse-expr.js— parses an expression and renders the tree as a fully parenthesized infix string, so expectations read as grouping rather than as ANTLR internals:-x^2/2becomes((-(x^2))/2). It fails loudly on syntax errors and on input that parses only in part, sinceexprotherwise stops silently at the first token it cannot use.tests/expr-precedence.test.js— 22 cases covering power against unary sign, power against the binary operators, unary sign against the binary operators, associativity of^,*//and+/-, and precedence among*,+, relational,:NOT:,:AND:and:OR:.Run with:
pnpm testThe suite was checked against the pre-fix parser to confirm it catches the regression: exactly the five power-vs-unary-sign cases fail, and the other 17 pass. That makes it a real guard on alternative ordering in
Expr.g4, not just a restatement of current behavior.README.mdgains a short section on running the tests and on why alternative order in the grammar needs test coverage.Open items for review
package.jsonis still at0.6.3. This is a behavior change for consumers, so it wants at least a patch bump before publishing — arguably a minor bump, since any model relying on the old grouping will silently produce different numbers.tests/will be included in the published package. There is nofilesfield and no.npmignore, sogrammar/andtools/already ship too. Consistent with existing practice, but worth deciding on if you want to trim the tarball.Base branch note
todd/op-precedenceis based ondependabot/npm_and_yarn/js-yaml-3.14.2, not onorigin/main. As a result the diff againstmainalso carries an unrelatedpnpm-lock.yamlhunk (adds thesettings:block withautoInstallPeers/excludeLinksFromLockfile). Rebase before opening if you want the PR to stand alone:Verification status
All groupings in the table above were measured by running the actual parsers, not derived.
pnpm testpasses 22/22 on the regenerated parser and fails 5/22 on the pre-fix parser.