fix(parser): correct pure percent addition and preserve relative perc…#3573
fix(parser): correct pure percent addition and preserve relative perc…#3573philpeng1 wants to merge 3 commits intojosdejong:developfrom
Conversation
|
Thanks so much for your submission! Sorry it's taken a few days to get to it.
You didn't put in a test on this case -- is your proposed parse
? I will admit to some concern that the code added to the parser is intricate and may make an already fragile hand-written parser noticeably more tricky to navigate. I will continue to review the PR -- perhaps your solution is how we will need to go to solve this (I would love to solve it once and for all, since it has been a recurring thorn in mathjs' side). The collection of tests is great, we should extend them even more to include as many cases as we can make a reasonable decision on the interpretation of, if possible. |
|
On: I do not think these are the correct/desired parses/behaviors. It seems to me that So your two examples should be 65 and 130, respectively. |
|
As the items I raised above for this PR motivated me to give the alternative proposal of making |
|
@philpeng1 did you see the feedback of Glen? |
…ent semantics; add tests
…etry; update grouping/ambiguous tests
fcca5b3 to
6ba0d3b
Compare
|
hi, sorry for the late response! Been dealing with some other stuff. Also, another apology for the messy commits.
|
Fixes #3563
Summary
Problem
Pure percent sums add arithmetically.
A ± B% keeps the existing relative semantics when A is not a pure-percent expression (e.g., numbers/variables/units).
Do not break modulo (%) or other operators.
Changes
Added helper isPurePercentageExpression to identify pure percentage expressions, including nested parentheses, unary +/- wrappers, and add/sub trees.
Prefer unary percent for the RHS when the LHS is a pure-percent sum by setting a temporary parser flag (state.preferUnaryPercentAfterPlus). This prevents mis-parsing % as modulo in chains like 10% + 20% + x.
Retain “relative percent” semantics for non-percent LHS by transforming A ± B% into A ± (A * B%) only when the LHS is not pure percent.
Guard parseMultiplyDivideModulus against creating mod (%) when we are in a context that must treat the upcoming % as unary percent.
Convert a misparsed “mod” shape like A % (B%) directly followed by + or - into unary percentage on A (A/100), matching the intended semantics.
Tests (test/unit-tests/expression/parse.test.js):
Added/adjusted coverage for:
Pure percent arithmetic and chaining:
10% + 20%, 10% - 20%, 10% + 20% + 30%, 10% + 50% - 20%, etc.
Relative percent on numbers and variables:
50 + 20% + 10% == 66
x + 20% + 10%, x - 10% - 20%
Grouping and associativity:
10% + 20% + x
(10%) + (20%), (10% + 20%) + 30%, 10% + (20% + 30%)
Multiplication with grouped percents:
x * (10% + 20%)
Units with % on the right:
10 cm + 20% == 12 cm
Modulo/unary interactions remain valid:
100-3% (unary %)
3%-100 (mod)
11%-3 (mod with bitwise-not)
Behavioral notes
I had a lotta fun trying to solve this issue. I’m happy to adjust implementation details, test coverage, or semantics per maintainer feedback and coding style preferences.