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<crate::traits::CalcValue<'a>, 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<crate::traits::CalcValue<'a>, 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(crate::traits::CalcValue::Scalar(
174 data_store.retrieve_value(*vref),
175 )),
176 AstNodeData::Reference { ref_type, .. } => {
177 let reference =
178 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
179 self.eval_reference_to_calc(&reference)
180 }
181 AstNodeData::UnaryOp { op_id, expr_id } => {
182 let expr = self.evaluate_arena_ast(*expr_id, data_store, sheet_registry)?;
183
184 let op = data_store.resolve_ast_string(*op_id);
185 let v = expr.into_literal();
187 match v {
188 LiteralValue::Array(arr) => self
189 .map_array(arr, |cell| self.eval_unary_scalar(op, cell))
190 .map(crate::traits::CalcValue::Scalar),
191 other => self
192 .eval_unary_scalar(op, other)
193 .map(crate::traits::CalcValue::Scalar),
194 }
195 }
196 AstNodeData::BinaryOp {
197 op_id,
198 left_id,
199 right_id,
200 } => {
201 let op = data_store.resolve_ast_string(*op_id);
202 if op == ":" {
203 let lref =
204 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
205 let rref = self.evaluate_arena_ast_as_reference(
206 *right_id,
207 data_store,
208 sheet_registry,
209 )?;
210 return match crate::reference::combine_references(&lref, &rref) {
211 Ok(_r) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
212 ExcelError::new(ExcelErrorKind::Ref).with_message(
213 "Reference produced by ':' cannot be used directly as a value",
214 ),
215 ))),
216 Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
217 };
218 }
219
220 let left = self
221 .evaluate_arena_ast(*left_id, data_store, sheet_registry)?
222 .into_literal();
223 let right = self
224 .evaluate_arena_ast(*right_id, data_store, sheet_registry)?
225 .into_literal();
226
227 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
228 return self
229 .compare(op, left, right)
230 .map(crate::traits::CalcValue::Scalar);
231 }
232
233 match op {
234 "+" => self
235 .numeric_binary(left, right, |a, b| a + b)
236 .map(crate::traits::CalcValue::Scalar),
237 "-" => self
238 .numeric_binary(left, right, |a, b| a - b)
239 .map(crate::traits::CalcValue::Scalar),
240 "*" => self
241 .numeric_binary(left, right, |a, b| a * b)
242 .map(crate::traits::CalcValue::Scalar),
243 "/" => self
244 .divide(left, right)
245 .map(crate::traits::CalcValue::Scalar),
246 "^" => self
247 .power(left, right)
248 .map(crate::traits::CalcValue::Scalar),
249 "&" => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
250 format!(
251 "{}{}",
252 crate::coercion::to_text_invariant(&left),
253 crate::coercion::to_text_invariant(&right)
254 ),
255 ))),
256 _ => Err(ExcelError::new(ExcelErrorKind::NImpl)
257 .with_message(format!("Binary op '{op}'"))),
258 }
259 }
260 AstNodeData::Array { .. } => {
261 let (rows, cols, elements) =
262 data_store.get_array_elems(node_id).ok_or_else(|| {
263 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
264 })?;
265
266 let rows_usize = rows as usize;
267 let cols_usize = cols as usize;
268 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows_usize);
269 for r in 0..rows_usize {
270 let mut row = Vec::with_capacity(cols_usize);
271 for c in 0..cols_usize {
272 let idx = r * cols_usize + c;
273 if let Some(&elem_id) = elements.get(idx) {
274 row.push(
275 self.evaluate_arena_ast(elem_id, data_store, sheet_registry)?
276 .into_literal(),
277 );
278 }
279 }
280 out.push(row);
281 }
282
283 Ok(crate::traits::CalcValue::Range(
284 crate::engine::range_view::RangeView::from_owned_rows(
285 out,
286 self.context.date_system(),
287 ),
288 ))
289 }
290 AstNodeData::Function { name_id, .. } => {
291 let name = data_store.resolve_ast_string(*name_id);
292 let fun = self.context.get_function("", name).ok_or_else(|| {
293 ExcelError::new(ExcelErrorKind::Name)
294 .with_message(format!("Unknown function: {name}"))
295 })?;
296
297 let args = data_store.get_args(node_id).ok_or_else(|| {
298 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
299 })?;
300
301 let handles: Vec<ArgumentHandle> = args
302 .iter()
303 .copied()
304 .map(|arg_id| {
305 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
306 })
307 .collect();
308
309 let fctx = DefaultFunctionContext::new_with_sheet(
310 self.context,
311 self.current_cell,
312 self.current_sheet,
313 );
314
315 fun.dispatch(&handles, &fctx)
316 }
317 }
318 }
319
320 fn evaluate_ast_uncached(
321 &self,
322 node: &ASTNode,
323 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
324 let current_sheet = self.current_sheet.to_string();
328 let range_probe = |reference: &ReferenceType| -> Option<(u32, u32)> {
329 use formualizer_parse::parser::ReferenceType as RT;
331 match reference {
332 RT::Range {
333 sheet,
334 start_row,
335 start_col,
336 end_row,
337 end_col,
338 ..
339 } => {
340 let sheet_name = sheet.as_deref().unwrap_or(¤t_sheet);
341 let mut sr = *start_row;
343 let mut sc = *start_col;
344 let mut er = *end_row;
345 let mut ec = *end_col;
346
347 if sr.is_none() && er.is_none() {
349 let scv = sc.unwrap_or(1);
351 let ecv = ec.unwrap_or(scv);
352 sr = Some(1);
353 if let Some((_, max_r)) =
354 self.context.used_rows_for_columns(sheet_name, scv, ecv)
355 {
356 er = Some(max_r);
357 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
358 er = Some(max_rows);
359 }
360 }
361
362 if sc.is_none() && ec.is_none() {
364 let srv = sr.unwrap_or(1);
366 let erv = er.unwrap_or(srv);
367 sc = Some(1);
368 if let Some((_, max_c)) =
369 self.context.used_cols_for_rows(sheet_name, srv, erv)
370 {
371 ec = Some(max_c);
372 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
373 ec = Some(max_cols);
374 }
375 }
376
377 if sr.is_some() && er.is_none() {
379 let scv = sc.unwrap_or(1);
380 let ecv = ec.unwrap_or(scv);
381 if let Some((_, max_r)) =
382 self.context.used_rows_for_columns(sheet_name, scv, ecv)
383 {
384 er = Some(max_r);
385 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
386 er = Some(max_rows);
387 }
388 }
389 if er.is_some() && sr.is_none() {
390 sr = Some(1);
392 }
393 if sc.is_some() && ec.is_none() {
394 let srv = sr.unwrap_or(1);
395 let erv = er.unwrap_or(srv);
396 if let Some((_, max_c)) =
397 self.context.used_cols_for_rows(sheet_name, srv, erv)
398 {
399 ec = Some(max_c);
400 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
401 ec = Some(max_cols);
402 }
403 }
404 if ec.is_some() && sc.is_none() {
405 sc = Some(1);
407 }
408
409 let sr = sr.unwrap_or(1);
410 let sc = sc.unwrap_or(1);
411 let er = er.unwrap_or(sr.saturating_sub(1));
412 let ec = ec.unwrap_or(sc.saturating_sub(1));
413 if er < sr || ec < sc {
414 return Some((0, 0));
415 }
416 Some((er.saturating_sub(sr) + 1, ec.saturating_sub(sc) + 1))
417 }
418 RT::Cell { .. } => Some((1, 1)),
419 _ => None,
420 }
421 };
422 let fn_lookup = |ns: &str, name: &str| self.context.get_function(ns, name);
423
424 let mut planner = crate::planner::Planner::new(crate::planner::PlanConfig::default())
425 .with_range_probe(&range_probe)
426 .with_function_lookup(&fn_lookup);
427 let plan = planner.plan(node);
428 self.eval_with_plan(node, &plan.root)
429 }
430
431 fn eval_with_plan(
432 &self,
433 node: &ASTNode,
434 plan_node: &crate::planner::PlanNode,
435 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
436 match &node.node_type {
437 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
438 ASTNodeType::Reference { reference, .. } => self.eval_reference_to_calc(reference),
439 ASTNodeType::UnaryOp { op, expr } => {
440 self.eval_unary(op, expr)
443 .map(crate::traits::CalcValue::Scalar)
444 }
445 ASTNodeType::BinaryOp { op, left, right } => self
446 .eval_binary(op, left, right)
447 .map(crate::traits::CalcValue::Scalar),
448 ASTNodeType::Function { name, args } => {
449 let strategy = plan_node.strategy;
450 if let Some(fun) = self.context.get_function("", name) {
451 use crate::function::FnCaps;
452 use crate::planner::ExecStrategy;
453 let caps = fun.caps();
454
455 if caps.contains(FnCaps::SHORT_CIRCUIT) || caps.contains(FnCaps::VOLATILE) {
457 return self.eval_function_to_calc(name, args);
458 }
459
460 if matches!(strategy, ExecStrategy::ArgParallel)
464 && caps.contains(FnCaps::PARALLEL_ARGS)
465 {
466 for arg in args {
468 match &arg.node_type {
469 ASTNodeType::Reference { reference, .. } => {
470 let _ = self
471 .context
472 .resolve_range_view(reference, self.current_sheet);
473 }
474 _ => {
475 let _ = self.evaluate_ast(arg);
476 }
477 }
478 }
479 return self.eval_function_to_calc(name, args);
480 }
481
482 return self.eval_function_to_calc(name, args);
484 }
485 self.eval_function_to_calc(name, args)
486 }
487 ASTNodeType::Array(rows) => self.eval_array_literal_to_calc(rows),
488 }
489 }
490
491 fn eval_reference_to_calc(
493 &self,
494 reference: &ReferenceType,
495 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
496 let view = self
497 .context
498 .resolve_range_view(reference, self.current_sheet)?
499 .with_cancel_token(self.context.cancellation_token());
500
501 match reference {
502 ReferenceType::Cell { .. } => {
503 Ok(crate::traits::CalcValue::Scalar(
505 view.as_1x1().unwrap_or(LiteralValue::Empty),
506 ))
507 }
508 _ => Ok(crate::traits::CalcValue::Range(view)),
509 }
510 }
511
512 fn eval_reference(&self, reference: &ReferenceType) -> Result<LiteralValue, ExcelError> {
513 self.eval_reference_to_calc(reference)
514 .map(|cv| cv.into_literal())
515 }
516
517 fn eval_unary(&self, op: &str, expr: &ASTNode) -> Result<LiteralValue, ExcelError> {
519 let v = self.evaluate_ast(expr)?.into_literal();
520 match v {
521 LiteralValue::Array(arr) => {
522 self.map_array(arr, |cell| self.eval_unary_scalar(op, cell))
523 }
524 other => self.eval_unary_scalar(op, other),
525 }
526 }
527
528 fn eval_unary_scalar(&self, op: &str, v: LiteralValue) -> Result<LiteralValue, ExcelError> {
529 match op {
530 "+" => self.apply_number_unary(v, |n| n),
531 "-" => self.apply_number_unary(v, |n| -n),
532 "%" => self.apply_number_unary(v, |n| n / 100.0),
533 _ => {
534 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(format!("Unary op '{op}'")))
535 }
536 }
537 }
538
539 fn apply_number_unary<F>(&self, v: LiteralValue, f: F) -> Result<LiteralValue, ExcelError>
540 where
541 F: Fn(f64) -> f64,
542 {
543 match crate::coercion::to_number_lenient_with_locale(&v, &self.context.locale()) {
544 Ok(n) => match crate::coercion::sanitize_numeric(f(n)) {
545 Ok(n2) => Ok(LiteralValue::Number(n2)),
546 Err(e) => Ok(LiteralValue::Error(e)),
547 },
548 Err(e) => Ok(LiteralValue::Error(e)),
549 }
550 }
551
552 fn eval_binary(
554 &self,
555 op: &str,
556 left: &ASTNode,
557 right: &ASTNode,
558 ) -> Result<LiteralValue, ExcelError> {
559 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
561 let l = self.evaluate_ast(left)?.into_literal();
562 let r = self.evaluate_ast(right)?.into_literal();
563 return self.compare(op, l, r);
564 }
565
566 let l_val = self.evaluate_ast(left)?.into_literal();
567 let r_val = self.evaluate_ast(right)?.into_literal();
568
569 match op {
570 "+" => self.numeric_binary(l_val, r_val, |a, b| a + b),
571 "-" => self.numeric_binary(l_val, r_val, |a, b| a - b),
572 "*" => self.numeric_binary(l_val, r_val, |a, b| a * b),
573 "/" => self.divide(l_val, r_val),
574 "^" => self.power(l_val, r_val),
575 "&" => Ok(LiteralValue::Text(format!(
576 "{}{}",
577 crate::coercion::to_text_invariant(&l_val),
578 crate::coercion::to_text_invariant(&r_val)
579 ))),
580 ":" => {
581 let lref = self.evaluate_ast_as_reference(left)?;
583 let rref = self.evaluate_ast_as_reference(right)?;
584 match crate::reference::combine_references(&lref, &rref) {
585 Ok(_r) => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
586 "Reference produced by ':' cannot be used directly as a value",
587 )),
588 Err(e) => Ok(LiteralValue::Error(e)),
589 }
590 }
591 _ => {
592 Err(ExcelError::new(ExcelErrorKind::NImpl)
593 .with_message(format!("Binary op '{op}'")))
594 }
595 }
596 }
597
598 fn eval_function_to_calc(
600 &self,
601 name: &str,
602 args: &[ASTNode],
603 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
604 if let Some(fun) = self.context.get_function("", name) {
605 let handles: Vec<ArgumentHandle> =
606 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
607 let fctx = DefaultFunctionContext::new_with_sheet(
609 self.context,
610 self.current_cell,
611 self.current_sheet,
612 );
613 fun.dispatch(&handles, &fctx)
614 } else {
615 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
617 ExcelError::new(ExcelErrorKind::Name)
618 .with_message(format!("Unknown function: {name}")),
619 )))
620 }
621 }
622
623 fn eval_function(&self, name: &str, args: &[ASTNode]) -> Result<LiteralValue, ExcelError> {
624 self.eval_function_to_calc(name, args)
625 .map(|cv| cv.into_literal())
626 }
627
628 pub fn function_context(&self, cell_ref: Option<&CellRef>) -> DefaultFunctionContext<'_> {
629 DefaultFunctionContext::new_with_sheet(self.context, cell_ref.cloned(), self.current_sheet)
630 }
631
632 fn eval_array_literal_to_calc(
634 &self,
635 rows: &[Vec<ASTNode>],
636 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
637 let mut out = Vec::with_capacity(rows.len());
638 for row in rows {
639 let mut r = Vec::with_capacity(row.len());
640 for cell in row {
641 r.push(self.evaluate_ast(cell)?.into_literal());
642 }
643 out.push(r);
644 }
645 Ok(crate::traits::CalcValue::Range(
646 crate::engine::range_view::RangeView::from_owned_rows(out, self.context.date_system()),
647 ))
648 }
649
650 fn eval_array_literal(&self, rows: &[Vec<ASTNode>]) -> Result<LiteralValue, ExcelError> {
651 self.eval_array_literal_to_calc(rows)
652 .map(|cv| cv.into_literal())
653 }
654
655 fn numeric_binary<F>(
657 &self,
658 left: LiteralValue,
659 right: LiteralValue,
660 f: F,
661 ) -> Result<LiteralValue, ExcelError>
662 where
663 F: Fn(f64, f64) -> f64 + Copy,
664 {
665 self.broadcast_apply(left, right, |l, r| {
666 let a = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
667 let b = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
668 match (a, b) {
669 (Ok(a), Ok(b)) => match crate::coercion::sanitize_numeric(f(a, b)) {
670 Ok(n2) => Ok(LiteralValue::Number(n2)),
671 Err(e) => Ok(LiteralValue::Error(e)),
672 },
673 (Err(e), _) | (_, Err(e)) => Ok(LiteralValue::Error(e)),
674 }
675 })
676 }
677
678 fn divide(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
679 self.broadcast_apply(left, right, |l, r| {
680 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
681 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
682 let (a, b) = match (ln, rn) {
683 (Ok(a), Ok(b)) => (a, b),
684 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
685 };
686 if b == 0.0 {
687 return Ok(LiteralValue::Error(ExcelError::from_error_string(
688 "#DIV/0!",
689 )));
690 }
691 match crate::coercion::sanitize_numeric(a / b) {
692 Ok(n) => Ok(LiteralValue::Number(n)),
693 Err(e) => Ok(LiteralValue::Error(e)),
694 }
695 })
696 }
697
698 fn power(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
699 self.broadcast_apply(left, right, |l, r| {
700 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
701 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
702 let (a, b) = match (ln, rn) {
703 (Ok(a), Ok(b)) => (a, b),
704 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
705 };
706 if a < 0.0 && b.fract() != 0.0 {
708 return Ok(LiteralValue::Error(ExcelError::new_num()));
709 }
710 match crate::coercion::sanitize_numeric(a.powf(b)) {
711 Ok(n) => Ok(LiteralValue::Number(n)),
712 Err(e) => Ok(LiteralValue::Error(e)),
713 }
714 })
715 }
716
717 fn map_array<F>(&self, arr: Vec<Vec<LiteralValue>>, f: F) -> Result<LiteralValue, ExcelError>
718 where
719 F: Fn(LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
720 {
721 let mut out = Vec::with_capacity(arr.len());
722 for row in arr {
723 let mut new_row = Vec::with_capacity(row.len());
724 for cell in row {
725 new_row.push(match f(cell) {
726 Ok(v) => v,
727 Err(e) => LiteralValue::Error(e),
728 });
729 }
730 out.push(new_row);
731 }
732 Ok(LiteralValue::Array(out))
733 }
734
735 fn combine_arrays<F>(
736 &self,
737 l: Vec<Vec<LiteralValue>>,
738 r: Vec<Vec<LiteralValue>>,
739 f: F,
740 ) -> Result<LiteralValue, ExcelError>
741 where
742 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
743 {
744 let l_shape = (l.len(), l.first().map(|r| r.len()).unwrap_or(0));
746 let r_shape = (r.len(), r.first().map(|r| r.len()).unwrap_or(0));
747 let target = match broadcast_shape(&[l_shape, r_shape]) {
748 Ok(s) => s,
749 Err(e) => return Ok(LiteralValue::Error(e)),
750 };
751
752 let mut out = Vec::with_capacity(target.0);
753 for i in 0..target.0 {
754 let mut row = Vec::with_capacity(target.1);
755 for j in 0..target.1 {
756 let (li, lj) = project_index((i, j), l_shape);
757 let (ri, rj) = project_index((i, j), r_shape);
758 let lv = l
759 .get(li)
760 .and_then(|r| r.get(lj))
761 .cloned()
762 .unwrap_or(LiteralValue::Empty);
763 let rv = r
764 .get(ri)
765 .and_then(|r| r.get(rj))
766 .cloned()
767 .unwrap_or(LiteralValue::Empty);
768 row.push(match f(lv, rv) {
769 Ok(v) => v,
770 Err(e) => LiteralValue::Error(e),
771 });
772 }
773 out.push(row);
774 }
775 Ok(LiteralValue::Array(out))
776 }
777
778 fn broadcast_apply<F>(
779 &self,
780 left: LiteralValue,
781 right: LiteralValue,
782 f: F,
783 ) -> Result<LiteralValue, ExcelError>
784 where
785 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
786 {
787 use LiteralValue::*;
788 match (left, right) {
789 (Array(l), Array(r)) => self.combine_arrays(l, r, f),
790 (Array(arr), v) => {
791 let shape_l = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
792 let shape_r = (1usize, 1usize);
793 let target = match broadcast_shape(&[shape_l, shape_r]) {
794 Ok(s) => s,
795 Err(e) => return Ok(LiteralValue::Error(e)),
796 };
797 let mut out = Vec::with_capacity(target.0);
798 for i in 0..target.0 {
799 let mut row = Vec::with_capacity(target.1);
800 for j in 0..target.1 {
801 let (li, lj) = project_index((i, j), shape_l);
802 let lv = arr
803 .get(li)
804 .and_then(|r| r.get(lj))
805 .cloned()
806 .unwrap_or(LiteralValue::Empty);
807 row.push(match f(lv, v.clone()) {
808 Ok(vv) => vv,
809 Err(e) => LiteralValue::Error(e),
810 });
811 }
812 out.push(row);
813 }
814 Ok(LiteralValue::Array(out))
815 }
816 (v, Array(arr)) => {
817 let shape_l = (1usize, 1usize);
818 let shape_r = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
819 let target = match broadcast_shape(&[shape_l, shape_r]) {
820 Ok(s) => s,
821 Err(e) => return Ok(LiteralValue::Error(e)),
822 };
823 let mut out = Vec::with_capacity(target.0);
824 for i in 0..target.0 {
825 let mut row = Vec::with_capacity(target.1);
826 for j in 0..target.1 {
827 let (ri, rj) = project_index((i, j), shape_r);
828 let rv = arr
829 .get(ri)
830 .and_then(|r| r.get(rj))
831 .cloned()
832 .unwrap_or(LiteralValue::Empty);
833 row.push(match f(v.clone(), rv) {
834 Ok(vv) => vv,
835 Err(e) => LiteralValue::Error(e),
836 });
837 }
838 out.push(row);
839 }
840 Ok(LiteralValue::Array(out))
841 }
842 (l, r) => f(l, r),
843 }
844 }
845
846 fn coerce_number(&self, v: &LiteralValue) -> Result<f64, ExcelError> {
848 coercion::to_number_lenient(v)
849 }
850
851 fn coerce_text(&self, v: &LiteralValue) -> String {
852 coercion::to_text_invariant(v)
853 }
854
855 fn compare(
857 &self,
858 op: &str,
859 left: LiteralValue,
860 right: LiteralValue,
861 ) -> Result<LiteralValue, ExcelError> {
862 use LiteralValue::*;
863 if matches!(left, Error(_)) {
864 return Ok(left);
865 }
866 if matches!(right, Error(_)) {
867 return Ok(right);
868 }
869
870 match (left, right) {
872 (Array(l), Array(r)) => self.combine_arrays(l, r, |a, b| self.compare(op, a, b)),
873 (Array(arr), v) => self.broadcast_apply(Array(arr), v, |a, b| self.compare(op, a, b)),
874 (v, Array(arr)) => self.broadcast_apply(v, Array(arr), |a, b| self.compare(op, a, b)),
875 (l, r) => {
876 let res = match (l, r) {
877 (Number(a), Number(b)) => self.cmp_f64(a, b, op),
878 (Int(a), Number(b)) => self.cmp_f64(a as f64, b, op),
879 (Number(a), Int(b)) => self.cmp_f64(a, b as f64, op),
880 (Boolean(a), Boolean(b)) => {
881 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
882 }
883 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
884 (a, b) => {
885 let an = crate::coercion::to_number_lenient_with_locale(
887 &a,
888 &self.context.locale(),
889 )
890 .ok();
891 let bn = crate::coercion::to_number_lenient_with_locale(
892 &b,
893 &self.context.locale(),
894 )
895 .ok();
896 if let (Some(a), Some(b)) = (an, bn) {
897 self.cmp_f64(a, b, op)
898 } else {
899 self.cmp_text(
900 &crate::coercion::to_text_invariant(&a),
901 &crate::coercion::to_text_invariant(&b),
902 op,
903 )
904 }
905 }
906 };
907 Ok(LiteralValue::Boolean(res))
908 }
909 }
910 }
911
912 fn cmp_f64(&self, a: f64, b: f64, op: &str) -> bool {
913 match op {
914 "=" => a == b,
915 "<>" => a != b,
916 ">" => a > b,
917 "<" => a < b,
918 ">=" => a >= b,
919 "<=" => a <= b,
920 _ => unreachable!(),
921 }
922 }
923 fn cmp_text(&self, a: &str, b: &str, op: &str) -> bool {
924 let loc = self.context.locale();
925 let (a, b) = (loc.fold_case_invariant(a), loc.fold_case_invariant(b));
926 self.cmp_f64(
927 a.cmp(&b) as i32 as f64,
928 0.0,
929 match op {
930 "=" => "=",
931 "<>" => "<>",
932 ">" => ">",
933 "<" => "<",
934 ">=" => ">=",
935 "<=" => "<=",
936 _ => unreachable!(),
937 },
938 )
939 }
940}