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};
9use rustc_hash::FxHashMap;
10use std::sync::Arc;
11
12use crate::engine::arena::{AstNodeData, AstNodeId, DataStore};
13use crate::engine::sheet_registry::SheetRegistry;
14
15#[derive(Clone)]
16pub enum LocalBinding {
17 Value(LiteralValue),
18 Callable(Arc<dyn crate::traits::CustomCallable>),
19}
20
21#[derive(Clone, Default)]
22pub struct LocalEnv {
23 head: Option<Arc<EnvFrame>>,
24}
25
26#[derive(Clone)]
27struct EnvFrame {
28 parent: Option<Arc<EnvFrame>>,
29 bindings: FxHashMap<String, LocalBinding>,
30}
31
32impl LocalEnv {
33 #[inline(always)]
34 pub fn is_empty(&self) -> bool {
35 self.head.is_none()
36 }
37
38 fn norm(name: &str) -> String {
39 name.to_ascii_uppercase()
40 }
41
42 pub fn lookup(&self, name: &str) -> Option<LocalBinding> {
43 self.head.as_ref()?;
44 let key = Self::norm(name);
45 let mut cur = self.head.as_ref().cloned();
46 while let Some(frame) = cur {
47 if let Some(v) = frame.bindings.get(&key) {
48 return Some(v.clone());
49 }
50 cur = frame.parent.clone();
51 }
52 None
53 }
54
55 pub fn with_binding(&self, name: &str, value: LocalBinding) -> Self {
56 let mut bindings = FxHashMap::default();
57 bindings.insert(Self::norm(name), value);
58 Self {
59 head: Some(Arc::new(EnvFrame {
60 parent: self.head.clone(),
61 bindings,
62 })),
63 }
64 }
65}
66
67pub struct Interpreter<'a> {
68 pub context: &'a dyn EvaluationContext,
69 current_sheet: &'a str,
70 current_cell: Option<crate::CellRef>,
71 local_env: LocalEnv,
72}
73
74impl<'a> Interpreter<'a> {
75 pub fn new(context: &'a dyn EvaluationContext, current_sheet: &'a str) -> Self {
76 Self {
77 context,
78 current_sheet,
79 current_cell: None,
80 local_env: LocalEnv::default(),
81 }
82 }
83
84 pub fn new_with_cell(
85 context: &'a dyn EvaluationContext,
86 current_sheet: &'a str,
87 cell: crate::CellRef,
88 ) -> Self {
89 Self {
90 context,
91 current_sheet,
92 current_cell: Some(cell),
93 local_env: LocalEnv::default(),
94 }
95 }
96
97 pub fn current_sheet(&self) -> &'a str {
98 self.current_sheet
99 }
100
101 pub fn local_env(&self) -> &LocalEnv {
102 &self.local_env
103 }
104
105 pub fn with_local_env(&self, env: LocalEnv) -> Self {
106 Self {
107 context: self.context,
108 current_sheet: self.current_sheet,
109 current_cell: self.current_cell,
110 local_env: env,
111 }
112 }
113
114 fn resolve_local_reference(
115 &self,
116 reference: &ReferenceType,
117 ) -> Option<crate::traits::CalcValue<'a>> {
118 if self.local_env.is_empty() {
119 return None;
120 }
121 let name = match reference {
122 ReferenceType::NamedRange(name) => name,
123 _ => return None,
124 };
125 match self.local_env.lookup(name)? {
126 LocalBinding::Value(v) => Some(crate::traits::CalcValue::Scalar(v)),
127 LocalBinding::Callable(c) => Some(crate::traits::CalcValue::Callable(c)),
128 }
129 }
130
131 fn resolve_local_callable(&self, name: &str) -> Option<Arc<dyn crate::traits::CustomCallable>> {
132 if self.local_env.is_empty() {
133 return None;
134 }
135 match self.local_env.lookup(name)? {
136 LocalBinding::Callable(c) => Some(c),
137 LocalBinding::Value(_) => None,
138 }
139 }
140
141 pub fn resolve_local_name(&self, name: &str) -> Option<LocalBinding> {
142 self.local_env.lookup(name)
143 }
144
145 pub fn resolve_range_view<'c>(
146 &'c self,
147 reference: &ReferenceType,
148 current_sheet: &str,
149 ) -> Result<crate::engine::range_view::RangeView<'c>, ExcelError> {
150 self.context.resolve_range_view(reference, current_sheet)
151 }
152
153 pub fn evaluate_ast_as_reference(&self, node: &ASTNode) -> Result<ReferenceType, ExcelError> {
158 match &node.node_type {
159 ASTNodeType::Reference { reference, .. } => Ok(reference.clone()),
160 ASTNodeType::Function { name, args } => {
161 if let Some(fun) = self.context.get_function("", name) {
162 let handles: Vec<ArgumentHandle> =
164 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
165 let fctx = DefaultFunctionContext::new_with_sheet(
166 self.context,
167 None,
168 self.current_sheet,
169 );
170 if let Some(res) = fun.eval_reference(&handles, &fctx) {
171 res
172 } else {
173 Err(ExcelError::new(ExcelErrorKind::Ref)
174 .with_message("Function does not return a reference"))
175 }
176 } else {
177 Err(ExcelError::new(ExcelErrorKind::Name)
178 .with_message(format!("Unknown function: {name}")))
179 }
180 }
181 ASTNodeType::BinaryOp { op, left, right } if op == ":" => {
182 let lref = self.evaluate_ast_as_reference(left)?;
183 let rref = self.evaluate_ast_as_reference(right)?;
184 crate::reference::combine_references(&lref, &rref)
185 }
186 ASTNodeType::Array(_)
187 | ASTNodeType::UnaryOp { .. }
188 | ASTNodeType::BinaryOp { .. }
189 | ASTNodeType::Literal(_) => Err(ExcelError::new(ExcelErrorKind::Ref)
190 .with_message("Expression cannot be used as a reference")),
191 }
192 }
193
194 pub(crate) fn evaluate_arena_ast_as_reference(
195 &self,
196 node_id: AstNodeId,
197 data_store: &DataStore,
198 sheet_registry: &SheetRegistry,
199 ) -> Result<ReferenceType, ExcelError> {
200 let node = data_store.get_node(node_id).ok_or_else(|| {
201 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
202 })?;
203
204 match node {
205 AstNodeData::Reference { ref_type, .. } => {
206 Ok(data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry))
207 }
208 AstNodeData::Function { name_id, .. } => {
209 let name = data_store.resolve_ast_string(*name_id);
210 let fun = self.context.get_function("", name).ok_or_else(|| {
211 ExcelError::new(ExcelErrorKind::Name)
212 .with_message(format!("Unknown function: {name}"))
213 })?;
214
215 let args = data_store.get_args(node_id).ok_or_else(|| {
216 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
217 })?;
218
219 let handles: Vec<ArgumentHandle> = args
220 .iter()
221 .copied()
222 .map(|arg_id| {
223 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
224 })
225 .collect();
226
227 let fctx =
228 DefaultFunctionContext::new_with_sheet(self.context, None, self.current_sheet);
229
230 fun.eval_reference(&handles, &fctx).ok_or_else(|| {
231 ExcelError::new(ExcelErrorKind::Ref)
232 .with_message("Function does not return a reference")
233 })?
234 }
235 AstNodeData::BinaryOp {
236 op_id,
237 left_id,
238 right_id,
239 } => {
240 let op = data_store.resolve_ast_string(*op_id);
241 if op != ":" {
242 return Err(ExcelError::new(ExcelErrorKind::Ref)
243 .with_message("Expression cannot be used as a reference"));
244 }
245 let lref =
246 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
247 let rref =
248 self.evaluate_arena_ast_as_reference(*right_id, data_store, sheet_registry)?;
249 crate::reference::combine_references(&lref, &rref)
250 }
251 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
252 .with_message("Expression cannot be used as a reference")),
253 }
254 }
255
256 pub fn evaluate_ast(&self, node: &ASTNode) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
258 self.evaluate_ast_uncached(node)
259 }
260
261 pub(crate) fn evaluate_arena_ast(
262 &self,
263 node_id: AstNodeId,
264 data_store: &DataStore,
265 sheet_registry: &SheetRegistry,
266 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
267 let node = data_store.get_node(node_id).ok_or_else(|| {
268 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
269 })?;
270
271 match node {
272 AstNodeData::Literal(vref) => Ok(crate::traits::CalcValue::Scalar(
273 data_store.retrieve_value(*vref),
274 )),
275 AstNodeData::Reference { ref_type, .. } => {
276 let reference =
277 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
278 if let Some(local) = self.resolve_local_reference(&reference) {
279 return Ok(local);
280 }
281 self.eval_reference_to_calc(&reference)
282 }
283 AstNodeData::UnaryOp { op_id, expr_id } => {
284 let expr = self.evaluate_arena_ast(*expr_id, data_store, sheet_registry)?;
285
286 let op = data_store.resolve_ast_string(*op_id);
287 if op == "@" {
288 if let Some(AstNodeData::Reference { ref_type, .. }) =
291 data_store.get_node(*expr_id)
292 {
293 let reference = data_store
294 .reconstruct_reference_type_for_eval(ref_type, sheet_registry);
295 let v = self.implicit_intersection_from_reference(&reference);
296 return Ok(crate::traits::CalcValue::Scalar(v));
297 }
298
299 let v = self.eval_implicit_intersection_calc(expr);
300 return Ok(crate::traits::CalcValue::Scalar(v));
301 }
302 let v = expr.into_literal();
304 match v {
305 LiteralValue::Array(arr) => self
306 .map_array(arr, |cell| self.eval_unary_scalar(op, cell))
307 .map(crate::traits::CalcValue::Scalar),
308 other => self
309 .eval_unary_scalar(op, other)
310 .map(crate::traits::CalcValue::Scalar),
311 }
312 }
313 AstNodeData::BinaryOp {
314 op_id,
315 left_id,
316 right_id,
317 } => {
318 let op = data_store.resolve_ast_string(*op_id);
319 if op == ":" {
320 let lref =
321 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
322 let rref = self.evaluate_arena_ast_as_reference(
323 *right_id,
324 data_store,
325 sheet_registry,
326 )?;
327 return match crate::reference::combine_references(&lref, &rref) {
328 Ok(_r) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
329 ExcelError::new(ExcelErrorKind::Ref).with_message(
330 "Reference produced by ':' cannot be used directly as a value",
331 ),
332 ))),
333 Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
334 };
335 }
336
337 let left = self
338 .evaluate_arena_ast(*left_id, data_store, sheet_registry)?
339 .into_literal();
340 let right = self
341 .evaluate_arena_ast(*right_id, data_store, sheet_registry)?
342 .into_literal();
343
344 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
345 return self
346 .compare(op, left, right)
347 .map(crate::traits::CalcValue::Scalar);
348 }
349
350 match op {
351 "+" => self
352 .add_sub_date_aware('+', left, right)
353 .map(crate::traits::CalcValue::Scalar),
354 "-" => self
355 .add_sub_date_aware('-', left, right)
356 .map(crate::traits::CalcValue::Scalar),
357 "*" => self
358 .numeric_binary(left, right, |a, b| a * b)
359 .map(crate::traits::CalcValue::Scalar),
360 "/" => self
361 .divide(left, right)
362 .map(crate::traits::CalcValue::Scalar),
363 "^" => self
364 .power(left, right)
365 .map(crate::traits::CalcValue::Scalar),
366 "&" => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
367 format!(
368 "{}{}",
369 crate::coercion::to_text_invariant(&left),
370 crate::coercion::to_text_invariant(&right)
371 ),
372 ))),
373 _ => Err(ExcelError::new(ExcelErrorKind::NImpl)
374 .with_message(format!("Binary op '{op}'"))),
375 }
376 }
377 AstNodeData::Array { .. } => {
378 let (rows, cols, elements) =
379 data_store.get_array_elems(node_id).ok_or_else(|| {
380 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
381 })?;
382
383 let rows_usize = rows as usize;
384 let cols_usize = cols as usize;
385 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows_usize);
386 for r in 0..rows_usize {
387 let mut row = Vec::with_capacity(cols_usize);
388 for c in 0..cols_usize {
389 let idx = r * cols_usize + c;
390 if let Some(&elem_id) = elements.get(idx) {
391 row.push(
392 self.evaluate_arena_ast(elem_id, data_store, sheet_registry)?
393 .into_literal(),
394 );
395 }
396 }
397 out.push(row);
398 }
399
400 Ok(crate::traits::CalcValue::Range(
401 crate::engine::range_view::RangeView::from_owned_rows(
402 out,
403 self.context.date_system(),
404 ),
405 ))
406 }
407 AstNodeData::Function { name_id, .. } => {
408 let name = data_store.resolve_ast_string(*name_id);
409 let args = data_store.get_args(node_id).ok_or_else(|| {
410 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
411 })?;
412
413 if let Some(fun) = self.context.get_function("", name) {
414 let handles: Vec<ArgumentHandle> = args
415 .iter()
416 .copied()
417 .map(|arg_id| {
418 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
419 })
420 .collect();
421
422 let fctx = DefaultFunctionContext::new_with_sheet(
423 self.context,
424 self.current_cell,
425 self.current_sheet,
426 );
427
428 return fun.dispatch(&handles, &fctx);
429 }
430
431 if let Some(callable) = self.resolve_local_callable(name) {
432 let mut eval_args = Vec::with_capacity(args.len());
433 for arg_id in args {
434 eval_args.push(
435 self.evaluate_arena_ast(*arg_id, data_store, sheet_registry)?
436 .into_literal(),
437 );
438 }
439 return callable.invoke(self, &eval_args);
440 }
441
442 Err(ExcelError::new(ExcelErrorKind::Name)
443 .with_message(format!("Unknown function: {name}")))
444 }
445 }
446 }
447
448 fn evaluate_ast_uncached(
449 &self,
450 node: &ASTNode,
451 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
452 let current_sheet = self.current_sheet.to_string();
456 let range_probe = |reference: &ReferenceType| -> Option<(u32, u32)> {
457 use formualizer_parse::parser::ReferenceType as RT;
459 match reference {
460 RT::Range {
461 sheet,
462 start_row,
463 start_col,
464 end_row,
465 end_col,
466 ..
467 } => {
468 let sheet_name = sheet.as_deref().unwrap_or(¤t_sheet);
469 let mut sr = *start_row;
471 let mut sc = *start_col;
472 let mut er = *end_row;
473 let mut ec = *end_col;
474
475 if sr.is_none() && er.is_none() {
477 let scv = sc.unwrap_or(1);
479 let ecv = ec.unwrap_or(scv);
480 sr = Some(1);
481 if let Some((_, max_r)) =
482 self.context.used_rows_for_columns(sheet_name, scv, ecv)
483 {
484 er = Some(max_r);
485 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
486 er = Some(max_rows);
487 }
488 }
489
490 if sc.is_none() && ec.is_none() {
492 let srv = sr.unwrap_or(1);
494 let erv = er.unwrap_or(srv);
495 sc = Some(1);
496 if let Some((_, max_c)) =
497 self.context.used_cols_for_rows(sheet_name, srv, erv)
498 {
499 ec = Some(max_c);
500 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
501 ec = Some(max_cols);
502 }
503 }
504
505 if sr.is_some() && er.is_none() {
507 let scv = sc.unwrap_or(1);
508 let ecv = ec.unwrap_or(scv);
509 if let Some((_, max_r)) =
510 self.context.used_rows_for_columns(sheet_name, scv, ecv)
511 {
512 er = Some(max_r);
513 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
514 er = Some(max_rows);
515 }
516 }
517 if er.is_some() && sr.is_none() {
518 sr = Some(1);
520 }
521 if sc.is_some() && ec.is_none() {
522 let srv = sr.unwrap_or(1);
523 let erv = er.unwrap_or(srv);
524 if let Some((_, max_c)) =
525 self.context.used_cols_for_rows(sheet_name, srv, erv)
526 {
527 ec = Some(max_c);
528 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
529 ec = Some(max_cols);
530 }
531 }
532 if ec.is_some() && sc.is_none() {
533 sc = Some(1);
535 }
536
537 let sr = sr.unwrap_or(1);
538 let sc = sc.unwrap_or(1);
539 let er = er.unwrap_or(sr.saturating_sub(1));
540 let ec = ec.unwrap_or(sc.saturating_sub(1));
541 if er < sr || ec < sc {
542 return Some((0, 0));
543 }
544 Some((er.saturating_sub(sr) + 1, ec.saturating_sub(sc) + 1))
545 }
546 RT::Cell { .. } => Some((1, 1)),
547 _ => None,
548 }
549 };
550 let fn_lookup = |ns: &str, name: &str| self.context.get_function(ns, name);
551
552 let mut planner = crate::planner::Planner::new(crate::planner::PlanConfig::default())
553 .with_range_probe(&range_probe)
554 .with_function_lookup(&fn_lookup);
555 let plan = planner.plan(node);
556 self.eval_with_plan(node, &plan.root)
557 }
558
559 fn eval_with_plan(
560 &self,
561 node: &ASTNode,
562 plan_node: &crate::planner::PlanNode,
563 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
564 match &node.node_type {
565 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
566 ASTNodeType::Reference { reference, .. } => {
567 if let Some(local) = self.resolve_local_reference(reference) {
568 Ok(local)
569 } else {
570 self.eval_reference_to_calc(reference)
571 }
572 }
573 ASTNodeType::UnaryOp { op, expr } => {
574 self.eval_unary(op, expr)
577 .map(crate::traits::CalcValue::Scalar)
578 }
579 ASTNodeType::BinaryOp { op, left, right } => self
580 .eval_binary(op, left, right)
581 .map(crate::traits::CalcValue::Scalar),
582 ASTNodeType::Function { name, args } => {
583 let strategy = plan_node.strategy;
584 if let Some(fun) = self.context.get_function("", name) {
585 use crate::function::FnCaps;
586 use crate::planner::ExecStrategy;
587 let caps = fun.caps();
588
589 if caps.contains(FnCaps::SHORT_CIRCUIT) || caps.contains(FnCaps::VOLATILE) {
591 return self.eval_function_to_calc(name, args);
592 }
593
594 if matches!(strategy, ExecStrategy::ArgParallel)
598 && caps.contains(FnCaps::PARALLEL_ARGS)
599 {
600 for arg in args {
602 match &arg.node_type {
603 ASTNodeType::Reference { reference, .. } => {
604 let _ = self
605 .context
606 .resolve_range_view(reference, self.current_sheet);
607 }
608 _ => {
609 let _ = self.evaluate_ast(arg);
610 }
611 }
612 }
613 return self.eval_function_to_calc(name, args);
614 }
615
616 return self.eval_function_to_calc(name, args);
618 }
619 self.eval_function_to_calc(name, args)
620 }
621 ASTNodeType::Array(rows) => self.eval_array_literal_to_calc(rows),
622 }
623 }
624
625 fn eval_reference_to_calc(
627 &self,
628 reference: &ReferenceType,
629 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
630 let view = self
631 .context
632 .resolve_range_view(reference, self.current_sheet)?
633 .with_cancel_token(self.context.cancellation_token());
634
635 match reference {
636 ReferenceType::Cell { .. } => {
637 Ok(crate::traits::CalcValue::Scalar(
639 view.as_1x1().unwrap_or(LiteralValue::Empty),
640 ))
641 }
642 _ => Ok(crate::traits::CalcValue::Range(view)),
643 }
644 }
645
646 fn eval_reference(&self, reference: &ReferenceType) -> Result<LiteralValue, ExcelError> {
647 self.eval_reference_to_calc(reference)
648 .map(|cv| cv.into_literal())
649 }
650
651 fn eval_unary(&self, op: &str, expr: &ASTNode) -> Result<LiteralValue, ExcelError> {
653 if op == "@" {
654 if let ASTNodeType::Reference { reference, .. } = &expr.node_type {
655 return Ok(self.implicit_intersection_from_reference(reference));
656 }
657
658 let cv = self.evaluate_ast(expr)?;
659 return Ok(self.eval_implicit_intersection_calc(cv));
660 }
661
662 let v = self.evaluate_ast(expr)?.into_literal();
663 match v {
664 LiteralValue::Array(arr) => {
665 self.map_array(arr, |cell| self.eval_unary_scalar(op, cell))
666 }
667 other => self.eval_unary_scalar(op, other),
668 }
669 }
670
671 fn eval_unary_scalar(&self, op: &str, v: LiteralValue) -> Result<LiteralValue, ExcelError> {
672 match op {
673 "+" => self.apply_number_unary(v, |n| n),
674 "-" => self.apply_number_unary(v, |n| -n),
675 "%" => self.apply_number_unary(v, |n| n / 100.0),
676 _ => {
677 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(format!("Unary op '{op}'")))
678 }
679 }
680 }
681
682 fn eval_implicit_intersection_calc(&self, cv: crate::traits::CalcValue<'a>) -> LiteralValue {
683 let (cur_r0, cur_c0) = match self.current_cell {
684 Some(cell) => (cell.coord.row() as usize, cell.coord.col() as usize),
685 None => (0usize, 0usize),
686 };
687
688 match cv {
689 crate::traits::CalcValue::Scalar(v) => match v {
690 LiteralValue::Array(arr) => {
691 if arr.is_empty() || arr.first().map(|r| r.is_empty()).unwrap_or(true) {
692 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
693 }
694 arr[0][0].clone()
695 }
696 other => other,
697 },
698 crate::traits::CalcValue::Range(rv) => {
699 if rv.is_empty() {
700 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
701 }
702
703 if rv.sheet_name() == "__tmp" {
708 return rv.get_cell(0, 0);
709 }
710
711 if let Some(v) = rv.as_1x1() {
712 return v;
713 }
714
715 let (rows, cols) = rv.dims();
716 let sr = rv.start_row();
717 let sc = rv.start_col();
718 let er = rv.end_row();
719 let ec = rv.end_col();
720
721 if cols == 1 {
726 if cur_r0 < sr || cur_r0 > er {
727 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
728 }
729 let rel_r = cur_r0 - sr;
730 return rv.get_cell(rel_r, 0);
731 }
732
733 if rows == 1 {
734 if cur_c0 < sc || cur_c0 > ec {
735 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
736 }
737 let rel_c = cur_c0 - sc;
738 return rv.get_cell(0, rel_c);
739 }
740
741 if cur_r0 < sr || cur_r0 > er || cur_c0 < sc || cur_c0 > ec {
742 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
743 }
744 let rel_r = cur_r0 - sr;
745 let rel_c = cur_c0 - sc;
746 rv.get_cell(rel_r, rel_c)
747 }
748 crate::traits::CalcValue::Callable(_) => LiteralValue::Error(
749 ExcelError::new(ExcelErrorKind::Calc).with_message("LAMBDA value must be invoked"),
750 ),
751 }
752 }
753
754 fn implicit_intersection_from_reference(&self, reference: &ReferenceType) -> LiteralValue {
755 let (cur_r1, cur_c1) = match self.current_cell {
756 Some(cell) => (
757 cell.coord.row().saturating_add(1),
758 cell.coord.col().saturating_add(1),
759 ),
760 None => (1u32, 1u32),
761 };
762
763 match reference {
764 ReferenceType::Cell {
765 sheet, row, col, ..
766 } => {
767 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
768 match self
769 .context
770 .resolve_cell_reference(Some(sheet_name), *row, *col)
771 {
772 Ok(v) => v,
773 Err(e) => LiteralValue::Error(e),
774 }
775 }
776 ReferenceType::Range {
777 sheet,
778 start_row,
779 start_col,
780 end_row,
781 end_col,
782 ..
783 } => {
784 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
785
786 let (sr, sc, er, ec) = match (start_row, start_col, end_row, end_col) {
787 (Some(sr), Some(sc), Some(er), Some(ec)) => (*sr, *sc, *er, *ec),
788 _ => {
789 let cv = match self.eval_reference_to_calc(reference) {
792 Ok(cv) => cv,
793 Err(e) => return LiteralValue::Error(e),
794 };
795 return self.eval_implicit_intersection_calc(cv);
796 }
797 };
798
799 let (mut sr, mut er) = (sr, er);
801 let (mut sc, mut ec) = (sc, ec);
802 if sr > er {
803 std::mem::swap(&mut sr, &mut er);
804 }
805 if sc > ec {
806 std::mem::swap(&mut sc, &mut ec);
807 }
808
809 let pick = if sc == ec {
810 if cur_r1 < sr || cur_r1 > er {
812 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
813 }
814 (cur_r1, sc)
815 } else if sr == er {
816 if cur_c1 < sc || cur_c1 > ec {
818 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
819 }
820 (sr, cur_c1)
821 } else {
822 if cur_r1 < sr || cur_r1 > er || cur_c1 < sc || cur_c1 > ec {
824 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
825 }
826 (cur_r1, cur_c1)
827 };
828
829 match self
830 .context
831 .resolve_cell_reference(Some(sheet_name), pick.0, pick.1)
832 {
833 Ok(v) => v,
834 Err(e) => LiteralValue::Error(e),
835 }
836 }
837 other => {
839 let cv = match self.eval_reference_to_calc(other) {
840 Ok(cv) => cv,
841 Err(e) => return LiteralValue::Error(e),
842 };
843 self.eval_implicit_intersection_calc(cv)
844 }
845 }
846 }
847
848 fn apply_number_unary<F>(&self, v: LiteralValue, f: F) -> Result<LiteralValue, ExcelError>
849 where
850 F: Fn(f64) -> f64,
851 {
852 match crate::coercion::to_number_lenient_with_locale(&v, &self.context.locale()) {
853 Ok(n) => match crate::coercion::sanitize_numeric(f(n)) {
854 Ok(n2) => Ok(LiteralValue::Number(n2)),
855 Err(e) => Ok(LiteralValue::Error(e)),
856 },
857 Err(e) => Ok(LiteralValue::Error(e)),
858 }
859 }
860
861 fn eval_binary(
863 &self,
864 op: &str,
865 left: &ASTNode,
866 right: &ASTNode,
867 ) -> Result<LiteralValue, ExcelError> {
868 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
870 let l = self.evaluate_ast(left)?.into_literal();
871 let r = self.evaluate_ast(right)?.into_literal();
872 return self.compare(op, l, r);
873 }
874
875 let l_val = self.evaluate_ast(left)?.into_literal();
876 let r_val = self.evaluate_ast(right)?.into_literal();
877
878 match op {
879 "+" => self.add_sub_date_aware('+', l_val, r_val),
880 "-" => self.add_sub_date_aware('-', l_val, r_val),
881 "*" => self.numeric_binary(l_val, r_val, |a, b| a * b),
882 "/" => self.divide(l_val, r_val),
883 "^" => self.power(l_val, r_val),
884 "&" => Ok(LiteralValue::Text(format!(
885 "{}{}",
886 crate::coercion::to_text_invariant(&l_val),
887 crate::coercion::to_text_invariant(&r_val)
888 ))),
889 ":" => {
890 let lref = self.evaluate_ast_as_reference(left)?;
892 let rref = self.evaluate_ast_as_reference(right)?;
893 match crate::reference::combine_references(&lref, &rref) {
894 Ok(_r) => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
895 "Reference produced by ':' cannot be used directly as a value",
896 )),
897 Err(e) => Ok(LiteralValue::Error(e)),
898 }
899 }
900 _ => {
901 Err(ExcelError::new(ExcelErrorKind::NImpl)
902 .with_message(format!("Binary op '{op}'")))
903 }
904 }
905 }
906
907 fn add_sub_date_aware(
908 &self,
909 op: char,
910 left: LiteralValue,
911 right: LiteralValue,
912 ) -> Result<LiteralValue, ExcelError> {
913 debug_assert!(op == '+' || op == '-');
914
915 self.broadcast_apply(left, right, |l, r| {
916 use LiteralValue::*;
917
918 let date_system = self.context.date_system();
919
920 let date_like_serial = |v: &LiteralValue| -> Option<f64> {
921 match v {
922 Date(d) => Some(crate::builtins::datetime::date_to_serial_for(
923 date_system,
924 d,
925 )),
926 DateTime(dt) => Some(crate::builtins::datetime::datetime_to_serial_for(
927 date_system,
928 dt,
929 )),
930 _ => None,
931 }
932 };
933
934 let to_num = |v: &LiteralValue| -> Result<f64, ExcelError> {
935 crate::coercion::to_number_lenient_with_locale(v, &self.context.locale())
936 };
937
938 let serial_to_literal = |serial: f64| -> LiteralValue {
939 match crate::coercion::sanitize_numeric(serial) {
940 Ok(serial) => {
941 match crate::builtins::datetime::serial_to_datetime_for(date_system, serial)
942 {
943 Ok(dt) => {
944 if dt.time() == chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap() {
945 Date(dt.date())
946 } else {
947 DateTime(dt)
948 }
949 }
950 Err(e) => Error(e),
951 }
952 }
953 Err(e) => Error(e),
954 }
955 };
956
957 if let Some(ls) = date_like_serial(&l) {
959 match op {
960 '+' => {
961 let rn = to_num(&r)?;
962 return Ok(serial_to_literal(ls + rn));
963 }
964 '-' => {
965 if let Some(rs) = date_like_serial(&r) {
967 return Ok(Number(ls - rs));
968 }
969 let rn = to_num(&r)?;
970 return Ok(serial_to_literal(ls - rn));
971 }
972 _ => unreachable!(),
973 }
974 }
975
976 if op == '+'
978 && let Some(rs) = date_like_serial(&r)
979 {
980 let ln = to_num(&l)?;
981 return Ok(serial_to_literal(ln + rs));
982 }
983
984 self.numeric_binary(l, r, |a, b| if op == '+' { a + b } else { a - b })
986 })
987 }
988
989 fn eval_function_to_calc(
991 &self,
992 name: &str,
993 args: &[ASTNode],
994 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
995 if let Some(fun) = self.context.get_function("", name) {
996 let handles: Vec<ArgumentHandle> =
997 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
998 let fctx = DefaultFunctionContext::new_with_sheet(
1000 self.context,
1001 self.current_cell,
1002 self.current_sheet,
1003 );
1004 return fun.dispatch(&handles, &fctx);
1005 }
1006
1007 if let Some(callable) = self.resolve_local_callable(name) {
1008 let mut eval_args = Vec::with_capacity(args.len());
1009 for arg in args {
1010 eval_args.push(self.evaluate_ast(arg)?.into_literal());
1011 }
1012 return callable.invoke(self, &eval_args);
1013 }
1014
1015 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1017 ExcelError::new(ExcelErrorKind::Name).with_message(format!("Unknown function: {name}")),
1018 )))
1019 }
1020
1021 fn eval_function(&self, name: &str, args: &[ASTNode]) -> Result<LiteralValue, ExcelError> {
1022 self.eval_function_to_calc(name, args)
1023 .map(|cv| cv.into_literal())
1024 }
1025
1026 pub fn function_context(&self, cell_ref: Option<&CellRef>) -> DefaultFunctionContext<'_> {
1027 DefaultFunctionContext::new_with_sheet(self.context, cell_ref.cloned(), self.current_sheet)
1028 }
1029
1030 fn eval_array_literal_to_calc(
1032 &self,
1033 rows: &[Vec<ASTNode>],
1034 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1035 let mut out = Vec::with_capacity(rows.len());
1036 for row in rows {
1037 let mut r = Vec::with_capacity(row.len());
1038 for cell in row {
1039 r.push(self.evaluate_ast(cell)?.into_literal());
1040 }
1041 out.push(r);
1042 }
1043 Ok(crate::traits::CalcValue::Range(
1044 crate::engine::range_view::RangeView::from_owned_rows(out, self.context.date_system()),
1045 ))
1046 }
1047
1048 fn eval_array_literal(&self, rows: &[Vec<ASTNode>]) -> Result<LiteralValue, ExcelError> {
1049 self.eval_array_literal_to_calc(rows)
1050 .map(|cv| cv.into_literal())
1051 }
1052
1053 fn numeric_binary<F>(
1055 &self,
1056 left: LiteralValue,
1057 right: LiteralValue,
1058 f: F,
1059 ) -> Result<LiteralValue, ExcelError>
1060 where
1061 F: Fn(f64, f64) -> f64 + Copy,
1062 {
1063 self.broadcast_apply(left, right, |l, r| {
1064 let a = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1065 let b = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1066 match (a, b) {
1067 (Ok(a), Ok(b)) => match crate::coercion::sanitize_numeric(f(a, b)) {
1068 Ok(n2) => Ok(LiteralValue::Number(n2)),
1069 Err(e) => Ok(LiteralValue::Error(e)),
1070 },
1071 (Err(e), _) | (_, Err(e)) => Ok(LiteralValue::Error(e)),
1072 }
1073 })
1074 }
1075
1076 fn divide(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
1077 self.broadcast_apply(left, right, |l, r| {
1078 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1079 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1080 let (a, b) = match (ln, rn) {
1081 (Ok(a), Ok(b)) => (a, b),
1082 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
1083 };
1084 if b == 0.0 {
1085 return Ok(LiteralValue::Error(ExcelError::from_error_string(
1086 "#DIV/0!",
1087 )));
1088 }
1089 match crate::coercion::sanitize_numeric(a / b) {
1090 Ok(n) => Ok(LiteralValue::Number(n)),
1091 Err(e) => Ok(LiteralValue::Error(e)),
1092 }
1093 })
1094 }
1095
1096 fn power(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
1097 self.broadcast_apply(left, right, |l, r| {
1098 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1099 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1100 let (a, b) = match (ln, rn) {
1101 (Ok(a), Ok(b)) => (a, b),
1102 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
1103 };
1104 if a < 0.0 && b.fract() != 0.0 {
1106 return Ok(LiteralValue::Error(ExcelError::new_num()));
1107 }
1108 match crate::coercion::sanitize_numeric(a.powf(b)) {
1109 Ok(n) => Ok(LiteralValue::Number(n)),
1110 Err(e) => Ok(LiteralValue::Error(e)),
1111 }
1112 })
1113 }
1114
1115 fn map_array<F>(&self, arr: Vec<Vec<LiteralValue>>, f: F) -> Result<LiteralValue, ExcelError>
1116 where
1117 F: Fn(LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1118 {
1119 let mut out = Vec::with_capacity(arr.len());
1120 for row in arr {
1121 let mut new_row = Vec::with_capacity(row.len());
1122 for cell in row {
1123 new_row.push(match f(cell) {
1124 Ok(v) => v,
1125 Err(e) => LiteralValue::Error(e),
1126 });
1127 }
1128 out.push(new_row);
1129 }
1130 Ok(LiteralValue::Array(out))
1131 }
1132
1133 fn combine_arrays<F>(
1134 &self,
1135 l: Vec<Vec<LiteralValue>>,
1136 r: Vec<Vec<LiteralValue>>,
1137 f: F,
1138 ) -> Result<LiteralValue, ExcelError>
1139 where
1140 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1141 {
1142 let l_shape = (l.len(), l.first().map(|r| r.len()).unwrap_or(0));
1144 let r_shape = (r.len(), r.first().map(|r| r.len()).unwrap_or(0));
1145 let target = match broadcast_shape(&[l_shape, r_shape]) {
1146 Ok(s) => s,
1147 Err(e) => return Ok(LiteralValue::Error(e)),
1148 };
1149
1150 let mut out = Vec::with_capacity(target.0);
1151 for i in 0..target.0 {
1152 let mut row = Vec::with_capacity(target.1);
1153 for j in 0..target.1 {
1154 let (li, lj) = project_index((i, j), l_shape);
1155 let (ri, rj) = project_index((i, j), r_shape);
1156 let lv = l
1157 .get(li)
1158 .and_then(|r| r.get(lj))
1159 .cloned()
1160 .unwrap_or(LiteralValue::Empty);
1161 let rv = r
1162 .get(ri)
1163 .and_then(|r| r.get(rj))
1164 .cloned()
1165 .unwrap_or(LiteralValue::Empty);
1166 row.push(match f(lv, rv) {
1167 Ok(v) => v,
1168 Err(e) => LiteralValue::Error(e),
1169 });
1170 }
1171 out.push(row);
1172 }
1173 Ok(LiteralValue::Array(out))
1174 }
1175
1176 fn broadcast_apply<F>(
1177 &self,
1178 left: LiteralValue,
1179 right: LiteralValue,
1180 f: F,
1181 ) -> Result<LiteralValue, ExcelError>
1182 where
1183 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1184 {
1185 use LiteralValue::*;
1186 match (left, right) {
1187 (Array(l), Array(r)) => self.combine_arrays(l, r, f),
1188 (Array(arr), v) => {
1189 let shape_l = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
1190 let shape_r = (1usize, 1usize);
1191 let target = match broadcast_shape(&[shape_l, shape_r]) {
1192 Ok(s) => s,
1193 Err(e) => return Ok(LiteralValue::Error(e)),
1194 };
1195 let mut out = Vec::with_capacity(target.0);
1196 for i in 0..target.0 {
1197 let mut row = Vec::with_capacity(target.1);
1198 for j in 0..target.1 {
1199 let (li, lj) = project_index((i, j), shape_l);
1200 let lv = arr
1201 .get(li)
1202 .and_then(|r| r.get(lj))
1203 .cloned()
1204 .unwrap_or(LiteralValue::Empty);
1205 row.push(match f(lv, v.clone()) {
1206 Ok(vv) => vv,
1207 Err(e) => LiteralValue::Error(e),
1208 });
1209 }
1210 out.push(row);
1211 }
1212 Ok(LiteralValue::Array(out))
1213 }
1214 (v, Array(arr)) => {
1215 let shape_l = (1usize, 1usize);
1216 let shape_r = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
1217 let target = match broadcast_shape(&[shape_l, shape_r]) {
1218 Ok(s) => s,
1219 Err(e) => return Ok(LiteralValue::Error(e)),
1220 };
1221 let mut out = Vec::with_capacity(target.0);
1222 for i in 0..target.0 {
1223 let mut row = Vec::with_capacity(target.1);
1224 for j in 0..target.1 {
1225 let (ri, rj) = project_index((i, j), shape_r);
1226 let rv = arr
1227 .get(ri)
1228 .and_then(|r| r.get(rj))
1229 .cloned()
1230 .unwrap_or(LiteralValue::Empty);
1231 row.push(match f(v.clone(), rv) {
1232 Ok(vv) => vv,
1233 Err(e) => LiteralValue::Error(e),
1234 });
1235 }
1236 out.push(row);
1237 }
1238 Ok(LiteralValue::Array(out))
1239 }
1240 (l, r) => f(l, r),
1241 }
1242 }
1243
1244 fn coerce_number(&self, v: &LiteralValue) -> Result<f64, ExcelError> {
1246 coercion::to_number_lenient(v)
1247 }
1248
1249 fn coerce_text(&self, v: &LiteralValue) -> String {
1250 coercion::to_text_invariant(v)
1251 }
1252
1253 fn compare(
1255 &self,
1256 op: &str,
1257 left: LiteralValue,
1258 right: LiteralValue,
1259 ) -> Result<LiteralValue, ExcelError> {
1260 use LiteralValue::*;
1261 if matches!(left, Error(_)) {
1262 return Ok(left);
1263 }
1264 if matches!(right, Error(_)) {
1265 return Ok(right);
1266 }
1267
1268 match (left, right) {
1270 (Array(l), Array(r)) => self.combine_arrays(l, r, |a, b| self.compare(op, a, b)),
1271 (Array(arr), v) => self.broadcast_apply(Array(arr), v, |a, b| self.compare(op, a, b)),
1272 (v, Array(arr)) => self.broadcast_apply(v, Array(arr), |a, b| self.compare(op, a, b)),
1273 (l, r) => {
1274 let res = match (l, r) {
1275 (Number(a), Number(b)) => self.cmp_f64(a, b, op),
1276 (Int(a), Number(b)) => self.cmp_f64(a as f64, b, op),
1277 (Number(a), Int(b)) => self.cmp_f64(a, b as f64, op),
1278 (Boolean(a), Boolean(b)) => {
1279 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
1280 }
1281 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
1282 (a, b) => {
1283 let an = crate::coercion::to_number_lenient_with_locale(
1285 &a,
1286 &self.context.locale(),
1287 )
1288 .ok();
1289 let bn = crate::coercion::to_number_lenient_with_locale(
1290 &b,
1291 &self.context.locale(),
1292 )
1293 .ok();
1294 if let (Some(a), Some(b)) = (an, bn) {
1295 self.cmp_f64(a, b, op)
1296 } else {
1297 self.cmp_text(
1298 &crate::coercion::to_text_invariant(&a),
1299 &crate::coercion::to_text_invariant(&b),
1300 op,
1301 )
1302 }
1303 }
1304 };
1305 Ok(LiteralValue::Boolean(res))
1306 }
1307 }
1308 }
1309
1310 fn cmp_f64(&self, a: f64, b: f64, op: &str) -> bool {
1311 match op {
1312 "=" => a == b,
1313 "<>" => a != b,
1314 ">" => a > b,
1315 "<" => a < b,
1316 ">=" => a >= b,
1317 "<=" => a <= b,
1318 _ => unreachable!(),
1319 }
1320 }
1321 fn cmp_text(&self, a: &str, b: &str, op: &str) -> bool {
1322 let loc = self.context.locale();
1323 let (a, b) = (loc.fold_case_invariant(a), loc.fold_case_invariant(b));
1324 self.cmp_f64(
1325 a.cmp(&b) as i32 as f64,
1326 0.0,
1327 match op {
1328 "=" => "=",
1329 "<>" => "<>",
1330 ">" => ">",
1331 "<" => "<",
1332 ">=" => ">=",
1333 "<=" => "<=",
1334 _ => unreachable!(),
1335 },
1336 )
1337 }
1338}