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 "+" => self.apply_number_unary(v, |n| n),
700 "-" => self.apply_number_unary(v, |n| -n),
701 "%" => self.apply_number_unary(v, |n| n / 100.0),
702 _ => {
703 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(format!("Unary op '{op}'")))
704 }
705 }
706 }
707
708 fn eval_implicit_intersection_calc(&self, cv: crate::traits::CalcValue<'a>) -> LiteralValue {
709 let (cur_r0, cur_c0) = match self.current_cell {
710 Some(cell) => (cell.coord.row() as usize, cell.coord.col() as usize),
711 None => (0usize, 0usize),
712 };
713
714 match cv {
715 crate::traits::CalcValue::Scalar(v) => match v {
716 LiteralValue::Array(arr) => {
717 if arr.is_empty() || arr.first().map(|r| r.is_empty()).unwrap_or(true) {
718 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
719 }
720 arr[0][0].clone()
721 }
722 other => other,
723 },
724 crate::traits::CalcValue::Range(rv) => {
725 if rv.is_empty() {
726 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
727 }
728
729 if rv.sheet_name() == "__tmp" {
734 return rv.get_cell(0, 0);
735 }
736
737 if let Some(v) = rv.as_1x1() {
738 return v;
739 }
740
741 let (rows, cols) = rv.dims();
742 let sr = rv.start_row();
743 let sc = rv.start_col();
744 let er = rv.end_row();
745 let ec = rv.end_col();
746
747 if cols == 1 {
752 if cur_r0 < sr || cur_r0 > er {
753 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
754 }
755 let rel_r = cur_r0 - sr;
756 return rv.get_cell(rel_r, 0);
757 }
758
759 if rows == 1 {
760 if cur_c0 < sc || cur_c0 > ec {
761 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
762 }
763 let rel_c = cur_c0 - sc;
764 return rv.get_cell(0, rel_c);
765 }
766
767 if cur_r0 < sr || cur_r0 > er || cur_c0 < sc || cur_c0 > ec {
768 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
769 }
770 let rel_r = cur_r0 - sr;
771 let rel_c = cur_c0 - sc;
772 rv.get_cell(rel_r, rel_c)
773 }
774 crate::traits::CalcValue::Callable(_) => LiteralValue::Error(
775 ExcelError::new(ExcelErrorKind::Calc).with_message("LAMBDA value must be invoked"),
776 ),
777 }
778 }
779
780 fn implicit_intersection_from_reference(&self, reference: &ReferenceType) -> LiteralValue {
781 let (cur_r1, cur_c1) = match self.current_cell {
782 Some(cell) => (
783 cell.coord.row().saturating_add(1),
784 cell.coord.col().saturating_add(1),
785 ),
786 None => (1u32, 1u32),
787 };
788
789 match reference {
790 ReferenceType::Cell {
791 sheet, row, col, ..
792 } => {
793 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
794 match self
795 .context
796 .resolve_cell_reference(Some(sheet_name), *row, *col)
797 {
798 Ok(v) => v,
799 Err(e) => LiteralValue::Error(e),
800 }
801 }
802 ReferenceType::Range {
803 sheet,
804 start_row,
805 start_col,
806 end_row,
807 end_col,
808 ..
809 } => {
810 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
811
812 let (sr, sc, er, ec) = match (start_row, start_col, end_row, end_col) {
813 (Some(sr), Some(sc), Some(er), Some(ec)) => (*sr, *sc, *er, *ec),
814 _ => {
815 let cv = match self.eval_reference_to_calc(reference) {
818 Ok(cv) => cv,
819 Err(e) => return LiteralValue::Error(e),
820 };
821 return self.eval_implicit_intersection_calc(cv);
822 }
823 };
824
825 let (mut sr, mut er) = (sr, er);
827 let (mut sc, mut ec) = (sc, ec);
828 if sr > er {
829 std::mem::swap(&mut sr, &mut er);
830 }
831 if sc > ec {
832 std::mem::swap(&mut sc, &mut ec);
833 }
834
835 let pick = if sc == ec {
836 if cur_r1 < sr || cur_r1 > er {
838 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
839 }
840 (cur_r1, sc)
841 } else if sr == er {
842 if cur_c1 < sc || cur_c1 > ec {
844 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
845 }
846 (sr, cur_c1)
847 } else {
848 if cur_r1 < sr || cur_r1 > er || cur_c1 < sc || cur_c1 > ec {
850 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
851 }
852 (cur_r1, cur_c1)
853 };
854
855 match self
856 .context
857 .resolve_cell_reference(Some(sheet_name), pick.0, pick.1)
858 {
859 Ok(v) => v,
860 Err(e) => LiteralValue::Error(e),
861 }
862 }
863 other => {
865 let cv = match self.eval_reference_to_calc(other) {
866 Ok(cv) => cv,
867 Err(e) => return LiteralValue::Error(e),
868 };
869 self.eval_implicit_intersection_calc(cv)
870 }
871 }
872 }
873
874 fn apply_number_unary<F>(&self, v: LiteralValue, f: F) -> Result<LiteralValue, ExcelError>
875 where
876 F: Fn(f64) -> f64,
877 {
878 match crate::coercion::to_number_lenient_with_locale(&v, &self.context.locale()) {
879 Ok(n) => match crate::coercion::sanitize_numeric(f(n)) {
880 Ok(n2) => Ok(LiteralValue::Number(n2)),
881 Err(e) => Ok(LiteralValue::Error(e)),
882 },
883 Err(e) => Ok(LiteralValue::Error(e)),
884 }
885 }
886
887 fn eval_binary(
889 &self,
890 op: &str,
891 left: &ASTNode,
892 right: &ASTNode,
893 ) -> Result<LiteralValue, ExcelError> {
894 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
896 let l = self.evaluate_ast(left)?.into_literal();
897 let r = self.evaluate_ast(right)?.into_literal();
898 return self.compare(op, l, r);
899 }
900
901 let l_val = self.evaluate_ast(left)?.into_literal();
902 let r_val = self.evaluate_ast(right)?.into_literal();
903
904 match op {
905 "+" => self.add_sub_date_aware('+', l_val, r_val),
906 "-" => self.add_sub_date_aware('-', l_val, r_val),
907 "*" => self.numeric_binary(l_val, r_val, |a, b| a * b),
908 "/" => self.divide(l_val, r_val),
909 "^" => self.power(l_val, r_val),
910 "&" => Ok(LiteralValue::Text(format!(
911 "{}{}",
912 crate::coercion::to_text_invariant(&l_val),
913 crate::coercion::to_text_invariant(&r_val)
914 ))),
915 ":" => {
916 let lref = self.evaluate_ast_as_reference(left)?;
918 let rref = self.evaluate_ast_as_reference(right)?;
919 match crate::reference::combine_references(&lref, &rref) {
920 Ok(_r) => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
921 "Reference produced by ':' cannot be used directly as a value",
922 )),
923 Err(e) => Ok(LiteralValue::Error(e)),
924 }
925 }
926 _ => {
927 Err(ExcelError::new(ExcelErrorKind::NImpl)
928 .with_message(format!("Binary op '{op}'")))
929 }
930 }
931 }
932
933 fn add_sub_date_aware(
934 &self,
935 op: char,
936 left: LiteralValue,
937 right: LiteralValue,
938 ) -> Result<LiteralValue, ExcelError> {
939 debug_assert!(op == '+' || op == '-');
940
941 self.broadcast_apply(left, right, |l, r| {
942 use LiteralValue::*;
943
944 let date_system = self.context.date_system();
945
946 let date_like_serial = |v: &LiteralValue| -> Option<f64> {
947 match v {
948 Date(d) => Some(crate::builtins::datetime::date_to_serial_for(
949 date_system,
950 d,
951 )),
952 DateTime(dt) => Some(crate::builtins::datetime::datetime_to_serial_for(
953 date_system,
954 dt,
955 )),
956 _ => None,
957 }
958 };
959
960 let to_num = |v: &LiteralValue| -> Result<f64, ExcelError> {
961 crate::coercion::to_number_lenient_with_locale(v, &self.context.locale())
962 };
963
964 let serial_to_literal = |serial: f64| -> LiteralValue {
965 match crate::coercion::sanitize_numeric(serial) {
966 Ok(serial) => {
967 match crate::builtins::datetime::serial_to_datetime_for(date_system, serial)
968 {
969 Ok(dt) => {
970 if dt.time() == chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap() {
971 Date(dt.date())
972 } else {
973 DateTime(dt)
974 }
975 }
976 Err(e) => Error(e),
977 }
978 }
979 Err(e) => Error(e),
980 }
981 };
982
983 if let Some(ls) = date_like_serial(&l) {
985 match op {
986 '+' => {
987 let rn = to_num(&r)?;
988 return Ok(serial_to_literal(ls + rn));
989 }
990 '-' => {
991 if let Some(rs) = date_like_serial(&r) {
993 return Ok(Number(ls - rs));
994 }
995 let rn = to_num(&r)?;
996 return Ok(serial_to_literal(ls - rn));
997 }
998 _ => unreachable!(),
999 }
1000 }
1001
1002 if op == '+'
1004 && let Some(rs) = date_like_serial(&r)
1005 {
1006 let ln = to_num(&l)?;
1007 return Ok(serial_to_literal(ln + rs));
1008 }
1009
1010 self.numeric_binary(l, r, |a, b| if op == '+' { a + b } else { a - b })
1012 })
1013 }
1014
1015 fn eval_function_to_calc(
1017 &self,
1018 name: &str,
1019 args: &[ASTNode],
1020 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1021 if let Some(fun) = self.context.get_function("", name) {
1022 let handles: Vec<ArgumentHandle> =
1023 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
1024 let fctx = DefaultFunctionContext::new_with_sheet(
1026 self.context,
1027 self.current_cell,
1028 self.current_sheet,
1029 );
1030 return fun.dispatch(&handles, &fctx);
1031 }
1032
1033 if let Some(callable) = self.resolve_local_callable(name) {
1034 let mut eval_args = Vec::with_capacity(args.len());
1035 for arg in args {
1036 eval_args.push(self.evaluate_ast(arg)?.into_literal());
1037 }
1038 return callable.invoke(self, &eval_args);
1039 }
1040
1041 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1043 ExcelError::new(ExcelErrorKind::Name).with_message(format!("Unknown function: {name}")),
1044 )))
1045 }
1046
1047 fn eval_function(&self, name: &str, args: &[ASTNode]) -> Result<LiteralValue, ExcelError> {
1048 self.eval_function_to_calc(name, args)
1049 .map(|cv| cv.into_literal())
1050 }
1051
1052 pub fn function_context(&self, cell_ref: Option<&CellRef>) -> DefaultFunctionContext<'_> {
1053 DefaultFunctionContext::new_with_sheet(self.context, cell_ref.cloned(), self.current_sheet)
1054 }
1055
1056 fn eval_array_literal_to_calc(
1058 &self,
1059 rows: &[Vec<ASTNode>],
1060 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1061 let mut out = Vec::with_capacity(rows.len());
1062 for row in rows {
1063 let mut r = Vec::with_capacity(row.len());
1064 for cell in row {
1065 r.push(self.evaluate_ast(cell)?.into_literal());
1066 }
1067 out.push(r);
1068 }
1069 Ok(crate::traits::CalcValue::Range(
1070 crate::engine::range_view::RangeView::from_owned_rows(out, self.context.date_system()),
1071 ))
1072 }
1073
1074 fn eval_array_literal(&self, rows: &[Vec<ASTNode>]) -> Result<LiteralValue, ExcelError> {
1075 self.eval_array_literal_to_calc(rows)
1076 .map(|cv| cv.into_literal())
1077 }
1078
1079 fn numeric_binary<F>(
1081 &self,
1082 left: LiteralValue,
1083 right: LiteralValue,
1084 f: F,
1085 ) -> Result<LiteralValue, ExcelError>
1086 where
1087 F: Fn(f64, f64) -> f64 + Copy,
1088 {
1089 self.broadcast_apply(left, right, |l, r| {
1090 let a = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1091 let b = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1092 match (a, b) {
1093 (Ok(a), Ok(b)) => match crate::coercion::sanitize_numeric(f(a, b)) {
1094 Ok(n2) => Ok(LiteralValue::Number(n2)),
1095 Err(e) => Ok(LiteralValue::Error(e)),
1096 },
1097 (Err(e), _) | (_, Err(e)) => Ok(LiteralValue::Error(e)),
1098 }
1099 })
1100 }
1101
1102 fn divide(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
1103 self.broadcast_apply(left, right, |l, r| {
1104 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1105 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1106 let (a, b) = match (ln, rn) {
1107 (Ok(a), Ok(b)) => (a, b),
1108 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
1109 };
1110 if b == 0.0 {
1111 return Ok(LiteralValue::Error(ExcelError::from_error_string(
1112 "#DIV/0!",
1113 )));
1114 }
1115 match crate::coercion::sanitize_numeric(a / b) {
1116 Ok(n) => Ok(LiteralValue::Number(n)),
1117 Err(e) => Ok(LiteralValue::Error(e)),
1118 }
1119 })
1120 }
1121
1122 fn power(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
1123 self.broadcast_apply(left, right, |l, r| {
1124 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1125 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1126 let (a, b) = match (ln, rn) {
1127 (Ok(a), Ok(b)) => (a, b),
1128 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
1129 };
1130 if a < 0.0 && b.fract() != 0.0 {
1132 return Ok(LiteralValue::Error(ExcelError::new_num()));
1133 }
1134 match crate::coercion::sanitize_numeric(a.powf(b)) {
1135 Ok(n) => Ok(LiteralValue::Number(n)),
1136 Err(e) => Ok(LiteralValue::Error(e)),
1137 }
1138 })
1139 }
1140
1141 fn map_array<F>(&self, arr: Vec<Vec<LiteralValue>>, f: F) -> Result<LiteralValue, ExcelError>
1142 where
1143 F: Fn(LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1144 {
1145 let mut out = Vec::with_capacity(arr.len());
1146 for row in arr {
1147 let mut new_row = Vec::with_capacity(row.len());
1148 for cell in row {
1149 new_row.push(match f(cell) {
1150 Ok(v) => v,
1151 Err(e) => LiteralValue::Error(e),
1152 });
1153 }
1154 out.push(new_row);
1155 }
1156 Ok(LiteralValue::Array(out))
1157 }
1158
1159 fn combine_arrays<F>(
1160 &self,
1161 l: Vec<Vec<LiteralValue>>,
1162 r: Vec<Vec<LiteralValue>>,
1163 f: F,
1164 ) -> Result<LiteralValue, ExcelError>
1165 where
1166 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1167 {
1168 let l_shape = (l.len(), l.first().map(|r| r.len()).unwrap_or(0));
1170 let r_shape = (r.len(), r.first().map(|r| r.len()).unwrap_or(0));
1171 let target = match broadcast_shape(&[l_shape, r_shape]) {
1172 Ok(s) => s,
1173 Err(e) => return Ok(LiteralValue::Error(e)),
1174 };
1175
1176 let mut out = Vec::with_capacity(target.0);
1177 for i in 0..target.0 {
1178 let mut row = Vec::with_capacity(target.1);
1179 for j in 0..target.1 {
1180 let (li, lj) = project_index((i, j), l_shape);
1181 let (ri, rj) = project_index((i, j), r_shape);
1182 let lv = l
1183 .get(li)
1184 .and_then(|r| r.get(lj))
1185 .cloned()
1186 .unwrap_or(LiteralValue::Empty);
1187 let rv = r
1188 .get(ri)
1189 .and_then(|r| r.get(rj))
1190 .cloned()
1191 .unwrap_or(LiteralValue::Empty);
1192 row.push(match f(lv, rv) {
1193 Ok(v) => v,
1194 Err(e) => LiteralValue::Error(e),
1195 });
1196 }
1197 out.push(row);
1198 }
1199 Ok(LiteralValue::Array(out))
1200 }
1201
1202 fn broadcast_apply<F>(
1203 &self,
1204 left: LiteralValue,
1205 right: LiteralValue,
1206 f: F,
1207 ) -> Result<LiteralValue, ExcelError>
1208 where
1209 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1210 {
1211 use LiteralValue::*;
1212 match (left, right) {
1213 (Array(l), Array(r)) => self.combine_arrays(l, r, f),
1214 (Array(arr), v) => {
1215 let shape_l = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
1216 let shape_r = (1usize, 1usize);
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 (li, lj) = project_index((i, j), shape_l);
1226 let lv = arr
1227 .get(li)
1228 .and_then(|r| r.get(lj))
1229 .cloned()
1230 .unwrap_or(LiteralValue::Empty);
1231 row.push(match f(lv, v.clone()) {
1232 Ok(vv) => vv,
1233 Err(e) => LiteralValue::Error(e),
1234 });
1235 }
1236 out.push(row);
1237 }
1238 Ok(LiteralValue::Array(out))
1239 }
1240 (v, Array(arr)) => {
1241 let shape_l = (1usize, 1usize);
1242 let shape_r = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
1243 let target = match broadcast_shape(&[shape_l, shape_r]) {
1244 Ok(s) => s,
1245 Err(e) => return Ok(LiteralValue::Error(e)),
1246 };
1247 let mut out = Vec::with_capacity(target.0);
1248 for i in 0..target.0 {
1249 let mut row = Vec::with_capacity(target.1);
1250 for j in 0..target.1 {
1251 let (ri, rj) = project_index((i, j), shape_r);
1252 let rv = arr
1253 .get(ri)
1254 .and_then(|r| r.get(rj))
1255 .cloned()
1256 .unwrap_or(LiteralValue::Empty);
1257 row.push(match f(v.clone(), rv) {
1258 Ok(vv) => vv,
1259 Err(e) => LiteralValue::Error(e),
1260 });
1261 }
1262 out.push(row);
1263 }
1264 Ok(LiteralValue::Array(out))
1265 }
1266 (l, r) => f(l, r),
1267 }
1268 }
1269
1270 fn coerce_number(&self, v: &LiteralValue) -> Result<f64, ExcelError> {
1272 coercion::to_number_lenient(v)
1273 }
1274
1275 fn coerce_text(&self, v: &LiteralValue) -> String {
1276 coercion::to_text_invariant(v)
1277 }
1278
1279 fn compare(
1281 &self,
1282 op: &str,
1283 left: LiteralValue,
1284 right: LiteralValue,
1285 ) -> Result<LiteralValue, ExcelError> {
1286 use LiteralValue::*;
1287 if matches!(left, Error(_)) {
1288 return Ok(left);
1289 }
1290 if matches!(right, Error(_)) {
1291 return Ok(right);
1292 }
1293
1294 match (left, right) {
1296 (Array(l), Array(r)) => self.combine_arrays(l, r, |a, b| self.compare(op, a, b)),
1297 (Array(arr), v) => self.broadcast_apply(Array(arr), v, |a, b| self.compare(op, a, b)),
1298 (v, Array(arr)) => self.broadcast_apply(v, Array(arr), |a, b| self.compare(op, a, b)),
1299 (l, r) => {
1300 let res = match (l, r) {
1301 (Number(a), Number(b)) => self.cmp_f64(a, b, op),
1302 (Int(a), Number(b)) => self.cmp_f64(a as f64, b, op),
1303 (Number(a), Int(b)) => self.cmp_f64(a, b as f64, op),
1304 (Boolean(a), Boolean(b)) => {
1305 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
1306 }
1307 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
1308 (a, b) => {
1309 let an = crate::coercion::to_number_lenient_with_locale(
1311 &a,
1312 &self.context.locale(),
1313 )
1314 .ok();
1315 let bn = crate::coercion::to_number_lenient_with_locale(
1316 &b,
1317 &self.context.locale(),
1318 )
1319 .ok();
1320 if let (Some(a), Some(b)) = (an, bn) {
1321 self.cmp_f64(a, b, op)
1322 } else {
1323 self.cmp_text(
1324 &crate::coercion::to_text_invariant(&a),
1325 &crate::coercion::to_text_invariant(&b),
1326 op,
1327 )
1328 }
1329 }
1330 };
1331 Ok(LiteralValue::Boolean(res))
1332 }
1333 }
1334 }
1335
1336 fn cmp_f64(&self, a: f64, b: f64, op: &str) -> bool {
1337 match op {
1338 "=" => a == b,
1339 "<>" => a != b,
1340 ">" => a > b,
1341 "<" => a < b,
1342 ">=" => a >= b,
1343 "<=" => a <= b,
1344 _ => unreachable!(),
1345 }
1346 }
1347 fn cmp_text(&self, a: &str, b: &str, op: &str) -> bool {
1348 let loc = self.context.locale();
1349 let (a, b) = (loc.fold_case_invariant(a), loc.fold_case_invariant(b));
1350 self.cmp_f64(
1351 a.cmp(&b) as i32 as f64,
1352 0.0,
1353 match op {
1354 "=" => "=",
1355 "<>" => "<>",
1356 ">" => ">",
1357 "<" => "<",
1358 ">=" => ">=",
1359 "<=" => "<=",
1360 _ => unreachable!(),
1361 },
1362 )
1363 }
1364}