oficina / sheets Public

Pin the right-associative ^ branch of Render with a test

samePrecedenceNeedsParens special-cases precPower to bracket the left
child instead of the right, and that branch had zero coverage: none of
the three round-1 cases exercised ^. If the special case were ever
simplified away, (2^3)^2 would render as 2^3^2, which reparses as
2^(3^2) -- 64 silently becoming 512.

Adds two rows to TestRenderRoundTripsThroughReparse: (2^3)^2 (must keep
its brackets) and 2^3^2 (must not gain any), both checked via the
existing Parse -> Render -> Parse -> reflect.DeepEqual round trip.
Verified by temporarily deleting the precPower special case: the new
right_associative_power_needs_left_parens subtest fails exactly as
expected, confirming the test actually exercises the branch.
Paul Campbell 789ae1ee11a175b1198dafded60a4bf1f5f2f017 parent 9d02c1d
1 file changed, +10 −0
  • internal/sheets/calc/translate_test.go +10 −0
diff --git a/internal/sheets/calc/translate_test.go b/internal/sheets/calc/translate_test.go
index dade354..8dd959d 100644
--- a/internal/sheets/calc/translate_test.go
+++ b/internal/sheets/calc/translate_test.go
@@ -95,6 +95,16 @@ func TestRenderRoundTripsThroughReparse(t *testing.T) {
// same-precedence RIGHT operand: without them "8-3-1"
// reparses as (8-3)-1 — 6 would silently become 4.
{"left_associative_subtraction_needs_right_parens", "8-(3-1)"},
+ // ^ is the one right-associative level, so it needs parens on
+ // a same-precedence LEFT child instead of the right: without
+ // them "2^3^2" reparses as 2^(3^2) — 64 would silently become
+ // 512.
+ {"right_associative_power_needs_left_parens", "(2^3)^2"},
+ // The mirror case: a right child at the same precedence as its
+ // right-associative parent must NOT gain parens — this is the
+ // branch with the least test coverage, and the one where a
+ // wrong or deleted associativity check would go unnoticed.
+ {"right_associative_power_keeps_bare_right_child", "2^3^2"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {