fix: add depth limit to formula evaluation, propagate errors

Circular or self-referencing formulas now return CellValue::Error
instead of stack overflowing. eval_expr uses Result<f64, String>
internally so errors (circular refs, div/0, missing refs) propagate
immediately through the expression tree via ?. The depth limit (16)
is checked per evaluate_depth call — normal 1-2 level chains are
unaffected.

Also adds CellValue::Error variant for displaying ERR:reason in the
grid, and handles it in format, persistence, and search.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-04-09 01:35:05 -07:00
parent 32d215f3d6
commit 737d14a5c0
5 changed files with 1324 additions and 139 deletions

View File

@ -62,15 +62,21 @@ impl std::fmt::Display for CellKey {
pub enum CellValue {
Number(f64),
Text(String),
/// Evaluation error (circular reference, depth overflow, etc.)
Error(String),
}
impl CellValue {
pub fn as_f64(&self) -> Option<f64> {
match self {
CellValue::Number(n) => Some(*n),
CellValue::Text(_) => None,
_ => None,
}
}
pub fn is_error(&self) -> bool {
matches!(self, CellValue::Error(_))
}
}
impl std::fmt::Display for CellValue {
@ -84,6 +90,7 @@ impl std::fmt::Display for CellValue {
}
}
CellValue::Text(s) => write!(f, "{s}"),
CellValue::Error(msg) => write!(f, "ERR:{msg}"),
}
}
}