fix(HF-268): support consecutive percent operators - #1773
Tobiadefami wants to merge 4 commits into
Conversation
Formulas such as =5%% failed to parse even though =(5%)% worked. Consume consecutive postfix percent operators and wrap the preceding expression for each token, so =5%% returns 0.0005 and =5%%% returns 0.000005, matching Excel. Preserve each operator's whitespace and the existing operator precedence. Document the behavior in the operator guide and Unreleased changelog. Validated with the matching fix/HF-268 test branch: 502 Jest suites and 6,254 tests passed, and Chrome and Firefox each passed 6,254 tests. Compilation and performance benchmarks passed. Full lint passed with zero errors and existing warnings using the test TypeScript project.
|
@Tobiadefami thanks for the pull request. No CLA step needed here — our records show you signed the Contributor License Agreement on 2026-07-31. That signature came from our previous signing form and has been carried over, so there is nothing for you to re-sign. |
|
Task linked: HF-268 Promile operator ( |
Add the pull request reference to the consecutive percent operators changelog entry, matching the surrounding entries. Validated the PR target and checked the Markdown diff for whitespace errors.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5361c44. Configure here.
Performance comparison of head (0fc1808) vs base (5abbbd9) |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
hyperformula-docs | 0fc1808 | Sep 30 2026, 09:28 AM |
marcin-kordas-hoc
left a comment
There was a problem hiding this comment.
Recommendation: Approve, blocked on a companion PR.
I reproduced the reported parsing defect on the current base: =1%%, =1%%%, =-1%%, =5%%
and =5%%% all fail to parse before this change ("Redundant input, expecting EOF but found: %"),
and all five parse and evaluate correctly after it (0.0001, 0.000001, -0.0001, 0.0005 and
0.000005 respectively). Measured via MS Graph against a live Excel Online session: Excel accepts
every one of these forms, plus a chained-percent-on-a-reference case and a percent/exponent
precedence case, and the fixed engine's values match Excel's, including the precedence case
(2^1%% → 1.0000693171 in the engine and 1.00006931712038 in Excel — agreement to 10 decimal
places).
I ran the full engine test suite together with the paired hyperformula-tests branch
(fix/HF-268) locally: 502 suites, 6254 tests passing, matching this PR's own reported numbers,
and confirmed the same result from this PR's CI log for the browser (Karma) run.
One blocker before merge: fix/HF-268 on hyperformula-tests has no open PR. Since our test repo
CI resolves the paired branch by name, it did gate this PR's own green status — but with no PR
open there, those test changes have no path into hyperformula-tests's develop. I confirmed
the consequence directly: running the four touched paired spec files against hyperformula-tests
develop (instead of fix/HF-268) while keeping this fix gives 2 failing suites / 3 failing
tests, because develop still encodes the pre-fix expectation that %% doesn't parse. Merging
this PR alone would redden our own develop the next time the paired suite runs against it.
Could you open a PR for fix/HF-268 so the two land together?
One clarification on intent, since the task title can mislead: HF-268's intended semantics are
Excel's chained percent — each % divides by 100, so 5%% = 0.0005 — not a promille (‰, ÷1000)
operator. Verified against live Excel Online and matched by this PR.
|
@marcin-kordas-hoc Thanks for catching this. I opened the companion test PR (hyperformula-tests #65). It targets develop from fix/HF-268, so the tests can be reviewed and merged alongside engine #1773. |
Gather the consecutive PercentOp tokens in the MANY loop and build the nested percent AST with a single reduce afterwards, keeping token consumption separate from AST construction. Behavior is unchanged: =5%% returns 0.0005 and each operator keeps its leading whitespace. Validated with the matching fix/HF-268 test branch: 502 Jest suites and 6,254 tests passed (3 skipped). Type-check and lint passed with zero errors.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1773 +/- ##
===========================================
- Coverage 97.32% 97.32% -0.01%
===========================================
Files 195 195
Lines 15739 15738 -1
Branches 3461 3491 +30
===========================================
- Hits 15318 15317 -1
Misses 413 413
Partials 8 8
🚀 New features to boost your workflow:
|

Formulas such as =5%% failed to parse even though =(5%)% worked. Consume consecutive postfix percent operators and wrap the preceding expression for each token, so =5%% returns 0.0005 and =5%%% returns 0.000005, matching Excel.
Preserve each operator's whitespace and the existing operator precedence. Document the behavior in the operator guide and Unreleased changelog.
Validated with the matching fix/HF-268 test branch: 502 Jest suites and 6,254 tests passed, and Chrome and Firefox each passed 6,254 tests. Compilation and performance benchmarks passed. Full lint passed with zero errors and existing warnings using the test TypeScript project.
Context
How did you test your changes?
Types of changes
Related issues:
Checklist:
Note
Low Risk
Localized parser change for unary
%chaining with docs/changelog only; no auth, data, or API surface changes.Overview
Fixes formula parsing so postfix
%can repeat on the same operand (e.g.=5%%), aligning behavior with Excel where each%divides the current value by 100.The parser rule
rightUnaryOpAtomicExpressionno longer accepts at most onePercentOp; it consumes any number of%tokens and nestsbuildPercentOpAstfor each, preserving per-operator whitespace and existing precedence.=5%%evaluates to0.0005(same as=(5%)%).The operator guide and Unreleased changelog document consecutive
%and clarify that unary%divides by 100.Reviewed by Cursor Bugbot for commit 0fc1808. Bugbot is set up for automated code reviews on this repo. Configure here.