1use std::collections::{HashMap, HashSet};
8
9use crate::cell::CellValue;
10use crate::error::{Error, Result};
11use crate::formula::ast::{BinaryOperator, CellReference, Expr, UnaryOperator};
12use crate::formula::functions;
13use crate::utils::cell_ref::{column_name_to_number, column_number_to_name};
14
15const MAX_EVAL_DEPTH: usize = 256;
17
18pub trait CellDataProvider {
20 fn get_cell(&self, sheet: &str, col: u32, row: u32) -> CellValue;
22 fn current_sheet(&self) -> &str;
24}
25
26pub struct CellSnapshot {
28 cells: std::collections::HashMap<(String, u32, u32), CellValue>,
29 current_sheet: String,
30}
31
32impl CellSnapshot {
33 pub fn new(current_sheet: String) -> Self {
35 Self {
36 cells: std::collections::HashMap::new(),
37 current_sheet,
38 }
39 }
40
41 pub fn set_cell(&mut self, sheet: &str, col: u32, row: u32, value: CellValue) {
43 self.cells.insert((sheet.to_string(), col, row), value);
44 }
45
46 pub fn set_current_sheet(&mut self, sheet: &str) {
48 self.current_sheet = sheet.to_string();
49 }
50}
51
52impl CellDataProvider for CellSnapshot {
53 fn get_cell(&self, sheet: &str, col: u32, row: u32) -> CellValue {
54 self.cells
55 .get(&(sheet.to_string(), col, row))
56 .cloned()
57 .unwrap_or(CellValue::Empty)
58 }
59
60 fn current_sheet(&self) -> &str {
61 &self.current_sheet
62 }
63}
64
65pub fn evaluate(expr: &Expr, provider: &dyn CellDataProvider) -> Result<CellValue> {
67 let mut evaluator = Evaluator::new(provider);
68 evaluator.eval_expr(expr)
69}
70
71pub struct Evaluator<'a> {
73 provider: &'a dyn CellDataProvider,
74 eval_stack: HashSet<(String, u32, u32)>,
75 depth: usize,
76}
77
78impl<'a> Evaluator<'a> {
79 pub fn new(provider: &'a dyn CellDataProvider) -> Self {
81 Self {
82 provider,
83 eval_stack: HashSet::new(),
84 depth: 0,
85 }
86 }
87
88 pub fn eval_expr(&mut self, expr: &Expr) -> Result<CellValue> {
90 self.depth += 1;
91 if self.depth > MAX_EVAL_DEPTH {
92 self.depth -= 1;
93 return Err(Error::FormulaError(
94 "maximum evaluation depth exceeded".to_string(),
95 ));
96 }
97 let result = self.eval_inner(expr);
98 self.depth -= 1;
99 result
100 }
101
102 pub fn eval_arg(&mut self, args: &[Expr], index: usize) -> Result<CellValue> {
105 if index >= args.len() {
106 return Err(Error::FormulaError(format!(
107 "missing argument at index {index}"
108 )));
109 }
110 self.eval_expr(&args[index])
111 }
112
113 pub fn collect_numbers(&mut self, args: &[Expr]) -> Result<Vec<f64>> {
116 let mut nums = Vec::new();
117 for arg in args {
118 match arg {
119 Expr::Range { start, end } => {
120 let values = self.expand_range(start, end)?;
121 for v in values {
122 if let Ok(n) = coerce_to_number(&v) {
123 nums.push(n);
124 }
125 }
126 }
127 _ => {
128 let v = self.eval_expr(arg)?;
129 nums.push(coerce_to_number(&v)?);
130 }
131 }
132 }
133 Ok(nums)
134 }
135
136 pub fn flatten_args_to_values(&mut self, args: &[Expr]) -> Result<Vec<CellValue>> {
138 let mut values = Vec::new();
139 for arg in args {
140 match arg {
141 Expr::Range { start, end } => {
142 values.extend(self.expand_range(start, end)?);
143 }
144 _ => {
145 values.push(self.eval_expr(arg)?);
146 }
147 }
148 }
149 Ok(values)
150 }
151
152 pub fn expand_range(
154 &mut self,
155 start: &CellReference,
156 end: &CellReference,
157 ) -> Result<Vec<CellValue>> {
158 let sheet = start
159 .sheet
160 .as_deref()
161 .unwrap_or_else(|| self.provider.current_sheet());
162 let start_col = column_name_to_number(&start.col)?;
163 let end_col = column_name_to_number(&end.col)?;
164 let start_row = start.row;
165 let end_row = end.row;
166 let min_col = start_col.min(end_col);
167 let max_col = start_col.max(end_col);
168 let min_row = start_row.min(end_row);
169 let max_row = start_row.max(end_row);
170 let mut values = Vec::new();
171 for r in min_row..=max_row {
172 for c in min_col..=max_col {
173 values.push(self.resolve_cell(sheet, c, r)?);
174 }
175 }
176 Ok(values)
177 }
178
179 pub fn current_sheet(&self) -> &str {
181 self.provider.current_sheet()
182 }
183
184 fn eval_inner(&mut self, expr: &Expr) -> Result<CellValue> {
187 match expr {
188 Expr::Number(n) => Ok(CellValue::Number(*n)),
189 Expr::String(s) => Ok(CellValue::String(s.clone())),
190 Expr::Bool(b) => Ok(CellValue::Bool(*b)),
191 Expr::Error(e) => Ok(CellValue::Error(e.clone())),
192 Expr::CellRef(cell_ref) => self.eval_cell_ref(cell_ref),
193 Expr::Range { start, end } => {
194 let vals = self.expand_range(start, end)?;
196 Ok(vals.into_iter().next().unwrap_or(CellValue::Empty))
197 }
198 Expr::Paren(inner) => self.eval_expr(inner),
199 Expr::BinaryOp { op, left, right } => self.eval_binary(*op, left, right),
200 Expr::UnaryOp { op, operand } => self.eval_unary(*op, operand),
201 Expr::Function { name, args } => self.eval_function(name, args),
202 }
203 }
204
205 fn eval_cell_ref(&mut self, cell_ref: &CellReference) -> Result<CellValue> {
206 let sheet = cell_ref
207 .sheet
208 .as_deref()
209 .unwrap_or_else(|| self.provider.current_sheet())
210 .to_string();
211 let col = column_name_to_number(&cell_ref.col)?;
212 let row = cell_ref.row;
213 self.resolve_cell(&sheet, col, row)
214 }
215
216 fn resolve_cell(&mut self, sheet: &str, col: u32, row: u32) -> Result<CellValue> {
218 let key = (sheet.to_string(), col, row);
219 if self.eval_stack.contains(&key) {
220 let col_name = column_number_to_name(col)?;
221 return Err(Error::CircularReference {
222 cell: format!("{col_name}{row}"),
223 });
224 }
225 let cell_value = self.provider.get_cell(sheet, col, row);
226 match cell_value {
227 CellValue::Formula { ref expr, .. } => {
228 self.eval_stack.insert(key.clone());
229 let parsed = crate::formula::parser::parse_formula(expr)?;
230 let result = self.eval_expr(&parsed);
231 self.eval_stack.remove(&key);
232 result
233 }
234 other => Ok(other),
235 }
236 }
237
238 fn eval_binary(&mut self, op: BinaryOperator, left: &Expr, right: &Expr) -> Result<CellValue> {
239 let lhs = self.eval_expr(left)?;
240 let rhs = self.eval_expr(right)?;
241
242 if let CellValue::Error(ref e) = lhs {
244 return Ok(CellValue::Error(e.clone()));
245 }
246 if let CellValue::Error(ref e) = rhs {
247 return Ok(CellValue::Error(e.clone()));
248 }
249
250 match op {
251 BinaryOperator::Concat => {
252 let ls = coerce_to_string(&lhs);
253 let rs = coerce_to_string(&rhs);
254 Ok(CellValue::String(format!("{ls}{rs}")))
255 }
256 BinaryOperator::Add
257 | BinaryOperator::Sub
258 | BinaryOperator::Mul
259 | BinaryOperator::Div
260 | BinaryOperator::Pow => {
261 let ln = coerce_to_number(&lhs)?;
262 let rn = coerce_to_number(&rhs)?;
263 let result = match op {
264 BinaryOperator::Add => ln + rn,
265 BinaryOperator::Sub => ln - rn,
266 BinaryOperator::Mul => ln * rn,
267 BinaryOperator::Div => {
268 if rn == 0.0 {
269 return Ok(CellValue::Error("#DIV/0!".to_string()));
270 }
271 ln / rn
272 }
273 BinaryOperator::Pow => ln.powf(rn),
274 _ => unreachable!(),
275 };
276 Ok(CellValue::Number(result))
277 }
278 BinaryOperator::Eq
279 | BinaryOperator::Ne
280 | BinaryOperator::Lt
281 | BinaryOperator::Le
282 | BinaryOperator::Gt
283 | BinaryOperator::Ge => {
284 let ord = compare_values(&lhs, &rhs);
285 let result = match op {
286 BinaryOperator::Eq => ord == std::cmp::Ordering::Equal,
287 BinaryOperator::Ne => ord != std::cmp::Ordering::Equal,
288 BinaryOperator::Lt => ord == std::cmp::Ordering::Less,
289 BinaryOperator::Le => {
290 ord == std::cmp::Ordering::Less || ord == std::cmp::Ordering::Equal
291 }
292 BinaryOperator::Gt => ord == std::cmp::Ordering::Greater,
293 BinaryOperator::Ge => {
294 ord == std::cmp::Ordering::Greater || ord == std::cmp::Ordering::Equal
295 }
296 _ => unreachable!(),
297 };
298 Ok(CellValue::Bool(result))
299 }
300 }
301 }
302
303 fn eval_unary(&mut self, op: UnaryOperator, operand: &Expr) -> Result<CellValue> {
304 let val = self.eval_expr(operand)?;
305 if let CellValue::Error(ref e) = val {
306 return Ok(CellValue::Error(e.clone()));
307 }
308 let n = coerce_to_number(&val)?;
309 match op {
310 UnaryOperator::Neg => Ok(CellValue::Number(-n)),
311 UnaryOperator::Pos => Ok(CellValue::Number(n)),
312 UnaryOperator::Percent => Ok(CellValue::Number(n / 100.0)),
313 }
314 }
315
316 fn eval_function(&mut self, name: &str, args: &[Expr]) -> Result<CellValue> {
317 let func = functions::lookup_function(name).ok_or_else(|| Error::UnknownFunction {
318 name: name.to_string(),
319 })?;
320 func(args, self)
321 }
322}
323
324pub fn coerce_to_number(value: &CellValue) -> Result<f64> {
329 match value {
330 CellValue::Number(n) => Ok(*n),
331 CellValue::Date(n) => Ok(*n),
332 CellValue::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }),
333 CellValue::Empty => Ok(0.0),
334 CellValue::String(s) => s
335 .parse::<f64>()
336 .map_err(|_| Error::FormulaError(format!("cannot convert \"{s}\" to number"))),
337 CellValue::Error(e) => Err(Error::FormulaError(e.clone())),
338 CellValue::Formula { result, .. } => {
339 if let Some(ref inner) = result {
340 coerce_to_number(inner)
341 } else {
342 Ok(0.0)
343 }
344 }
345 CellValue::RichString(runs) => {
346 let plain = crate::rich_text::rich_text_to_plain(runs);
347 plain
348 .parse::<f64>()
349 .map_err(|_| Error::FormulaError(format!("cannot convert \"{plain}\" to number")))
350 }
351 }
352}
353
354pub fn coerce_to_string(value: &CellValue) -> String {
356 match value {
357 CellValue::String(s) => s.clone(),
358 CellValue::Number(n) => {
359 if n.fract() == 0.0 && n.is_finite() {
360 format!("{}", *n as i64)
361 } else {
362 format!("{n}")
363 }
364 }
365 CellValue::Bool(b) => {
366 if *b {
367 "TRUE".to_string()
368 } else {
369 "FALSE".to_string()
370 }
371 }
372 CellValue::Empty => String::new(),
373 CellValue::Error(e) => e.clone(),
374 CellValue::Date(n) => format!("{n}"),
375 CellValue::Formula { result, .. } => {
376 if let Some(ref inner) = result {
377 coerce_to_string(inner)
378 } else {
379 String::new()
380 }
381 }
382 CellValue::RichString(runs) => crate::rich_text::rich_text_to_plain(runs),
383 }
384}
385
386pub fn coerce_to_bool(value: &CellValue) -> Result<bool> {
389 match value {
390 CellValue::Bool(b) => Ok(*b),
391 CellValue::Number(n) => Ok(*n != 0.0),
392 CellValue::Date(n) => Ok(*n != 0.0),
393 CellValue::Empty => Ok(false),
394 CellValue::String(s) => match s.to_ascii_uppercase().as_str() {
395 "TRUE" => Ok(true),
396 "FALSE" => Ok(false),
397 _ => Err(Error::FormulaError(format!(
398 "cannot convert \"{s}\" to boolean"
399 ))),
400 },
401 CellValue::Error(e) => Err(Error::FormulaError(e.clone())),
402 CellValue::Formula { result, .. } => {
403 if let Some(ref inner) = result {
404 coerce_to_bool(inner)
405 } else {
406 Ok(false)
407 }
408 }
409 CellValue::RichString(runs) => {
410 let plain = crate::rich_text::rich_text_to_plain(runs);
411 match plain.to_ascii_uppercase().as_str() {
412 "TRUE" => Ok(true),
413 "FALSE" => Ok(false),
414 _ => Err(Error::FormulaError(format!(
415 "cannot convert \"{plain}\" to boolean"
416 ))),
417 }
418 }
419 }
420}
421
422pub fn compare_values(lhs: &CellValue, rhs: &CellValue) -> std::cmp::Ordering {
426 use std::cmp::Ordering;
427
428 fn type_rank(v: &CellValue) -> u8 {
429 match v {
430 CellValue::Empty => 0,
431 CellValue::Number(_) | CellValue::Date(_) => 1,
432 CellValue::String(_) | CellValue::RichString(_) => 2,
433 CellValue::Bool(_) => 3,
434 CellValue::Error(_) => 4,
435 CellValue::Formula { .. } => 5,
436 }
437 }
438
439 let lr = type_rank(lhs);
440 let rr = type_rank(rhs);
441 if lr != rr {
442 return lr.cmp(&rr);
443 }
444
445 match (lhs, rhs) {
446 (CellValue::Empty, CellValue::Empty) => Ordering::Equal,
447 (CellValue::Number(a), CellValue::Number(b))
448 | (CellValue::Date(a), CellValue::Date(b))
449 | (CellValue::Number(a), CellValue::Date(b))
450 | (CellValue::Date(a), CellValue::Number(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
451 (CellValue::String(a), CellValue::String(b)) => {
452 a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase())
453 }
454 (CellValue::Bool(a), CellValue::Bool(b)) => a.cmp(b),
455 _ => Ordering::Equal,
456 }
457}
458
459#[derive(Debug, Clone, PartialEq, Eq, Hash)]
461pub struct CellCoord {
462 pub sheet: String,
463 pub col: u32,
464 pub row: u32,
465}
466
467pub fn build_dependency_graph(
470 formula_cells: &[(CellCoord, String)],
471) -> Result<HashMap<CellCoord, Vec<CellCoord>>> {
472 let mut deps: HashMap<CellCoord, Vec<CellCoord>> = HashMap::new();
473 for (coord, formula_str) in formula_cells {
474 let expr = crate::formula::parser::parse_formula(formula_str)?;
475 let refs = extract_cell_refs(&expr, &coord.sheet);
476 deps.insert(coord.clone(), refs);
477 }
478 Ok(deps)
479}
480
481fn extract_cell_refs(expr: &Expr, current_sheet: &str) -> Vec<CellCoord> {
483 let mut refs = Vec::new();
484 collect_refs(expr, current_sheet, &mut refs);
485 refs
486}
487
488fn collect_refs(expr: &Expr, current_sheet: &str, refs: &mut Vec<CellCoord>) {
489 match expr {
490 Expr::CellRef(cell_ref) => {
491 let sheet = cell_ref
492 .sheet
493 .as_deref()
494 .unwrap_or(current_sheet)
495 .to_string();
496 if let Ok(col) = column_name_to_number(&cell_ref.col) {
497 refs.push(CellCoord {
498 sheet,
499 col,
500 row: cell_ref.row,
501 });
502 }
503 }
504 Expr::Range { start, end } => {
505 let sheet = start.sheet.as_deref().unwrap_or(current_sheet).to_string();
506 if let (Ok(start_col), Ok(end_col)) = (
507 column_name_to_number(&start.col),
508 column_name_to_number(&end.col),
509 ) {
510 let min_col = start_col.min(end_col);
511 let max_col = start_col.max(end_col);
512 let min_row = start.row.min(end.row);
513 let max_row = start.row.max(end.row);
514 for r in min_row..=max_row {
515 for c in min_col..=max_col {
516 refs.push(CellCoord {
517 sheet: sheet.clone(),
518 col: c,
519 row: r,
520 });
521 }
522 }
523 }
524 }
525 Expr::Function { args, .. } => {
526 for arg in args {
527 collect_refs(arg, current_sheet, refs);
528 }
529 }
530 Expr::BinaryOp { left, right, .. } => {
531 collect_refs(left, current_sheet, refs);
532 collect_refs(right, current_sheet, refs);
533 }
534 Expr::UnaryOp { operand, .. } => {
535 collect_refs(operand, current_sheet, refs);
536 }
537 Expr::Paren(inner) => {
538 collect_refs(inner, current_sheet, refs);
539 }
540 _ => {}
541 }
542}
543
544pub fn topological_sort(
548 formula_cells: &[CellCoord],
549 deps: &HashMap<CellCoord, Vec<CellCoord>>,
550) -> Result<Vec<CellCoord>> {
551 let formula_set: HashSet<CellCoord> = formula_cells.iter().cloned().collect();
552
553 let mut in_degree: HashMap<CellCoord, usize> = HashMap::new();
554 let mut dependents: HashMap<CellCoord, Vec<CellCoord>> = HashMap::new();
555
556 for cell in formula_cells {
557 in_degree.entry(cell.clone()).or_insert(0);
558 }
559
560 for (cell, cell_deps) in deps {
561 let dep_count = cell_deps.iter().filter(|d| formula_set.contains(d)).count();
562 in_degree.insert(cell.clone(), dep_count);
563 for dep in cell_deps {
564 if formula_set.contains(dep) {
565 dependents
566 .entry(dep.clone())
567 .or_default()
568 .push(cell.clone());
569 }
570 }
571 }
572
573 let mut queue: Vec<CellCoord> = in_degree
575 .iter()
576 .filter(|(_, °)| deg == 0)
577 .map(|(cell, _)| cell.clone())
578 .collect();
579 queue.sort_by(|a, b| (&a.sheet, a.row, a.col).cmp(&(&b.sheet, b.row, b.col)));
580
581 let mut sorted = Vec::new();
582 let mut visited = 0;
583
584 while let Some(cell) = queue.pop() {
585 sorted.push(cell.clone());
586 visited += 1;
587 if let Some(cell_dependents) = dependents.get(&cell) {
588 for dep in cell_dependents {
589 if let Some(deg) = in_degree.get_mut(dep) {
590 *deg -= 1;
591 if *deg == 0 {
592 queue.push(dep.clone());
593 }
594 }
595 }
596 }
597 queue.sort_by(|a, b| (&b.sheet, b.row, b.col).cmp(&(&a.sheet, a.row, a.col)));
598 }
599
600 if visited != formula_cells.len() {
601 return Err(Error::CircularReference {
602 cell: "multiple cells".to_string(),
603 });
604 }
605
606 Ok(sorted)
607}
608
609#[cfg(test)]
610mod tests {
611 use super::*;
612 use crate::formula::ast::{BinaryOperator, CellReference, Expr, UnaryOperator};
613 use crate::formula::parser::parse_formula;
614
615 fn make_snapshot() -> CellSnapshot {
616 CellSnapshot::new("Sheet1".to_string())
617 }
618
619 #[test]
622 fn eval_number_literal() {
623 let snap = make_snapshot();
624 let result = evaluate(&Expr::Number(42.0), &snap).unwrap();
625 assert_eq!(result, CellValue::Number(42.0));
626 }
627
628 #[test]
629 fn eval_string_literal() {
630 let snap = make_snapshot();
631 let result = evaluate(&Expr::String("hello".to_string()), &snap).unwrap();
632 assert_eq!(result, CellValue::String("hello".to_string()));
633 }
634
635 #[test]
636 fn eval_bool_literal() {
637 let snap = make_snapshot();
638 assert_eq!(
639 evaluate(&Expr::Bool(true), &snap).unwrap(),
640 CellValue::Bool(true)
641 );
642 assert_eq!(
643 evaluate(&Expr::Bool(false), &snap).unwrap(),
644 CellValue::Bool(false)
645 );
646 }
647
648 #[test]
649 fn eval_error_literal() {
650 let snap = make_snapshot();
651 let result = evaluate(&Expr::Error("#N/A".to_string()), &snap).unwrap();
652 assert_eq!(result, CellValue::Error("#N/A".to_string()));
653 }
654
655 #[test]
658 fn eval_add() {
659 let snap = make_snapshot();
660 let expr = Expr::BinaryOp {
661 op: BinaryOperator::Add,
662 left: Box::new(Expr::Number(10.0)),
663 right: Box::new(Expr::Number(5.0)),
664 };
665 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(15.0));
666 }
667
668 #[test]
669 fn eval_sub() {
670 let snap = make_snapshot();
671 let expr = Expr::BinaryOp {
672 op: BinaryOperator::Sub,
673 left: Box::new(Expr::Number(10.0)),
674 right: Box::new(Expr::Number(3.0)),
675 };
676 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(7.0));
677 }
678
679 #[test]
680 fn eval_mul() {
681 let snap = make_snapshot();
682 let expr = Expr::BinaryOp {
683 op: BinaryOperator::Mul,
684 left: Box::new(Expr::Number(4.0)),
685 right: Box::new(Expr::Number(3.0)),
686 };
687 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(12.0));
688 }
689
690 #[test]
691 fn eval_div() {
692 let snap = make_snapshot();
693 let expr = Expr::BinaryOp {
694 op: BinaryOperator::Div,
695 left: Box::new(Expr::Number(10.0)),
696 right: Box::new(Expr::Number(4.0)),
697 };
698 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(2.5));
699 }
700
701 #[test]
702 fn eval_div_by_zero() {
703 let snap = make_snapshot();
704 let expr = Expr::BinaryOp {
705 op: BinaryOperator::Div,
706 left: Box::new(Expr::Number(1.0)),
707 right: Box::new(Expr::Number(0.0)),
708 };
709 assert_eq!(
710 evaluate(&expr, &snap).unwrap(),
711 CellValue::Error("#DIV/0!".to_string())
712 );
713 }
714
715 #[test]
716 fn eval_pow() {
717 let snap = make_snapshot();
718 let expr = Expr::BinaryOp {
719 op: BinaryOperator::Pow,
720 left: Box::new(Expr::Number(2.0)),
721 right: Box::new(Expr::Number(10.0)),
722 };
723 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(1024.0));
724 }
725
726 #[test]
727 fn eval_concat() {
728 let snap = make_snapshot();
729 let expr = Expr::BinaryOp {
730 op: BinaryOperator::Concat,
731 left: Box::new(Expr::String("hello".to_string())),
732 right: Box::new(Expr::String(" world".to_string())),
733 };
734 assert_eq!(
735 evaluate(&expr, &snap).unwrap(),
736 CellValue::String("hello world".to_string())
737 );
738 }
739
740 #[test]
741 fn eval_eq() {
742 let snap = make_snapshot();
743 let expr = Expr::BinaryOp {
744 op: BinaryOperator::Eq,
745 left: Box::new(Expr::Number(5.0)),
746 right: Box::new(Expr::Number(5.0)),
747 };
748 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Bool(true));
749 }
750
751 #[test]
752 fn eval_ne() {
753 let snap = make_snapshot();
754 let expr = Expr::BinaryOp {
755 op: BinaryOperator::Ne,
756 left: Box::new(Expr::Number(5.0)),
757 right: Box::new(Expr::Number(3.0)),
758 };
759 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Bool(true));
760 }
761
762 #[test]
763 fn eval_lt() {
764 let snap = make_snapshot();
765 let expr = Expr::BinaryOp {
766 op: BinaryOperator::Lt,
767 left: Box::new(Expr::Number(3.0)),
768 right: Box::new(Expr::Number(5.0)),
769 };
770 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Bool(true));
771 }
772
773 #[test]
774 fn eval_le() {
775 let snap = make_snapshot();
776 let expr = Expr::BinaryOp {
777 op: BinaryOperator::Le,
778 left: Box::new(Expr::Number(5.0)),
779 right: Box::new(Expr::Number(5.0)),
780 };
781 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Bool(true));
782 }
783
784 #[test]
785 fn eval_gt() {
786 let snap = make_snapshot();
787 let expr = Expr::BinaryOp {
788 op: BinaryOperator::Gt,
789 left: Box::new(Expr::Number(5.0)),
790 right: Box::new(Expr::Number(3.0)),
791 };
792 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Bool(true));
793 }
794
795 #[test]
796 fn eval_ge() {
797 let snap = make_snapshot();
798 let expr = Expr::BinaryOp {
799 op: BinaryOperator::Ge,
800 left: Box::new(Expr::Number(5.0)),
801 right: Box::new(Expr::Number(5.0)),
802 };
803 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Bool(true));
804 }
805
806 #[test]
809 fn eval_unary_neg() {
810 let snap = make_snapshot();
811 let expr = Expr::UnaryOp {
812 op: UnaryOperator::Neg,
813 operand: Box::new(Expr::Number(7.0)),
814 };
815 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(-7.0));
816 }
817
818 #[test]
819 fn eval_unary_pos() {
820 let snap = make_snapshot();
821 let expr = Expr::UnaryOp {
822 op: UnaryOperator::Pos,
823 operand: Box::new(Expr::Number(7.0)),
824 };
825 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(7.0));
826 }
827
828 #[test]
829 fn eval_unary_percent() {
830 let snap = make_snapshot();
831 let expr = Expr::UnaryOp {
832 op: UnaryOperator::Percent,
833 operand: Box::new(Expr::Number(50.0)),
834 };
835 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(0.5));
836 }
837
838 #[test]
841 fn eval_cell_ref() {
842 let mut snap = make_snapshot();
843 snap.set_cell("Sheet1", 1, 1, CellValue::Number(42.0));
844 let expr = Expr::CellRef(CellReference {
845 col: "A".to_string(),
846 row: 1,
847 abs_col: false,
848 abs_row: false,
849 sheet: None,
850 });
851 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(42.0));
852 }
853
854 #[test]
855 fn eval_cell_ref_empty() {
856 let snap = make_snapshot();
857 let expr = Expr::CellRef(CellReference {
858 col: "Z".to_string(),
859 row: 99,
860 abs_col: false,
861 abs_row: false,
862 sheet: None,
863 });
864 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Empty);
865 }
866
867 #[test]
868 fn eval_cell_ref_cross_sheet() {
869 let mut snap = make_snapshot();
870 snap.set_cell("Sheet2", 2, 3, CellValue::String("remote".to_string()));
871 let expr = Expr::CellRef(CellReference {
872 col: "B".to_string(),
873 row: 3,
874 abs_col: false,
875 abs_row: false,
876 sheet: Some("Sheet2".to_string()),
877 });
878 assert_eq!(
879 evaluate(&expr, &snap).unwrap(),
880 CellValue::String("remote".to_string())
881 );
882 }
883
884 #[test]
887 fn eval_range_expansion() {
888 let mut snap = make_snapshot();
889 snap.set_cell("Sheet1", 1, 1, CellValue::Number(1.0));
890 snap.set_cell("Sheet1", 1, 2, CellValue::Number(2.0));
891 snap.set_cell("Sheet1", 1, 3, CellValue::Number(3.0));
892 let expr = parse_formula("SUM(A1:A3)").unwrap();
893 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(6.0));
894 }
895
896 #[test]
897 fn eval_range_2d() {
898 let mut snap = make_snapshot();
899 snap.set_cell("Sheet1", 1, 1, CellValue::Number(1.0));
900 snap.set_cell("Sheet1", 2, 1, CellValue::Number(2.0));
901 snap.set_cell("Sheet1", 1, 2, CellValue::Number(3.0));
902 snap.set_cell("Sheet1", 2, 2, CellValue::Number(4.0));
903 let expr = parse_formula("SUM(A1:B2)").unwrap();
904 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(10.0));
905 }
906
907 #[test]
910 fn coerce_bool_to_number() {
911 assert_eq!(coerce_to_number(&CellValue::Bool(true)).unwrap(), 1.0);
912 assert_eq!(coerce_to_number(&CellValue::Bool(false)).unwrap(), 0.0);
913 }
914
915 #[test]
916 fn coerce_empty_to_number() {
917 assert_eq!(coerce_to_number(&CellValue::Empty).unwrap(), 0.0);
918 }
919
920 #[test]
921 fn coerce_string_to_number_success() {
922 assert_eq!(
923 coerce_to_number(&CellValue::String("3.15".to_string())).unwrap(),
924 3.15
925 );
926 }
927
928 #[test]
929 fn coerce_string_to_number_fail() {
930 assert!(coerce_to_number(&CellValue::String("abc".to_string())).is_err());
931 }
932
933 #[test]
934 fn coerce_number_to_string() {
935 assert_eq!(coerce_to_string(&CellValue::Number(42.0)), "42");
936 assert_eq!(coerce_to_string(&CellValue::Number(3.15)), "3.15");
937 }
938
939 #[test]
940 fn coerce_bool_to_string() {
941 assert_eq!(coerce_to_string(&CellValue::Bool(true)), "TRUE");
942 assert_eq!(coerce_to_string(&CellValue::Bool(false)), "FALSE");
943 }
944
945 #[test]
946 fn coerce_number_to_bool() {
947 assert!(coerce_to_bool(&CellValue::Number(1.0)).unwrap());
948 assert!(!coerce_to_bool(&CellValue::Number(0.0)).unwrap());
949 }
950
951 #[test]
954 fn eval_circular_reference() {
955 let mut snap = make_snapshot();
956 snap.set_cell(
958 "Sheet1",
959 1,
960 1,
961 CellValue::Formula {
962 expr: "A1".to_string(),
963 result: None,
964 },
965 );
966 let expr = Expr::CellRef(CellReference {
967 col: "A".to_string(),
968 row: 1,
969 abs_col: false,
970 abs_row: false,
971 sheet: None,
972 });
973 let result = evaluate(&expr, &snap);
974 assert!(result.is_err());
975 let err_str = result.unwrap_err().to_string();
976 assert!(
977 err_str.contains("circular reference"),
978 "expected circular reference error, got: {err_str}"
979 );
980 }
981
982 #[test]
983 fn eval_indirect_circular_reference() {
984 let mut snap = make_snapshot();
985 snap.set_cell(
987 "Sheet1",
988 1,
989 1,
990 CellValue::Formula {
991 expr: "B1".to_string(),
992 result: None,
993 },
994 );
995 snap.set_cell(
996 "Sheet1",
997 2,
998 1,
999 CellValue::Formula {
1000 expr: "A1".to_string(),
1001 result: None,
1002 },
1003 );
1004 let expr = Expr::CellRef(CellReference {
1005 col: "A".to_string(),
1006 row: 1,
1007 abs_col: false,
1008 abs_row: false,
1009 sheet: None,
1010 });
1011 let result = evaluate(&expr, &snap);
1012 assert!(result.is_err());
1013 assert!(result
1014 .unwrap_err()
1015 .to_string()
1016 .contains("circular reference"));
1017 }
1018
1019 #[test]
1022 fn eval_max_depth_exceeded() {
1023 let snap = make_snapshot();
1024 let mut expr = Expr::Number(1.0);
1026 for _ in 0..300 {
1027 expr = Expr::Paren(Box::new(expr));
1028 }
1029 let result = evaluate(&expr, &snap);
1030 assert!(result.is_err());
1031 assert!(result
1032 .unwrap_err()
1033 .to_string()
1034 .contains("maximum evaluation depth"));
1035 }
1036
1037 #[test]
1040 fn eval_sum_function() {
1041 let snap = make_snapshot();
1042 let expr = parse_formula("SUM(1,2,3)").unwrap();
1043 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(6.0));
1044 }
1045
1046 #[test]
1047 fn eval_average_function() {
1048 let snap = make_snapshot();
1049 let expr = parse_formula("AVERAGE(10,20,30)").unwrap();
1050 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(20.0));
1051 }
1052
1053 #[test]
1054 fn eval_if_true_branch() {
1055 let snap = make_snapshot();
1056 let expr = parse_formula("IF(TRUE,10,20)").unwrap();
1057 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(10.0));
1058 }
1059
1060 #[test]
1061 fn eval_if_false_branch() {
1062 let snap = make_snapshot();
1063 let expr = parse_formula("IF(FALSE,10,20)").unwrap();
1064 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(20.0));
1065 }
1066
1067 #[test]
1068 fn eval_unknown_function() {
1069 let snap = make_snapshot();
1070 let expr = parse_formula("NONEXISTENT(1)").unwrap();
1071 let result = evaluate(&expr, &snap);
1072 assert!(result.is_err());
1073 assert!(result.unwrap_err().to_string().contains("unknown function"));
1074 }
1075
1076 #[test]
1077 fn eval_nested_functions() {
1078 let snap = make_snapshot();
1079 let expr = parse_formula("SUM(1,MAX(2,3))").unwrap();
1080 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(4.0));
1081 }
1082
1083 #[test]
1084 fn eval_abs_function() {
1085 let snap = make_snapshot();
1086 let expr = parse_formula("ABS(-42)").unwrap();
1087 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(42.0));
1088 }
1089
1090 #[test]
1091 fn eval_formula_cell_chain() {
1092 let mut snap = make_snapshot();
1093 snap.set_cell("Sheet1", 1, 1, CellValue::Number(10.0));
1094 snap.set_cell("Sheet1", 2, 1, CellValue::Number(20.0));
1095 snap.set_cell(
1097 "Sheet1",
1098 3,
1099 1,
1100 CellValue::Formula {
1101 expr: "A1+B1".to_string(),
1102 result: None,
1103 },
1104 );
1105 let expr = Expr::CellRef(CellReference {
1106 col: "C".to_string(),
1107 row: 1,
1108 abs_col: false,
1109 abs_row: false,
1110 sheet: None,
1111 });
1112 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(30.0));
1113 }
1114
1115 #[test]
1116 fn eval_error_propagation_in_binary() {
1117 let snap = make_snapshot();
1118 let expr = Expr::BinaryOp {
1119 op: BinaryOperator::Add,
1120 left: Box::new(Expr::Error("#VALUE!".to_string())),
1121 right: Box::new(Expr::Number(1.0)),
1122 };
1123 assert_eq!(
1124 evaluate(&expr, &snap).unwrap(),
1125 CellValue::Error("#VALUE!".to_string())
1126 );
1127 }
1128
1129 #[test]
1130 fn eval_parsed_expression() {
1131 let mut snap = make_snapshot();
1132 snap.set_cell("Sheet1", 1, 1, CellValue::Number(5.0));
1133 snap.set_cell("Sheet1", 2, 1, CellValue::Number(3.0));
1134 let expr = parse_formula("A1*B1+2").unwrap();
1135 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Number(17.0));
1136 }
1137
1138 #[test]
1139 fn eval_comparison_strings() {
1140 let snap = make_snapshot();
1141 let expr = Expr::BinaryOp {
1142 op: BinaryOperator::Lt,
1143 left: Box::new(Expr::String("abc".to_string())),
1144 right: Box::new(Expr::String("def".to_string())),
1145 };
1146 assert_eq!(evaluate(&expr, &snap).unwrap(), CellValue::Bool(true));
1147 }
1148
1149 #[test]
1150 fn eval_wrong_arg_count() {
1151 let snap = make_snapshot();
1152 let expr = parse_formula("ABS(1,2)").unwrap();
1153 let result = evaluate(&expr, &snap);
1154 assert!(result.is_err());
1155 assert!(result.unwrap_err().to_string().contains("expects"));
1156 }
1157
1158 #[test]
1159 fn eval_concat_number_and_string() {
1160 let snap = make_snapshot();
1161 let expr = Expr::BinaryOp {
1162 op: BinaryOperator::Concat,
1163 left: Box::new(Expr::Number(42.0)),
1164 right: Box::new(Expr::String(" items".to_string())),
1165 };
1166 assert_eq!(
1167 evaluate(&expr, &snap).unwrap(),
1168 CellValue::String("42 items".to_string())
1169 );
1170 }
1171
1172 fn coord(sheet: &str, col: u32, row: u32) -> CellCoord {
1173 CellCoord {
1174 sheet: sheet.to_string(),
1175 col,
1176 row,
1177 }
1178 }
1179
1180 #[test]
1181 fn test_build_dependency_graph_simple() {
1182 let formula_cells = vec![(coord("Sheet1", 1, 1), "B1+C1".to_string())];
1184 let deps = build_dependency_graph(&formula_cells).unwrap();
1185 let a1_deps = deps.get(&coord("Sheet1", 1, 1)).unwrap();
1186 assert!(a1_deps.contains(&coord("Sheet1", 2, 1))); assert!(a1_deps.contains(&coord("Sheet1", 3, 1))); assert_eq!(a1_deps.len(), 2);
1189 }
1190
1191 #[test]
1192 fn test_build_dependency_graph_range() {
1193 let formula_cells = vec![(coord("Sheet1", 1, 1), "SUM(B1:B5)".to_string())];
1195 let deps = build_dependency_graph(&formula_cells).unwrap();
1196 let a1_deps = deps.get(&coord("Sheet1", 1, 1)).unwrap();
1197 assert_eq!(a1_deps.len(), 5);
1198 for r in 1..=5 {
1199 assert!(a1_deps.contains(&coord("Sheet1", 2, r)));
1200 }
1201 }
1202
1203 #[test]
1204 fn test_build_dependency_graph_cross_sheet() {
1205 let formula_cells = vec![(coord("Sheet1", 1, 1), "Sheet2!B1".to_string())];
1207 let deps = build_dependency_graph(&formula_cells).unwrap();
1208 let a1_deps = deps.get(&coord("Sheet1", 1, 1)).unwrap();
1209 assert_eq!(a1_deps.len(), 1);
1210 assert_eq!(a1_deps[0], coord("Sheet2", 2, 1));
1211 }
1212
1213 #[test]
1214 fn test_extract_cell_refs_from_nested_expr() {
1215 let expr = parse_formula("A1+SUM(B1:B3,C1)").unwrap();
1216 let refs = extract_cell_refs(&expr, "Sheet1");
1217 assert!(refs.contains(&coord("Sheet1", 1, 1))); assert!(refs.contains(&coord("Sheet1", 2, 1))); assert!(refs.contains(&coord("Sheet1", 2, 2))); assert!(refs.contains(&coord("Sheet1", 2, 3))); assert!(refs.contains(&coord("Sheet1", 3, 1))); assert_eq!(refs.len(), 5);
1223 }
1224
1225 #[test]
1226 fn test_topological_sort_linear_chain() {
1227 let formula_cells = vec![coord("Sheet1", 1, 2), coord("Sheet1", 1, 3)];
1229 let mut deps = HashMap::new();
1230 deps.insert(coord("Sheet1", 1, 2), vec![coord("Sheet1", 1, 1)]); deps.insert(
1232 coord("Sheet1", 1, 3),
1233 vec![coord("Sheet1", 1, 2)], );
1235 let sorted = topological_sort(&formula_cells, &deps).unwrap();
1236 assert_eq!(sorted.len(), 2);
1237 let pos_a2 = sorted
1239 .iter()
1240 .position(|c| *c == coord("Sheet1", 1, 2))
1241 .unwrap();
1242 let pos_a3 = sorted
1243 .iter()
1244 .position(|c| *c == coord("Sheet1", 1, 3))
1245 .unwrap();
1246 assert!(pos_a2 < pos_a3);
1247 }
1248
1249 #[test]
1250 fn test_topological_sort_diamond() {
1251 let formula_cells = vec![
1254 coord("Sheet1", 1, 1), coord("Sheet1", 2, 1), coord("Sheet1", 3, 1), ];
1258 let mut deps = HashMap::new();
1259 deps.insert(
1260 coord("Sheet1", 1, 1),
1261 vec![coord("Sheet1", 2, 1), coord("Sheet1", 3, 1)],
1262 );
1263 deps.insert(coord("Sheet1", 2, 1), vec![coord("Sheet1", 4, 1)]); deps.insert(coord("Sheet1", 3, 1), vec![coord("Sheet1", 4, 1)]); let sorted = topological_sort(&formula_cells, &deps).unwrap();
1267 assert_eq!(sorted.len(), 3);
1268 let pos_a1 = sorted
1269 .iter()
1270 .position(|c| *c == coord("Sheet1", 1, 1))
1271 .unwrap();
1272 let pos_b1 = sorted
1273 .iter()
1274 .position(|c| *c == coord("Sheet1", 2, 1))
1275 .unwrap();
1276 let pos_c1 = sorted
1277 .iter()
1278 .position(|c| *c == coord("Sheet1", 3, 1))
1279 .unwrap();
1280 assert!(pos_b1 < pos_a1);
1282 assert!(pos_c1 < pos_a1);
1283 }
1284
1285 #[test]
1286 fn test_topological_sort_cycle_detection() {
1287 let formula_cells = vec![coord("Sheet1", 1, 1), coord("Sheet1", 2, 1)];
1289 let mut deps = HashMap::new();
1290 deps.insert(coord("Sheet1", 1, 1), vec![coord("Sheet1", 2, 1)]);
1291 deps.insert(coord("Sheet1", 2, 1), vec![coord("Sheet1", 1, 1)]);
1292
1293 let result = topological_sort(&formula_cells, &deps);
1294 assert!(result.is_err());
1295 let err_str = result.unwrap_err().to_string();
1296 assert!(
1297 err_str.contains("circular reference"),
1298 "expected circular reference error, got: {err_str}"
1299 );
1300 }
1301
1302 #[test]
1303 fn test_topological_sort_no_formulas() {
1304 let formula_cells: Vec<CellCoord> = vec![];
1305 let deps: HashMap<CellCoord, Vec<CellCoord>> = HashMap::new();
1306 let sorted = topological_sort(&formula_cells, &deps).unwrap();
1307 assert!(sorted.is_empty());
1308 }
1309
1310 #[test]
1311 fn test_set_current_sheet() {
1312 let mut snap = CellSnapshot::new("Sheet1".to_string());
1313 assert_eq!(snap.current_sheet(), "Sheet1");
1314 snap.set_current_sheet("Sheet2");
1315 assert_eq!(snap.current_sheet(), "Sheet2");
1316 }
1317}