Merge pull request #484 from nihilist-narwhal:fix-long-operator-chains

This commit is contained in:
Brandon Thomas 2026-10-12 23:35:26 -04:00
commit 96897b709c
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

@ -596,6 +596,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();