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