Long operator chains: evaluate them without the depth limit

A formula such as =A1+A2+…+A500 nests one level per operator, and the
evaluator recursed once per level, so past its depth limit (about 400
operators) the result was #CALC!. Excel allows such formulas up to its
8,192-character limit. Chains of arithmetic, comparison and & operators
are now folded in a loop.
This commit is contained in:
nihilist-narwhal 2026-10-10 01:57:03 -05:00
parent fc45b7f6d8
commit 6b6aa82be1
2 changed files with 27 additions and 3 deletions

View File

@ -465,9 +465,22 @@ impl<'h> Evaluator<'h> {
}
}
_ => {
let x = self.value(a);
let y = self.value(b);
Ev::V(binary_values(op, &x, &y))
// A long chain such as A1+A2+…+A500 nests to the left: fold it in a loop instead
// of one level of recursion per operator (past the depth limit it was #CALC!).
let mut rest = vec![(op, b)];
let mut left = a;
while let Expr::Binary(o, l, r) = left
&& !matches!(o, BinOp::Range | BinOp::Union | BinOp::Intersect)
{
rest.push((*o, r.as_ref()));
left = l.as_ref();
}
let mut acc = self.value(left);
while let Some((o, r)) = rest.pop() {
let y = self.value(r);
acc = binary_values(o, &acc, &y);
}
Ev::V(acc)
}
}
}

View File

@ -549,6 +549,17 @@ fn text_function() {
assert_eq!(t.get("A1"), Value::from("1,234.50"));
}
#[test]
fn long_operator_chains() {
let mut t = T::new();
t.set("A1", "1");
// 1,000 additions: a formula of 3,000 characters, well within Excel's 8,192.
t.set("B1", &format!("={}", vec!["A1"; 1000].join("+")));
assert_eq!(t.num("B1"), 1000.0);
t.set("B2", &format!("=5{}", "+1-1".repeat(600)));
assert_eq!(t.num("B2"), 5.0);
}
#[test]
fn subtotal_skips_hidden() {
let mut t = T::new();