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