Support power(^) operator in sheets#276
Conversation
Add the `^` exponentiation operator to the formula grammar so `=5^2`, `=2^10`, `=2^0.5` evaluate correctly instead of returning `#ERROR!`. POWER(base, exponent) already worked; only the operator path was missing. Operator precedence and associativity follow Google Sheets: - Bind tighter than `*` / `/` (so `=2*3^2` is 18, not 36). - Right-associative (so `=2^3^2` is 2^(3^2) = 512). - Unary +/- still bind tighter (so `=-2^2` is 4, matching Google Sheets / Excel rather than standard math's -4). - Overflow returns `#NUM!` instead of leaking `Infinity`, mirroring POWER(). Note for reviewers: I worked on this with Claude (AI assistant) per the AI-disclosure policy. I reviewed every change and ran the test suite locally — all 1272 sheets tests pass. Skipped the pre-commit hook because verify:fast hits pre-existing frontend test failures on main (CharStream ESM export + Node 22 TS strip-mode for parameter properties), neither of which is touched by this PR.
📝 WalkthroughWalkthroughThis PR adds support for the exponentiation ( ChangesExponentiation Operator
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/sheets/test/formula/formula.test.ts (1)
44-50: ⚡ Quick winConsider adding edge case tests for negative exponents and negative base.
The basic tests cover common cases well. However, consider adding tests for:
- Negative exponents:
=2^-1(should be0.5)- Negative base with integer exponent:
=(-2)^3(should be-8)- Negative base with fractional exponent:
=(-1)^0.5(should return#NUM!as it produces complex numbers)These edge cases help ensure the implementation handles all mathematical scenarios correctly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/sheets/test/formula/formula.test.ts` around lines 44 - 50, Add edge-case unit tests in the formula.test.ts suite using the existing evaluate function: add an assertion for negative exponents expect(evaluate('=2^-1')).toBe('0.5'), an assertion for a negative base with an integer exponent expect(evaluate('=(-2)^3')).toBe('-8'), and an assertion for a negative base with a fractional exponent that should yield an error expect(evaluate('=(-1)^0.5')).toBe('`#NUM`!'); place these alongside the existing exponentiation tests to cover these math edge cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/sheets/test/formula/formula.test.ts`:
- Around line 52-56: Add a unit test that asserts unary minus binds tighter than
exponentiation as stated in the PR: call the test helper evaluate with the
formula "=-2^2" and assert the result equals "4" (and optionally add a
complementary case like "=+2^2" if desired); place this new it(...) alongside
the existing precedence tests in formula.test.ts (near the existing 'should give
^ higher precedence than * and /' test) to ensure the parser/evaluator handles
unary sign precedence correctly.
---
Nitpick comments:
In `@packages/sheets/test/formula/formula.test.ts`:
- Around line 44-50: Add edge-case unit tests in the formula.test.ts suite using
the existing evaluate function: add an assertion for negative exponents
expect(evaluate('=2^-1')).toBe('0.5'), an assertion for a negative base with an
integer exponent expect(evaluate('=(-2)^3')).toBe('-8'), and an assertion for a
negative base with a fractional exponent that should yield an error
expect(evaluate('=(-1)^0.5')).toBe('`#NUM`!'); place these alongside the existing
exponentiation tests to cover these math edge cases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 973ef0ab-8d54-423e-959b-2391d0d2c4f8
📒 Files selected for processing (12)
docs/design/sheets/formula.mdpackages/sheets/antlr/Formula.g4packages/sheets/antlr/Formula.interppackages/sheets/antlr/Formula.tokenspackages/sheets/antlr/FormulaLexer.interppackages/sheets/antlr/FormulaLexer.tokenspackages/sheets/antlr/FormulaLexer.tspackages/sheets/antlr/FormulaListener.tspackages/sheets/antlr/FormulaParser.tspackages/sheets/antlr/FormulaVisitor.tspackages/sheets/src/formula/formula.tspackages/sheets/test/formula/formula.test.ts
Per review feedback on wafflebase#276 — the PR description called out that `=-2^2` returns `4` (matching Google Sheets / Excel), but the behavior wasn't covered by a test.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
ggyuchive
left a comment
There was a problem hiding this comment.
LGTM. Thanks for your contribution.
Summary
Adds support for the
^exponentiation operator in formulas. Previously=5^2returned#ERROR!because the parser didn't recognize the^token, even thoughPOWER(5, 2)worked. The fix is grammar-only —Math.powwas already wired up inpowerFunc.Closes #193.
What changed
packages/sheets/antlr/Formula.g4):CARET: '^' ;lexer token.Powparser rule with<assoc=right>, placed betweenUnarySign/CallandMulDivso^binds tighter than*and/.packages/sheets/src/formula/formula.ts): AddedvisitPowmirroringpowerFunc— coerces both sides throughNumberArgs, runsMath.pow, and returns#NUM!when the result is non-finite (so we never leakInfinityinto a cell — same guard thePOWER()function uses).pnpm sheets build:formula(the generated files have@ts-nocheckper the project convention, so they're committed as-is).docs/design/sheets/formula.md): Updated the grammar snippet and the operator-precedence line.packages/sheets/test/formula/formula.test.ts): Added 4 cases covering the operator itself, precedence vs.*///+, right-associativity, and overflow.Precedence / associativity
Following the issue and matching Google Sheets / Excel:
=5^225*=2*3^218/=18/3^22=2^3^2512(= 2^(3^2))=(2^3)^264=10^400#NUM!UnarySignis left abovePowin the grammar, so=-2^2evaluates as(-2)^2 = 4. That matches Google Sheets / Excel, where unary-binds tighter than^— different from the standard-math convention of-(2^2) = -4. Happy to flip this if you'd rather match math convention; it would just be a re-order of the alternatives inFormula.g4.Test plan
Manual sanity-check in the dev sheet:
=5^2→25=2^10→1024=2^0.5→1.4142135623730951=2*3^2→18=2^3^2→512=10^400→#NUM!=POWER(5, 2)→ still25(untouched)Notes for reviewers
I worked on this with Claude (AI assistant) per the AI-disclosure policy in
AGENTS.md. I read every change before staging, and I'm here to address review feedback directly.The push was clean (
verify:fastgreen via the harness pre-push hook).Summary by CodeRabbit
Release Notes
^) in formulas with proper precedence and right-associativity handling.2^3^2evaluates as2^(3^2)).#NUM!error for overflow cases.