1use crate::{
2 CellRef,
3 broadcast::{broadcast_shape, project_index},
4 coercion,
5 traits::{ArgumentHandle, DefaultFunctionContext, EvaluationContext},
6};
7use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
8use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
9
10use crate::engine::arena::{AstNodeData, AstNodeId, DataStore};
11use crate::engine::sheet_registry::SheetRegistry;
12
13pub struct Interpreter<'a> {
16 pub context: &'a dyn EvaluationContext,
17 current_sheet: &'a str,
18 current_cell: Option<crate::CellRef>,
19}
20
21impl<'a> Interpreter<'a> {
22 pub fn new(context: &'a dyn EvaluationContext, current_sheet: &'a str) -> Self {
23 Self {
24 context,
25 current_sheet,
26 current_cell: None,
27 }
28 }
29
30 pub fn new_with_cell(
31 context: &'a dyn EvaluationContext,
32 current_sheet: &'a str,
33 cell: crate::CellRef,
34 ) -> Self {
35 Self {
36 context,
37 current_sheet,
38 current_cell: Some(cell),
39 }
40 }
41
42 pub fn current_sheet(&self) -> &'a str {
43 self.current_sheet
44 }
45
46 pub fn resolve_range_view<'c>(
47 &'c self,
48 reference: &ReferenceType,
49 current_sheet: &str,
50 ) -> Result<crate::engine::range_view::RangeView<'c>, ExcelError> {
51 self.context.resolve_range_view(reference, current_sheet)
52 }
53
54 pub fn evaluate_ast_as_reference(&self, node: &ASTNode) -> Result<ReferenceType, ExcelError> {
59 match &node.node_type {
60 ASTNodeType::Reference { reference, .. } => Ok(reference.clone()),
61 ASTNodeType::Function { name, args } => {
62 if let Some(fun) = self.context.get_function("", name) {
63 let handles: Vec<ArgumentHandle> =
65 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
66 let fctx = DefaultFunctionContext::new_with_sheet(
67 self.context,
68 None,
69 self.current_sheet,
70 );
71 if let Some(res) = fun.eval_reference(&handles, &fctx) {
72 res
73 } else {
74 Err(ExcelError::new(ExcelErrorKind::Ref)
75 .with_message("Function does not return a reference"))
76 }
77 } else {
78 Err(ExcelError::new(ExcelErrorKind::Name)
79 .with_message(format!("Unknown function: {name}")))
80 }
81 }
82 ASTNodeType::BinaryOp { op, left, right } if op == ":" => {
83 let lref = self.evaluate_ast_as_reference(left)?;
84 let rref = self.evaluate_ast_as_reference(right)?;
85 crate::reference::combine_references(&lref, &rref)
86 }
87 ASTNodeType::Array(_)
88 | ASTNodeType::UnaryOp { .. }
89 | ASTNodeType::BinaryOp { .. }
90 | ASTNodeType::Literal(_) => Err(ExcelError::new(ExcelErrorKind::Ref)
91 .with_message("Expression cannot be used as a reference")),
92 }
93 }
94
95 pub(crate) fn evaluate_arena_ast_as_reference(
96 &self,
97 node_id: AstNodeId,
98 data_store: &DataStore,
99 sheet_registry: &SheetRegistry,
100 ) -> Result<ReferenceType, ExcelError> {
101 let node = data_store.get_node(node_id).ok_or_else(|| {
102 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
103 })?;
104
105 match node {
106 AstNodeData::Reference { ref_type, .. } => {
107 Ok(data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry))
108 }
109 AstNodeData::Function { name_id, .. } => {
110 let name = data_store.resolve_ast_string(*name_id);
111 let fun = self.context.get_function("", name).ok_or_else(|| {
112 ExcelError::new(ExcelErrorKind::Name)
113 .with_message(format!("Unknown function: {name}"))
114 })?;
115
116 let args = data_store.get_args(node_id).ok_or_else(|| {
117 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
118 })?;
119
120 let handles: Vec<ArgumentHandle> = args
121 .iter()
122 .copied()
123 .map(|arg_id| {
124 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
125 })
126 .collect();
127
128 let fctx =
129 DefaultFunctionContext::new_with_sheet(self.context, None, self.current_sheet);
130
131 fun.eval_reference(&handles, &fctx).ok_or_else(|| {
132 ExcelError::new(ExcelErrorKind::Ref)
133 .with_message("Function does not return a reference")
134 })?
135 }
136 AstNodeData::BinaryOp {
137 op_id,
138 left_id,
139 right_id,
140 } => {
141 let op = data_store.resolve_ast_string(*op_id);
142 if op != ":" {
143 return Err(ExcelError::new(ExcelErrorKind::Ref)
144 .with_message("Expression cannot be used as a reference"));
145 }
146 let lref =
147 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
148 let rref =
149 self.evaluate_arena_ast_as_reference(*right_id, data_store, sheet_registry)?;
150 crate::reference::combine_references(&lref, &rref)
151 }
152 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
153 .with_message("Expression cannot be used as a reference")),
154 }
155 }
156
157 pub fn evaluate_ast(&self, node: &ASTNode) -> Result<LiteralValue, ExcelError> {
159 self.evaluate_ast_uncached(node)
160 }
161
162 pub(crate) fn evaluate_arena_ast(
163 &self,
164 node_id: AstNodeId,
165 data_store: &DataStore,
166 sheet_registry: &SheetRegistry,
167 ) -> Result<LiteralValue, ExcelError> {
168 let node = data_store.get_node(node_id).ok_or_else(|| {
169 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
170 })?;
171
172 match node {
173 AstNodeData::Literal(vref) => Ok(data_store.retrieve_value(*vref)),
174 AstNodeData::Reference { ref_type, .. } => {
175 let reference =
176 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
177 self.eval_reference(&reference)
178 }
179 AstNodeData::UnaryOp { op_id, expr_id } => {
180 let expr = self.evaluate_arena_ast(*expr_id, data_store, sheet_registry)?;
181
182 let op = data_store.resolve_ast_string(*op_id);
183 match expr {
184 LiteralValue::Array(arr) => {
185 self.map_array(arr, |cell| self.eval_unary_scalar(op, cell))
186 }
187 other => self.eval_unary_scalar(op, other),
188 }
189 }
190 AstNodeData::BinaryOp {
191 op_id,
192 left_id,
193 right_id,
194 } => {
195 let op = data_store.resolve_ast_string(*op_id);
196 if op == ":" {
197 let lref =
198 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
199 let rref = self.evaluate_arena_ast_as_reference(
200 *right_id,
201 data_store,
202 sheet_registry,
203 )?;
204 return match crate::reference::combine_references(&lref, &rref) {
205 Ok(_r) => Ok(LiteralValue::Error(
206 ExcelError::new(ExcelErrorKind::Ref).with_message(
207 "Reference produced by ':' cannot be used directly as a value",
208 ),
209 )),
210 Err(e) => Ok(LiteralValue::Error(e)),
211 };
212 }
213
214 let left = self.evaluate_arena_ast(*left_id, data_store, sheet_registry)?;
215 let right = self.evaluate_arena_ast(*right_id, data_store, sheet_registry)?;
216
217 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
218 return self.compare(op, left, right);
219 }
220
221 match op {
222 "+" => self.numeric_binary(left, right, |a, b| a + b),
223 "-" => self.numeric_binary(left, right, |a, b| a - b),
224 "*" => self.numeric_binary(left, right, |a, b| a * b),
225 "/" => self.divide(left, right),
226 "^" => self.power(left, right),
227 "&" => Ok(LiteralValue::Text(format!(
228 "{}{}",
229 crate::coercion::to_text_invariant(&left),
230 crate::coercion::to_text_invariant(&right)
231 ))),
232 _ => Err(ExcelError::new(ExcelErrorKind::NImpl)
233 .with_message(format!("Binary op '{op}'"))),
234 }
235 }
236 AstNodeData::Array { .. } => {
237 let (rows, cols, elements) =
238 data_store.get_array_elems(node_id).ok_or_else(|| {
239 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
240 })?;
241
242 let rows_usize = rows as usize;
243 let cols_usize = cols as usize;
244 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows_usize);
245 for r in 0..rows_usize {
246 let mut row = Vec::with_capacity(cols_usize);
247 for c in 0..cols_usize {
248 let idx = r * cols_usize + c;
249 if let Some(&elem_id) = elements.get(idx) {
250 row.push(self.evaluate_arena_ast(
251 elem_id,
252 data_store,
253 sheet_registry,
254 )?);
255 }
256 }
257 out.push(row);
258 }
259
260 Ok(LiteralValue::Array(out))
261 }
262 AstNodeData::Function { name_id, .. } => {
263 let name = data_store.resolve_ast_string(*name_id);
264 let fun = self.context.get_function("", name).ok_or_else(|| {
265 ExcelError::new(ExcelErrorKind::Name)
266 .with_message(format!("Unknown function: {name}"))
267 })?;
268
269 let args = data_store.get_args(node_id).ok_or_else(|| {
270 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
271 })?;
272
273 let handles: Vec<ArgumentHandle> = args
274 .iter()
275 .copied()
276 .map(|arg_id| {
277 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
278 })
279 .collect();
280
281 let fctx = DefaultFunctionContext::new_with_sheet(
282 self.context,
283 self.current_cell,
284 self.current_sheet,
285 );
286
287 fun.dispatch(&handles, &fctx)
288 }
289 }
290 }
291
292 fn evaluate_ast_uncached(&self, node: &ASTNode) -> Result<LiteralValue, ExcelError> {
293 let current_sheet = self.current_sheet.to_string();
297 let range_probe = |reference: &ReferenceType| -> Option<(u32, u32)> {
298 use formualizer_parse::parser::ReferenceType as RT;
300 match reference {
301 RT::Range {
302 sheet,
303 start_row,
304 start_col,
305 end_row,
306 end_col,
307 } => {
308 let sheet_name = sheet.as_deref().unwrap_or(¤t_sheet);
309 let mut sr = *start_row;
311 let mut sc = *start_col;
312 let mut er = *end_row;
313 let mut ec = *end_col;
314
315 if sr.is_none() && er.is_none() {
317 let scv = sc.unwrap_or(1);
319 let ecv = ec.unwrap_or(scv);
320 sr = Some(1);
321 if let Some((_, max_r)) =
322 self.context.used_rows_for_columns(sheet_name, scv, ecv)
323 {
324 er = Some(max_r);
325 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
326 er = Some(max_rows);
327 }
328 }
329
330 if sc.is_none() && ec.is_none() {
332 let srv = sr.unwrap_or(1);
334 let erv = er.unwrap_or(srv);
335 sc = Some(1);
336 if let Some((_, max_c)) =
337 self.context.used_cols_for_rows(sheet_name, srv, erv)
338 {
339 ec = Some(max_c);
340 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
341 ec = Some(max_cols);
342 }
343 }
344
345 if sr.is_some() && er.is_none() {
347 let scv = sc.unwrap_or(1);
348 let ecv = ec.unwrap_or(scv);
349 if let Some((_, max_r)) =
350 self.context.used_rows_for_columns(sheet_name, scv, ecv)
351 {
352 er = Some(max_r);
353 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
354 er = Some(max_rows);
355 }
356 }
357 if er.is_some() && sr.is_none() {
358 sr = Some(1);
360 }
361 if sc.is_some() && ec.is_none() {
362 let srv = sr.unwrap_or(1);
363 let erv = er.unwrap_or(srv);
364 if let Some((_, max_c)) =
365 self.context.used_cols_for_rows(sheet_name, srv, erv)
366 {
367 ec = Some(max_c);
368 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
369 ec = Some(max_cols);
370 }
371 }
372 if ec.is_some() && sc.is_none() {
373 sc = Some(1);
375 }
376
377 let sr = sr.unwrap_or(1);
378 let sc = sc.unwrap_or(1);
379 let er = er.unwrap_or(sr.saturating_sub(1));
380 let ec = ec.unwrap_or(sc.saturating_sub(1));
381 if er < sr || ec < sc {
382 return Some((0, 0));
383 }
384 Some((er.saturating_sub(sr) + 1, ec.saturating_sub(sc) + 1))
385 }
386 RT::Cell { .. } => Some((1, 1)),
387 _ => None,
388 }
389 };
390 let fn_lookup = |ns: &str, name: &str| self.context.get_function(ns, name);
391
392 let mut planner = crate::planner::Planner::new(crate::planner::PlanConfig::default())
393 .with_range_probe(&range_probe)
394 .with_function_lookup(&fn_lookup);
395 let plan = planner.plan(node);
396 self.eval_with_plan(node, &plan.root)
397 }
398
399 fn eval_with_plan(
400 &self,
401 node: &ASTNode,
402 plan_node: &crate::planner::PlanNode,
403 ) -> Result<LiteralValue, ExcelError> {
404 match &node.node_type {
405 ASTNodeType::Literal(v) => Ok(v.clone()),
406 ASTNodeType::Reference { reference, .. } => self.eval_reference(reference),
407 ASTNodeType::UnaryOp { op, expr } => {
408 self.eval_unary(op, expr)
411 }
412 ASTNodeType::BinaryOp { op, left, right } => self.eval_binary(op, left, right),
413 ASTNodeType::Function { name, args } => {
414 let strategy = plan_node.strategy;
415 if let Some(fun) = self.context.get_function("", name) {
416 use crate::function::FnCaps;
417 use crate::planner::ExecStrategy;
418 let caps = fun.caps();
419
420 if caps.contains(FnCaps::SHORT_CIRCUIT) || caps.contains(FnCaps::VOLATILE) {
422 return self.eval_function(name, args);
423 }
424
425 if matches!(strategy, ExecStrategy::ChunkedReduce)
427 && caps.contains(FnCaps::WINDOWED)
428 {
429 let handles: Vec<ArgumentHandle> =
430 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
431 let fctx = DefaultFunctionContext::new_with_sheet(
432 self.context,
433 self.current_cell,
434 self.current_sheet,
435 );
436 let mut w = crate::window_ctx::SimpleWindowCtx::new(
437 &handles,
438 &fctx,
439 crate::window_ctx::WindowSpec::default(),
440 );
441 if let Some(res) = fun.eval_window(&mut w) {
442 return res;
443 }
444 return self.eval_function(name, args);
446 }
447
448 if matches!(strategy, ExecStrategy::ArgParallel)
450 && caps.contains(FnCaps::PARALLEL_ARGS)
451 {
452 for arg in args {
454 match &arg.node_type {
455 ASTNodeType::Reference { reference, .. } => {
456 let _ = self
457 .context
458 .resolve_range_view(reference, self.current_sheet);
459 }
460 _ => {
461 let _ = self.evaluate_ast(arg);
462 }
463 }
464 }
465 return self.eval_function(name, args);
466 }
467
468 return self.eval_function(name, args);
470 }
471 self.eval_function(name, args)
472 }
473 ASTNodeType::Array(rows) => self.eval_array_literal(rows),
474 }
475 }
476
477 fn eval_reference(&self, reference: &ReferenceType) -> Result<LiteralValue, ExcelError> {
479 let view = self
480 .context
481 .resolve_range_view(reference, self.current_sheet)?;
482
483 match reference {
484 ReferenceType::Cell { .. } => {
485 Ok(view.as_1x1().unwrap_or(LiteralValue::Empty))
487 }
488 _ => {
489 let (rows, cols) = view.dims();
491 let mut data = Vec::with_capacity(rows);
492
493 view.for_each_row(&mut |row| {
494 let row_data: Vec<LiteralValue> = (0..cols)
495 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
496 .collect();
497 data.push(row_data);
498 Ok(())
499 })?;
500
501 if data.len() == 1 && data[0].len() == 1 {
502 Ok(data[0][0].clone())
503 } else {
504 Ok(LiteralValue::Array(data))
505 }
506 }
507 }
508 }
509
510 fn eval_unary(&self, op: &str, expr: &ASTNode) -> Result<LiteralValue, ExcelError> {
512 let v = self.evaluate_ast(expr)?;
513 match v {
514 LiteralValue::Array(arr) => {
515 self.map_array(arr, |cell| self.eval_unary_scalar(op, cell))
516 }
517 other => self.eval_unary_scalar(op, other),
518 }
519 }
520
521 fn eval_unary_scalar(&self, op: &str, v: LiteralValue) -> Result<LiteralValue, ExcelError> {
522 match op {
523 "+" => self.apply_number_unary(v, |n| n),
524 "-" => self.apply_number_unary(v, |n| -n),
525 "%" => self.apply_number_unary(v, |n| n / 100.0),
526 _ => {
527 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(format!("Unary op '{op}'")))
528 }
529 }
530 }
531
532 fn apply_number_unary<F>(&self, v: LiteralValue, f: F) -> Result<LiteralValue, ExcelError>
533 where
534 F: Fn(f64) -> f64,
535 {
536 match crate::coercion::to_number_lenient_with_locale(&v, &self.context.locale()) {
537 Ok(n) => match crate::coercion::sanitize_numeric(f(n)) {
538 Ok(n2) => Ok(LiteralValue::Number(n2)),
539 Err(e) => Ok(LiteralValue::Error(e)),
540 },
541 Err(e) => Ok(LiteralValue::Error(e)),
542 }
543 }
544
545 fn eval_binary(
547 &self,
548 op: &str,
549 left: &ASTNode,
550 right: &ASTNode,
551 ) -> Result<LiteralValue, ExcelError> {
552 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
554 let l = self.evaluate_ast(left)?;
555 let r = self.evaluate_ast(right)?;
556 return self.compare(op, l, r);
557 }
558
559 let l_val = self.evaluate_ast(left)?;
560 let r_val = self.evaluate_ast(right)?;
561
562 match op {
563 "+" => self.numeric_binary(l_val, r_val, |a, b| a + b),
564 "-" => self.numeric_binary(l_val, r_val, |a, b| a - b),
565 "*" => self.numeric_binary(l_val, r_val, |a, b| a * b),
566 "/" => self.divide(l_val, r_val),
567 "^" => self.power(l_val, r_val),
568 "&" => Ok(LiteralValue::Text(format!(
569 "{}{}",
570 crate::coercion::to_text_invariant(&l_val),
571 crate::coercion::to_text_invariant(&r_val)
572 ))),
573 ":" => {
574 let lref = self.evaluate_ast_as_reference(left)?;
576 let rref = self.evaluate_ast_as_reference(right)?;
577 match crate::reference::combine_references(&lref, &rref) {
578 Ok(_r) => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
579 "Reference produced by ':' cannot be used directly as a value",
580 )),
581 Err(e) => Ok(LiteralValue::Error(e)),
582 }
583 }
584 _ => {
585 Err(ExcelError::new(ExcelErrorKind::NImpl)
586 .with_message(format!("Binary op '{op}'")))
587 }
588 }
589 }
590
591 fn eval_function(&self, name: &str, args: &[ASTNode]) -> Result<LiteralValue, ExcelError> {
593 if let Some(fun) = self.context.get_function("", name) {
594 let handles: Vec<ArgumentHandle> =
595 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
596 let fctx = DefaultFunctionContext::new_with_sheet(
598 self.context,
599 self.current_cell,
600 self.current_sheet,
601 );
602 fun.dispatch(&handles, &fctx)
603 } else {
604 Ok(LiteralValue::Error(
606 ExcelError::new(ExcelErrorKind::Name)
607 .with_message(format!("Unknown function: {name}")),
608 ))
609 }
610 }
611
612 pub fn function_context(&self, cell_ref: Option<&CellRef>) -> DefaultFunctionContext<'_> {
613 DefaultFunctionContext::new_with_sheet(self.context, cell_ref.cloned(), self.current_sheet)
614 }
615
616 fn eval_array_literal(&self, rows: &[Vec<ASTNode>]) -> Result<LiteralValue, ExcelError> {
621 let mut out = Vec::with_capacity(rows.len());
622 for row in rows {
623 let mut r = Vec::with_capacity(row.len());
624 for cell in row {
625 r.push(self.evaluate_ast(cell)?);
626 }
627 out.push(r);
628 }
629 Ok(LiteralValue::Array(out))
630 }
631
632 fn numeric_binary<F>(
634 &self,
635 left: LiteralValue,
636 right: LiteralValue,
637 f: F,
638 ) -> Result<LiteralValue, ExcelError>
639 where
640 F: Fn(f64, f64) -> f64 + Copy,
641 {
642 self.broadcast_apply(left, right, |l, r| {
643 let a = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
644 let b = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
645 match (a, b) {
646 (Ok(a), Ok(b)) => match crate::coercion::sanitize_numeric(f(a, b)) {
647 Ok(n2) => Ok(LiteralValue::Number(n2)),
648 Err(e) => Ok(LiteralValue::Error(e)),
649 },
650 (Err(e), _) | (_, Err(e)) => Ok(LiteralValue::Error(e)),
651 }
652 })
653 }
654
655 fn divide(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
656 self.broadcast_apply(left, right, |l, r| {
657 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
658 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
659 let (a, b) = match (ln, rn) {
660 (Ok(a), Ok(b)) => (a, b),
661 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
662 };
663 if b == 0.0 {
664 return Ok(LiteralValue::Error(ExcelError::from_error_string(
665 "#DIV/0!",
666 )));
667 }
668 match crate::coercion::sanitize_numeric(a / b) {
669 Ok(n) => Ok(LiteralValue::Number(n)),
670 Err(e) => Ok(LiteralValue::Error(e)),
671 }
672 })
673 }
674
675 fn power(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
676 self.broadcast_apply(left, right, |l, r| {
677 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
678 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
679 let (a, b) = match (ln, rn) {
680 (Ok(a), Ok(b)) => (a, b),
681 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
682 };
683 if a < 0.0 && b.fract() != 0.0 {
685 return Ok(LiteralValue::Error(ExcelError::new_num()));
686 }
687 match crate::coercion::sanitize_numeric(a.powf(b)) {
688 Ok(n) => Ok(LiteralValue::Number(n)),
689 Err(e) => Ok(LiteralValue::Error(e)),
690 }
691 })
692 }
693
694 fn map_array<F>(&self, arr: Vec<Vec<LiteralValue>>, f: F) -> Result<LiteralValue, ExcelError>
695 where
696 F: Fn(LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
697 {
698 let mut out = Vec::with_capacity(arr.len());
699 for row in arr {
700 let mut new_row = Vec::with_capacity(row.len());
701 for cell in row {
702 new_row.push(match f(cell) {
703 Ok(v) => v,
704 Err(e) => LiteralValue::Error(e),
705 });
706 }
707 out.push(new_row);
708 }
709 Ok(LiteralValue::Array(out))
710 }
711
712 fn combine_arrays<F>(
713 &self,
714 l: Vec<Vec<LiteralValue>>,
715 r: Vec<Vec<LiteralValue>>,
716 f: F,
717 ) -> Result<LiteralValue, ExcelError>
718 where
719 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
720 {
721 let l_shape = (l.len(), l.first().map(|r| r.len()).unwrap_or(0));
723 let r_shape = (r.len(), r.first().map(|r| r.len()).unwrap_or(0));
724 let target = match broadcast_shape(&[l_shape, r_shape]) {
725 Ok(s) => s,
726 Err(e) => return Ok(LiteralValue::Error(e)),
727 };
728
729 let mut out = Vec::with_capacity(target.0);
730 for i in 0..target.0 {
731 let mut row = Vec::with_capacity(target.1);
732 for j in 0..target.1 {
733 let (li, lj) = project_index((i, j), l_shape);
734 let (ri, rj) = project_index((i, j), r_shape);
735 let lv = l
736 .get(li)
737 .and_then(|r| r.get(lj))
738 .cloned()
739 .unwrap_or(LiteralValue::Empty);
740 let rv = r
741 .get(ri)
742 .and_then(|r| r.get(rj))
743 .cloned()
744 .unwrap_or(LiteralValue::Empty);
745 row.push(match f(lv, rv) {
746 Ok(v) => v,
747 Err(e) => LiteralValue::Error(e),
748 });
749 }
750 out.push(row);
751 }
752 Ok(LiteralValue::Array(out))
753 }
754
755 fn broadcast_apply<F>(
756 &self,
757 left: LiteralValue,
758 right: LiteralValue,
759 f: F,
760 ) -> Result<LiteralValue, ExcelError>
761 where
762 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
763 {
764 use LiteralValue::*;
765 match (left, right) {
766 (Array(l), Array(r)) => self.combine_arrays(l, r, f),
767 (Array(arr), v) => {
768 let shape_l = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
769 let shape_r = (1usize, 1usize);
770 let target = match broadcast_shape(&[shape_l, shape_r]) {
771 Ok(s) => s,
772 Err(e) => return Ok(LiteralValue::Error(e)),
773 };
774 let mut out = Vec::with_capacity(target.0);
775 for i in 0..target.0 {
776 let mut row = Vec::with_capacity(target.1);
777 for j in 0..target.1 {
778 let (li, lj) = project_index((i, j), shape_l);
779 let lv = arr
780 .get(li)
781 .and_then(|r| r.get(lj))
782 .cloned()
783 .unwrap_or(LiteralValue::Empty);
784 row.push(match f(lv, v.clone()) {
785 Ok(vv) => vv,
786 Err(e) => LiteralValue::Error(e),
787 });
788 }
789 out.push(row);
790 }
791 Ok(LiteralValue::Array(out))
792 }
793 (v, Array(arr)) => {
794 let shape_l = (1usize, 1usize);
795 let shape_r = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
796 let target = match broadcast_shape(&[shape_l, shape_r]) {
797 Ok(s) => s,
798 Err(e) => return Ok(LiteralValue::Error(e)),
799 };
800 let mut out = Vec::with_capacity(target.0);
801 for i in 0..target.0 {
802 let mut row = Vec::with_capacity(target.1);
803 for j in 0..target.1 {
804 let (ri, rj) = project_index((i, j), shape_r);
805 let rv = arr
806 .get(ri)
807 .and_then(|r| r.get(rj))
808 .cloned()
809 .unwrap_or(LiteralValue::Empty);
810 row.push(match f(v.clone(), rv) {
811 Ok(vv) => vv,
812 Err(e) => LiteralValue::Error(e),
813 });
814 }
815 out.push(row);
816 }
817 Ok(LiteralValue::Array(out))
818 }
819 (l, r) => f(l, r),
820 }
821 }
822
823 fn coerce_number(&self, v: &LiteralValue) -> Result<f64, ExcelError> {
825 coercion::to_number_lenient(v)
826 }
827
828 fn coerce_text(&self, v: &LiteralValue) -> String {
829 coercion::to_text_invariant(v)
830 }
831
832 fn compare(
834 &self,
835 op: &str,
836 left: LiteralValue,
837 right: LiteralValue,
838 ) -> Result<LiteralValue, ExcelError> {
839 use LiteralValue::*;
840 if matches!(left, Error(_)) {
841 return Ok(left);
842 }
843 if matches!(right, Error(_)) {
844 return Ok(right);
845 }
846
847 match (left, right) {
849 (Array(l), Array(r)) => self.combine_arrays(l, r, |a, b| self.compare(op, a, b)),
850 (Array(arr), v) => self.broadcast_apply(Array(arr), v, |a, b| self.compare(op, a, b)),
851 (v, Array(arr)) => self.broadcast_apply(v, Array(arr), |a, b| self.compare(op, a, b)),
852 (l, r) => {
853 let res = match (l, r) {
854 (Number(a), Number(b)) => self.cmp_f64(a, b, op),
855 (Int(a), Number(b)) => self.cmp_f64(a as f64, b, op),
856 (Number(a), Int(b)) => self.cmp_f64(a, b as f64, op),
857 (Boolean(a), Boolean(b)) => {
858 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
859 }
860 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
861 (a, b) => {
862 let an = crate::coercion::to_number_lenient_with_locale(
864 &a,
865 &self.context.locale(),
866 )
867 .ok();
868 let bn = crate::coercion::to_number_lenient_with_locale(
869 &b,
870 &self.context.locale(),
871 )
872 .ok();
873 if let (Some(a), Some(b)) = (an, bn) {
874 self.cmp_f64(a, b, op)
875 } else {
876 self.cmp_text(
877 &crate::coercion::to_text_invariant(&a),
878 &crate::coercion::to_text_invariant(&b),
879 op,
880 )
881 }
882 }
883 };
884 Ok(LiteralValue::Boolean(res))
885 }
886 }
887 }
888
889 fn cmp_f64(&self, a: f64, b: f64, op: &str) -> bool {
890 match op {
891 "=" => a == b,
892 "<>" => a != b,
893 ">" => a > b,
894 "<" => a < b,
895 ">=" => a >= b,
896 "<=" => a <= b,
897 _ => unreachable!(),
898 }
899 }
900 fn cmp_text(&self, a: &str, b: &str, op: &str) -> bool {
901 let loc = self.context.locale();
902 let (a, b) = (loc.fold_case_invariant(a), loc.fold_case_invariant(b));
903 self.cmp_f64(
904 a.cmp(&b) as i32 as f64,
905 0.0,
906 match op {
907 "=" => "=",
908 "<>" => "<>",
909 ">" => ">",
910 "<" => "<",
911 ">=" => ">=",
912 "<=" => "<=",
913 _ => unreachable!(),
914 },
915 )
916 }
917}