1mod edit;
5mod functions;
6
7pub use edit::get_word_boundaries_from_str;
8
9use serde::{Deserialize, Serialize};
10use std::collections::{HashMap, HashSet, VecDeque};
11use web_time::Instant;
12
13use super::cell::{CellRef, Dependency, EngineError, EvalError, TextCellRef, generate_unique_id};
14use super::column::DataColumn;
15use super::result_data::ResultData;
16#[derive(Default)]
18pub struct Context<'a> {
19 pub sheets: HashMap<String, &'a Sheet>,
21 pub pivot_tables: &'a [crate::core::pivot::PivotTable],
27 pub sheet_order: Vec<String>,
33}
34
35impl<'a> Context<'a> {
36 pub fn new() -> Self {
38 Self {
39 sheets: HashMap::new(),
40 pivot_tables: &[],
41 sheet_order: Vec::new(),
42 }
43 }
44
45 pub fn add_table(&mut self, name: String, sheet: &'a Sheet) {
47 self.sheets.insert(name, sheet);
48 }
49}
50
51enum LetScope<'a> {
59 Empty,
60 Bound {
61 name: &'a str,
62 value: &'a ResultData,
63 parent: &'a LetScope<'a>,
64 },
65}
66
67impl<'a> LetScope<'a> {
68 fn get(&self, name: &str) -> Option<&ResultData> {
69 match self {
70 LetScope::Empty => None,
71 LetScope::Bound {
72 name: n,
73 value,
74 parent,
75 } => {
76 if n.eq_ignore_ascii_case(name) {
77 Some(value)
78 } else {
79 parent.get(name)
80 }
81 }
82 }
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq)]
88pub enum Direction {
89 None,
91 Up,
93 Down,
95 Left,
97 Right,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct Sheet {
137 #[serde(default = "generate_unique_id")]
140 pub id: u64,
141 pub name: String,
143 pub(crate) columns: Vec<DataColumn>,
150 #[serde(default)]
152 pub tables: Vec<crate::core::table::ExcelTable>,
153 #[serde(skip, default)]
156 pub dependencies: HashMap<Dependency, HashSet<CellRef>>,
157 #[serde(skip, default)]
160 pub dependencies_rev: HashMap<CellRef, HashSet<Dependency>>,
161 #[serde(skip)]
164 pub uncommitted_actions: Vec<crate::core::SheetAction>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct SheetInit {
171 #[serde(default)]
173 pub id: Option<u64>,
174 pub name: Option<String>,
176 pub rows: usize,
178 pub cols: usize,
180}
181
182impl Default for SheetInit {
183 fn default() -> Self {
184 Self {
185 id: None,
186 name: None,
187 rows: 10,
188 cols: 5,
189 }
190 }
191}
192
193#[derive(Clone, Copy, PartialEq, Eq)]
195enum BlankPolicy {
196 Zero,
198 Skip,
200 Reject,
202}
203
204impl Sheet {
205 pub fn new(args: SheetInit) -> Sheet {
208 let SheetInit {
209 id,
210 name,
211 rows,
212 cols,
213 } = args;
214 let sheet_id = id.unwrap_or_else(generate_unique_id);
215 let sheet_name = name.unwrap_or_else(|| "table_1".to_string());
216
217 let mut columns = Vec::with_capacity(cols);
218 for _ in 0..cols {
219 columns.push(DataColumn::new(rows));
220 }
221
222 let mut uncommitted_actions = Vec::new();
223 for c in 0..cols {
224 for r in 0..rows {
225 uncommitted_actions.push(crate::core::SheetAction::SetCellSrc {
226 sheet_name: sheet_name.clone(),
227 col: c,
228 row: r,
229 src: String::new(),
230 });
231 }
232 }
233
234 Self {
235 id: sheet_id,
236 name: sheet_name,
237 columns,
238 tables: Vec::new(),
239 dependencies: HashMap::new(),
240 dependencies_rev: HashMap::new(),
241 uncommitted_actions,
242 }
243 }
244
245 pub fn setup_after_deserialization(&mut self) {
252 for col in &mut self.columns {
253 col.rebuild_after_load();
254 }
255 self.mark_all_dirty();
256 }
257
258 pub(crate) fn get_all_tables_for_compilation(&self, context: Option<&Context>) -> Vec<Sheet> {
266 let mut list = vec![self.clone()];
267 let mut seen = std::collections::HashSet::new();
268 seen.insert(self.id);
269 if let Some(ctx) = context {
270 for t in ctx.sheets.values() {
271 if !seen.contains(&t.id) {
272 seen.insert(t.id);
273 list.push((*t).clone());
274 }
275 }
276 }
277 list
278 }
279
280 pub fn mark_all_dirty(&mut self) {
285 for col in &mut self.columns {
286 col.dirty_indices.clear();
287 col.dirty_indices.extend(0..col.src.len());
288 }
289 }
290
291 pub fn commit(&mut self, context: Option<&Context>) -> Result<HashSet<CellRef>, EngineError> {
293 let mut queue: VecDeque<CellRef> = VecDeque::new();
294 let mut queue_set: HashSet<CellRef> = HashSet::new();
295 let mut updated_cells: HashSet<CellRef> = HashSet::new();
296
297 for (col_idx, col_data) in self.columns.iter_mut().enumerate() {
299 for row_idx in &col_data.dirty_indices {
300 let cell = CellRef::new(*row_idx, col_idx);
301 queue.push_back(cell);
302 queue_set.insert(cell);
303 updated_cells.insert(cell);
304 }
305 col_data.dirty_indices.clear();
306 }
307
308 let initial_queue_len = queue.len();
309 if initial_queue_len == 0 {
310 return Ok(updated_cells);
311 }
312
313 let start_commit = Instant::now();
314 log::info!(
315 "Sheet '{}' commit starting for {} dirty cells",
316 self.name,
317 initial_queue_len
318 );
319 let max_ops = 10000.max(initial_queue_len * 3);
320 let mut ops = 0;
321
322 let mut tables_for_compilation = self.get_all_tables_for_compilation(context);
323 let mut last_log_time = Instant::now();
324
325 while let Some(cell_ref) = queue.pop_front() {
326 queue_set.remove(&cell_ref);
327 ops += 1;
328 if ops > max_ops {
329 println!("Circular dependency or too many updates detected");
330 break;
331 }
332
333 if ops % 50000 == 0 {
334 log::info!(
335 "Sheet '{}' commit progress: {}/{} cells processed ({:.2?})",
336 self.name,
337 ops,
338 initial_queue_len,
339 last_log_time.elapsed()
340 );
341 last_log_time = Instant::now();
342 }
343
344 let mut detected_num_format: Option<String> = None;
347 let (result, new_deps, compiled_to_cache) = {
348 let src = self.get_src_str_ref(&cell_ref).unwrap_or("");
349 if !src.starts_with('=') {
350 let res = if src.is_empty() {
351 ResultData::None
352 } else if src.starts_with('"') && src.ends_with('"') && src.len() >= 2 {
353 ResultData::String(src[1..src.len() - 1].to_string())
354 } else if let Ok(i) = src.parse::<i64>() {
355 ResultData::Integer(i)
356 } else if let Ok(f) = src.parse::<f64>() {
357 ResultData::Float(f)
358 } else if src.eq_ignore_ascii_case("true") {
359 ResultData::Boolean(true)
360 } else if src.eq_ignore_ascii_case("false") {
361 ResultData::Boolean(false)
362 } else if let Some((date, format)) =
363 crate::core::date::parse_date(src.trim_matches(' '))
364 {
365 detected_num_format = Some(format.to_format_code());
369 ResultData::Float(crate::core::date::date_to_excel_serial(date))
370 } else {
371 ResultData::String(src.to_string())
372 };
373 (res, vec![], None)
374 } else {
375 let compiled =
376 crate::core::parser::compile_formula(src, &tables_for_compilation);
377 let eval_src =
378 crate::core::parser::serialize_formula(&compiled, &tables_for_compilation);
379 let (res, deps) = match self.eval_with_row(
380 &eval_src,
381 context,
382 Some(cell_ref.row),
383 Some(cell_ref.col),
384 ) {
385 Ok(r) => r,
386 Err(e) => (ResultData::Error(e.to_string()), vec![]),
387 };
388 let final_res = if let ResultData::None = res {
389 ResultData::Float(0.0)
390 } else {
391 res
392 };
393 (final_res, deps, Some(compiled))
394 }
395 };
396
397 if let Some(col) = self.columns.get_mut(cell_ref.col)
399 && cell_ref.row < col.compiled_src.len()
400 {
401 col.compiled_src[cell_ref.row] = compiled_to_cache.unwrap_or_default();
402 }
403
404 if let Some(old_deps) = self.dependencies_rev.remove(&cell_ref) {
407 for provider in old_deps {
408 if let Some(dependents) = self.dependencies.get_mut(&provider) {
409 dependents.remove(&cell_ref);
410 }
411 }
412 }
413
414 if !new_deps.is_empty() {
416 let mut new_deps_set = HashSet::new();
417 for provider in new_deps {
418 new_deps_set.insert(provider.clone());
419 self.dependencies
420 .entry(provider)
421 .or_default()
422 .insert(cell_ref);
423 }
424 self.dependencies_rev.insert(cell_ref, new_deps_set);
425 }
426
427 let inherited = if detected_num_format.is_some()
432 || !matches!(result, ResultData::Float(_) | ResultData::Integer(_))
433 {
434 None
435 } else {
436 self.get_src_str_ref(&cell_ref)
437 .and_then(|src| src.strip_prefix('='))
438 .and_then(|body| crate::core::parser::parse_excel_formula(body).ok())
439 .and_then(|ast| self.inherited_date_format(&ast))
440 };
441 if let Some(code) = detected_num_format.or(inherited) {
444 let existing = self
445 .get_cell_style(cell_ref.row, cell_ref.col)
446 .and_then(|s| s.num_format.clone());
447 if existing.is_none() {
448 self.update_cell_style(cell_ref.row, cell_ref.col, |style| {
449 style.num_format = Some(code);
450 });
451 }
452 }
453
454 if let Some(col) = self.columns.get_mut(cell_ref.col)
456 && cell_ref.row < col.data.len()
457 {
458 col.data.set(cell_ref.row, result.clone());
459 updated_cells.insert(cell_ref);
460 }
461 if let Some(comp_sheet) = tables_for_compilation
462 .iter_mut()
463 .find(|s| s.name == self.name)
464 && let Some(col) = comp_sheet.columns.get_mut(cell_ref.col)
465 && cell_ref.row < col.data.len()
466 {
467 col.data.set(cell_ref.row, result);
468 }
469
470 let local_dep_key = Dependency::Local(cell_ref);
474 if let Some(dependents) = self.dependencies.get(&local_dep_key) {
475 for dependent in dependents {
476 if !queue_set.contains(dependent) {
477 queue.push_back(*dependent);
478 queue_set.insert(*dependent);
479 }
480 }
481 }
482
483 let local_col_dep_key = Dependency::LocalColumn(cell_ref.col);
485 if let Some(dependents) = self.dependencies.get(&local_col_dep_key) {
486 for dependent in dependents {
487 if !queue_set.contains(dependent) {
488 queue.push_back(*dependent);
489 queue_set.insert(*dependent);
490 }
491 }
492 }
493 }
494 if initial_queue_len > 0 {
495 log::info!(
496 "Sheet '{}' commit finished. Processed {} cell updates. Total time: {:.2?}",
497 self.name,
498 ops,
499 start_commit.elapsed()
500 );
501 }
502 Ok(updated_cells)
503 }
504
505 pub fn eval_with_row(
518 &self,
519 input: &str,
520 context: Option<&Context>,
521 row: Option<usize>,
522 col: Option<usize>,
523 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
524 if input.is_empty() {
525 return Ok((ResultData::None, vec![]));
526 }
527 if let Some(formula) = input.strip_prefix('=') {
528 self.eval_excel(formula, context, row, col)
529 } else {
530 if let Ok(i) = input.parse::<i64>() {
531 Ok((ResultData::Integer(i), vec![]))
532 } else if let Ok(f) = input.parse::<f64>() {
533 Ok((ResultData::Float(f), vec![]))
534 } else if let Ok(b) = input.parse::<bool>() {
535 Ok((ResultData::Boolean(b), vec![]))
536 } else {
537 Ok((ResultData::String(input.to_string()), vec![]))
538 }
539 }
540 }
541
542 pub fn eval(
555 &self,
556 input: &str,
557 context: Option<&Context>,
558 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
559 self.eval_with_row(input, context, None, None)
560 }
561
562 fn eval_excel(
563 &self,
564 code: &str,
565 context: Option<&Context>,
566 row: Option<usize>,
567 col: Option<usize>,
568 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
569 let ast = crate::core::parser::parse_excel_formula(code)
570 .map_err(|e| EngineError::EvalError(EvalError::UnknownFunction(e)))?;
571
572 let mut deps = Vec::new();
573 let result = match self.evaluate_ast(&ast, context, row, col, &mut deps, &LetScope::Empty) {
574 Ok(r) => r,
575 Err(EngineError::EvalError(EvalError::UnknownFunction(err_str)))
576 if err_str.starts_with('#') =>
577 {
578 ResultData::Error(err_str)
579 }
580 Err(e) => return Err(e),
581 };
582 Ok((result, deps))
583 }
584
585 fn evaluate_ast(
586 &self,
587 ast: &crate::core::parser::Expr,
588 context: Option<&Context>,
589 row: Option<usize>,
590 col: Option<usize>,
591 deps: &mut Vec<Dependency>,
592 scope: &LetScope<'_>,
593 ) -> Result<ResultData, EngineError> {
594 use crate::core::SheetSection;
595 use crate::core::parser::Expr;
596 use crate::core::parser::Op;
597
598 match ast {
599 Expr::Number(n) => Ok(ResultData::Float(*n)),
600 Expr::String(s) => Ok(ResultData::String(s.clone())),
601 Expr::Boolean(b) => Ok(ResultData::Boolean(*b)),
602 Expr::Identifier(name) => match scope.get(name) {
603 Some(val) => Ok(val.clone()),
604 None => Ok(ResultData::Error("#NAME?".to_string())),
605 },
606 Expr::StructuredRef {
607 sheet,
608 column,
609 is_this_row,
610 section,
611 } => {
612 let ref_name = match sheet {
613 Some(name) => name.clone(),
614 None => self.name.clone(),
615 };
616
617 let mut found: Option<(&Sheet, &crate::core::table::ExcelTable)> =
625 self.find_table(&ref_name).map(|t| (self, t));
626 if found.is_none()
627 && let Some(ctx) = context
628 {
629 for s in ctx.sheets.values() {
630 if let Some(t) = s.find_table(&ref_name) {
631 found = Some((s, t));
632 break;
633 }
634 }
635 }
636
637 if let Some((table_sheet, excel_table)) = found {
638 let is_self = table_sheet.name == self.name;
639 let sheet_name = table_sheet.name.clone();
640
641 let col_indices: Vec<(usize, usize)> = if let Some(col_name) = column {
643 let local = excel_table.local_column_index(col_name).ok_or_else(|| {
644 EngineError::EvalError(EvalError::UnknownFunction(format!(
645 "Column not found: {}",
646 col_name
647 )))
648 })?;
649 vec![(local, excel_table.start_col + local)]
650 } else {
651 (0..excel_table.columns.len())
652 .map(|local| (local, excel_table.start_col + local))
653 .collect()
654 };
655 let is_whole_table = column.is_none();
656
657 match section {
658 SheetSection::Headers => {
659 let names: Vec<ResultData> = col_indices
660 .iter()
661 .map(|&(local, _)| {
662 ResultData::String(
663 excel_table.columns.get(local).cloned().unwrap_or_default(),
664 )
665 })
666 .collect();
667 if is_whole_table {
668 Ok(ResultData::List(names))
669 } else {
670 Ok(names.into_iter().next().unwrap_or(ResultData::None))
671 }
672 }
673 SheetSection::Totals => {
674 if let Some(totals_row) = excel_table.totals_row() {
675 let mut results = Vec::new();
676 for &(_, col_idx) in &col_indices {
677 let cell_ref = CellRef::new(totals_row, col_idx);
678 if is_self {
679 deps.push(Dependency::Local(cell_ref));
680 } else {
681 deps.push(Dependency::Remote {
682 sheet: sheet_name.clone(),
683 cell: cell_ref,
684 });
685 }
686 results.push(table_sheet.get_result_data(&cell_ref));
687 }
688 if is_whole_table {
689 Ok(ResultData::List(results))
690 } else {
691 Ok(results.into_iter().next().unwrap_or(ResultData::None))
692 }
693 } else {
694 Ok(ResultData::None)
695 }
696 }
697 SheetSection::Data | SheetSection::All => {
698 if *is_this_row {
699 let r = row.ok_or_else(|| {
700 EngineError::EvalError(EvalError::UnknownFunction(
701 "This row reference cannot be evaluated without row context"
702 .to_string(),
703 ))
704 })?;
705 let mut results = Vec::new();
706 for &(_, col_idx) in &col_indices {
707 let cell_ref = CellRef::new(r, col_idx);
708 if is_self {
709 deps.push(Dependency::Local(cell_ref));
710 } else {
711 deps.push(Dependency::Remote {
712 sheet: sheet_name.clone(),
713 cell: cell_ref,
714 });
715 }
716 results.push(table_sheet.get_result_data(&cell_ref));
717 }
718 if is_whole_table {
719 Ok(ResultData::List(results))
720 } else {
721 Ok(results.into_iter().next().unwrap_or(ResultData::None))
722 }
723 } else {
724 let mut results = Vec::new();
738 for &(_, col_idx) in &col_indices {
739 for r in
740 excel_table.data_start_row()..=excel_table.data_end_row()
741 {
742 let cell_ref = CellRef::new(r, col_idx);
743 if is_self {
744 deps.push(Dependency::Local(cell_ref));
745 } else {
746 deps.push(Dependency::Remote {
747 sheet: sheet_name.clone(),
748 cell: cell_ref,
749 });
750 }
751 results.push(table_sheet.get_result_data(&cell_ref));
752 }
753 }
754 Ok(ResultData::List(results))
755 }
756 }
757 }
758 } else {
759 let sheet_name = ref_name;
763 let is_self = sheet_name == self.name;
764
765 let target_table = if is_self {
766 self
767 } else if let Some(ctx) = context {
768 if let Some(t) = ctx.sheets.get(&sheet_name) {
769 t
770 } else {
771 return Err(EngineError::EvalError(EvalError::UnknownFunction(
772 format!("Sheet not found: {}", sheet_name),
773 )));
774 }
775 } else {
776 return Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
777 "No context to resolve sheet reference: {}",
778 sheet_name
779 ))));
780 };
781
782 let col_indices: Vec<usize> = if let Some(col_name) = column {
786 let pos = target_table
787 .columns
788 .iter()
789 .position(|c| c.name == *col_name)
790 .ok_or_else(|| {
791 EngineError::EvalError(EvalError::UnknownFunction(format!(
792 "Column not found: {}",
793 col_name
794 )))
795 })?;
796 vec![pos]
797 } else {
798 (0..target_table.columns.len()).collect()
799 };
800 let is_whole_table = column.is_none();
801
802 match section {
803 SheetSection::Headers => {
804 let names: Vec<ResultData> = col_indices
805 .iter()
806 .map(|&idx| {
807 ResultData::String(
808 target_table
809 .columns
810 .get(idx)
811 .map(|c| c.name.clone())
812 .unwrap_or_default(),
813 )
814 })
815 .collect();
816 if is_whole_table {
817 Ok(ResultData::List(names))
818 } else {
819 Ok(names.into_iter().next().unwrap_or(ResultData::None))
820 }
821 }
822 SheetSection::Totals => Ok(ResultData::None),
823 SheetSection::Data | SheetSection::All => {
824 if *is_this_row {
825 let r = row.ok_or_else(|| {
826 EngineError::EvalError(EvalError::UnknownFunction(
827 "This row reference cannot be evaluated without row context"
828 .to_string(),
829 ))
830 })?;
831 let mut results = Vec::new();
832 for &col_idx in &col_indices {
833 let cell_ref = CellRef::new(r, col_idx);
834 if is_self {
835 deps.push(Dependency::Local(cell_ref));
836 } else {
837 deps.push(Dependency::Remote {
838 sheet: sheet_name.clone(),
839 cell: cell_ref,
840 });
841 }
842 results.push(target_table.get_result_data(&cell_ref));
843 }
844 if is_whole_table {
845 Ok(ResultData::List(results))
846 } else {
847 Ok(results.into_iter().next().unwrap_or(ResultData::None))
848 }
849 } else {
850 let mut results = Vec::new();
851 for &col_idx in &col_indices {
852 if is_self {
853 deps.push(Dependency::LocalColumn(col_idx));
854 } else {
855 deps.push(Dependency::RemoteColumn {
856 sheet: sheet_name.clone(),
857 col: col_idx,
858 });
859 }
860 for r in 0..target_table.row_count() {
861 let cell_ref = CellRef::new(r, col_idx);
862 results.push(target_table.get_result_data(&cell_ref));
863 }
864 }
865 Ok(ResultData::List(results))
866 }
867 }
868 }
869 }
870 }
871 Expr::CellRef {
872 sheet,
873 row: r_val,
874 col,
875 ..
876 } => {
877 let cell_ref = CellRef::new(*r_val, *col);
878 let is_self = match sheet {
879 Some(name) => name == &self.name,
880 None => true,
881 };
882
883 if is_self {
884 deps.push(Dependency::Local(cell_ref));
885 Ok(self.get_result_data(&cell_ref))
886 } else {
887 let name = sheet.as_ref().unwrap().clone();
888 deps.push(Dependency::Remote {
889 sheet: name.clone(),
890 cell: cell_ref,
891 });
892
893 if let Some(ctx) = context {
894 if let Some(t) = ctx.sheets.get(&name) {
895 Ok(t.get_result_data(&cell_ref))
896 } else {
897 Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
898 "Sheet not found: {}",
899 name
900 ))))
901 }
902 } else {
903 Err(EngineError::EvalError(EvalError::UnknownFunction(
904 "No context to resolve sheet reference".to_string(),
905 )))
906 }
907 }
908 }
909 Expr::RangeRef {
910 sheet,
911 start_row,
912 start_col,
913 end_row,
914 end_col,
915 ..
916 } => {
917 let is_self = match sheet {
918 Some(name) => name == &self.name,
919 None => true,
920 };
921
922 let actual_end_row = if *end_row == usize::MAX {
923 if is_self {
924 self.row_count().saturating_sub(1)
925 } else if let Some(ctx) = context {
926 ctx.sheets
927 .get(sheet.as_ref().unwrap())
928 .map(|t| t.row_count().saturating_sub(1))
929 .unwrap_or(0)
930 } else {
931 0
932 }
933 } else {
934 *end_row
935 };
936
937 let is_col_range = *end_row == usize::MAX;
938
939 let mut seen_col_deps: HashSet<usize> = HashSet::new();
955
956 let mut results = Vec::new();
957 for r in *start_row..=actual_end_row {
958 for c in *start_col..=*end_col {
959 let cell_ref = CellRef::new(r, c);
960 if is_self {
961 if is_col_range {
962 if seen_col_deps.insert(c) {
963 let col_dep = Dependency::LocalColumn(c);
964 if !deps.contains(&col_dep) {
965 deps.push(col_dep);
966 }
967 }
968 } else {
969 deps.push(Dependency::Local(cell_ref));
970 }
971 if row == Some(r) && col == Some(c) {
988 results.push(ResultData::None);
989 } else {
990 results.push(self.get_result_data(&cell_ref));
991 }
992 } else {
993 let name = sheet.as_ref().unwrap().clone();
994 if is_col_range {
995 if seen_col_deps.insert(c) {
996 let col_dep = Dependency::RemoteColumn {
997 sheet: name.clone(),
998 col: c,
999 };
1000 if !deps.contains(&col_dep) {
1001 deps.push(col_dep);
1002 }
1003 }
1004 } else {
1005 deps.push(Dependency::Remote {
1006 sheet: name.clone(),
1007 cell: cell_ref,
1008 });
1009 }
1010 if let Some(ctx) = context {
1011 if let Some(t) = ctx.sheets.get(&name) {
1012 results.push(t.get_result_data(&cell_ref));
1013 } else {
1014 return Err(EngineError::EvalError(
1015 EvalError::UnknownFunction(format!(
1016 "Sheet not found: {}",
1017 name
1018 )),
1019 ));
1020 }
1021 } else {
1022 return Err(EngineError::EvalError(EvalError::UnknownFunction(
1023 "No context to resolve sheet reference".to_string(),
1024 )));
1025 }
1026 }
1027 }
1028 }
1029 Ok(ResultData::List(results))
1030 }
1031 Expr::List(list) => {
1032 let mut results = Vec::new();
1033 for item in list {
1034 results.push(self.evaluate_ast(item, context, row, col, deps, scope)?);
1035 }
1036 Ok(ResultData::List(results))
1037 }
1038 Expr::UnaryOp { op, expr } => {
1039 let val = self.evaluate_ast(expr, context, row, col, deps, scope)?;
1040 match op {
1041 Op::Sub => match val {
1042 ResultData::Float(f) => Ok(ResultData::Float(-f)),
1043 ResultData::Integer(i) => Ok(ResultData::Integer(-i)),
1044 _ => Err(EngineError::EvalError(EvalError::UnknownFunction(
1045 "Unary minus expects number".to_string(),
1046 ))),
1047 },
1048 _ => Ok(val),
1049 }
1050 }
1051 Expr::BinaryOp { op, left, right } => {
1052 let l_val = self.evaluate_ast(left, context, row, col, deps, scope)?;
1053 let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1054
1055 match op {
1056 Op::Eq | Op::Ne | Op::Lt | Op::Gt | Op::Le | Op::Ge => {
1057 if let ResultData::Error(_) = &l_val {
1058 return Ok(l_val);
1059 }
1060 if let ResultData::Error(_) = &r_val {
1061 return Ok(r_val);
1062 }
1063 let ord = Self::compare_excel_values(&l_val, &r_val);
1064 let b = match op {
1065 Op::Eq => ord.is_eq(),
1066 Op::Ne => !ord.is_eq(),
1067 Op::Lt => ord.is_lt(),
1068 Op::Gt => ord.is_gt(),
1069 Op::Le => ord.is_le(),
1070 Op::Ge => ord.is_ge(),
1071 _ => unreachable!(),
1072 };
1073 Ok(ResultData::Boolean(b))
1074 }
1075 _ => {
1076 if let ResultData::Error(_) = &l_val {
1077 return Ok(l_val);
1078 }
1079 let lf = match self.to_f64(&l_val) {
1080 Some(f) => f,
1081 None => return Ok(ResultData::Error("#VALUE!".to_string())),
1082 };
1083 if let ResultData::Error(_) = &r_val {
1084 return Ok(r_val);
1085 }
1086 let rf = match self.to_f64(&r_val) {
1087 Some(f) => f,
1088 None => return Ok(ResultData::Error("#VALUE!".to_string())),
1089 };
1090 match op {
1091 Op::Add => Ok(ResultData::Float(lf + rf)),
1092 Op::Sub => Ok(ResultData::Float(lf - rf)),
1093 Op::Mul => Ok(ResultData::Float(lf * rf)),
1094 Op::Div => {
1095 if rf == 0.0 {
1096 return Ok(ResultData::Error("#DIV/0!".to_string()));
1097 }
1098 Ok(ResultData::Float(lf / rf))
1099 }
1100 Op::Exp => {
1101 if lf == 0.0 && rf == 0.0 {
1102 return Ok(ResultData::Error("#NUM!".to_string()));
1103 }
1104 if lf == 0.0 && rf < 0.0 {
1105 return Ok(ResultData::Error("#DIV/0!".to_string()));
1106 }
1107 if lf < 0.0 {
1108 if rf.fract() != 0.0 || rf.abs() > 1e6 {
1109 return Ok(ResultData::Error("#NUM!".to_string()));
1110 }
1111 let res = lf.powi(rf as i32);
1112 if res.is_nan() || res.is_infinite() {
1113 return Ok(ResultData::Error("#NUM!".to_string()));
1114 }
1115 return Ok(ResultData::Float(res));
1116 }
1117 let res = lf.powf(rf);
1118 if res.is_nan() || res.is_infinite() {
1119 return Ok(ResultData::Error("#NUM!".to_string()));
1120 }
1121 Ok(ResultData::Float(res))
1122 }
1123 _ => unreachable!(),
1124 }
1125 }
1126 }
1127 }
1128 Expr::FunctionCall { name, args } => {
1129 self.evaluate_function(name, args, context, row, col, deps, scope)
1130 }
1131 }
1132 }
1133
1134 fn excel_type_rank(val: &ResultData) -> u8 {
1135 match val {
1136 ResultData::None => 0,
1137 ResultData::Integer(_) | ResultData::Float(_) => 1,
1138 ResultData::String(_) => 2,
1139 ResultData::Boolean(_) => 3,
1140 _ => 4,
1141 }
1142 }
1143
1144 fn compare_excel_values(l: &ResultData, r: &ResultData) -> std::cmp::Ordering {
1145 match (l, r) {
1147 (ResultData::None, ResultData::None) => return std::cmp::Ordering::Equal,
1148 (ResultData::None, ResultData::Integer(b)) => {
1149 return 0.0
1150 .partial_cmp(&(*b as f64))
1151 .unwrap_or(std::cmp::Ordering::Equal);
1152 }
1153 (ResultData::None, ResultData::Float(b)) => {
1154 return 0.0.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal);
1155 }
1156 (ResultData::Integer(a), ResultData::None) => {
1157 return (*a as f64)
1158 .partial_cmp(&0.0)
1159 .unwrap_or(std::cmp::Ordering::Equal);
1160 }
1161 (ResultData::Float(a), ResultData::None) => {
1162 return a.partial_cmp(&0.0).unwrap_or(std::cmp::Ordering::Equal);
1163 }
1164 (ResultData::None, ResultData::String(b)) => {
1165 return "".cmp(b.to_lowercase().as_str());
1166 }
1167 (ResultData::String(a), ResultData::None) => {
1168 return a.to_lowercase().as_str().cmp("");
1169 }
1170 (ResultData::None, ResultData::Boolean(b)) => {
1171 return false.cmp(b);
1172 }
1173 (ResultData::Boolean(a), ResultData::None) => {
1174 return a.cmp(&false);
1175 }
1176 _ => {}
1177 }
1178
1179 let rank_l = Self::excel_type_rank(l);
1180 let rank_r = Self::excel_type_rank(r);
1181 if rank_l != rank_r {
1182 return rank_l.cmp(&rank_r);
1183 }
1184 match (l, r) {
1185 (ResultData::Integer(a), ResultData::Integer(b)) => a.cmp(b),
1186 (ResultData::Float(a), ResultData::Float(b)) => {
1187 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
1188 }
1189 (ResultData::Integer(a), ResultData::Float(b)) => (*a as f64)
1190 .partial_cmp(b)
1191 .unwrap_or(std::cmp::Ordering::Equal),
1192 (ResultData::Float(a), ResultData::Integer(b)) => a
1193 .partial_cmp(&(*b as f64))
1194 .unwrap_or(std::cmp::Ordering::Equal),
1195 (ResultData::Boolean(a), ResultData::Boolean(b)) => a.cmp(b),
1196 (ResultData::String(a), ResultData::String(b)) => Self::compare_excel_strings(a, b),
1197 _ => std::cmp::Ordering::Equal,
1198 }
1199 }
1200
1201 fn sort_compare_blanks_last(
1211 l: &ResultData,
1212 r: &ResultData,
1213 sort_order: f64,
1214 ) -> std::cmp::Ordering {
1215 match (matches!(l, ResultData::None), matches!(r, ResultData::None)) {
1216 (true, true) => std::cmp::Ordering::Equal,
1217 (true, false) => std::cmp::Ordering::Greater,
1218 (false, true) => std::cmp::Ordering::Less,
1219 (false, false) => {
1220 let ord = Self::compare_excel_values(l, r);
1221 if sort_order < 0.0 { ord.reverse() } else { ord }
1222 }
1223 }
1224 }
1225
1226 fn is_excel_number_str(s: &str) -> bool {
1227 let s = s.trim();
1228 if s.is_empty() {
1229 return false;
1230 }
1231 let bytes = s.as_bytes();
1232 let first = bytes[0];
1233 if first == b'e' || first == b'E' {
1234 return false;
1235 }
1236 if (first == b'+' || first == b'-') && bytes.len() > 1 {
1237 let second = bytes[1];
1238 if second == b'e' || second == b'E' {
1239 return false;
1240 }
1241 }
1242 true
1243 }
1244
1245 fn compare_excel_strings(a: &str, b: &str) -> std::cmp::Ordering {
1246 let a_low = a.to_lowercase();
1247 let b_low = b.to_lowercase();
1248 let char_weight = |ch: char| -> u32 {
1249 match ch {
1250 '-' => 1,
1251 '(' => 2,
1252 ')' => 3,
1253 _ => (ch as u32) + 10,
1254 }
1255 };
1256 for (ca, cb) in a_low.chars().zip(b_low.chars()) {
1257 if ca != cb {
1258 let wa = char_weight(ca);
1259 let wb = char_weight(cb);
1260 return wa.cmp(&wb);
1261 }
1262 }
1263 a_low.len().cmp(&b_low.len())
1264 }
1265
1266 pub(crate) fn clean_float(val: f64) -> f64 {
1273 if val == 0.0 || !val.is_finite() {
1274 return val;
1275 }
1276 let abs_val = val.abs();
1277 let exp = abs_val.log10().floor() as i32;
1278 let factor = 10.0f64.powi(15 - 1 - exp);
1279 if factor.is_finite() && factor != 0.0 {
1280 let rounded = (val * factor).round() / factor;
1281 if (val - rounded).abs() <= 1e-14 * abs_val {
1282 return rounded;
1283 }
1284 }
1285 val
1286 }
1287
1288 pub(crate) fn to_f64(&self, val: &ResultData) -> Option<f64> {
1298 match val {
1299 ResultData::None => Some(0.0),
1300 ResultData::Float(f) => Some(*f),
1301 ResultData::Integer(i) => Some(*i as f64),
1302 ResultData::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
1303 ResultData::String(s) => {
1304 let s_trim = s.trim();
1305 if Self::is_excel_number_str(s_trim) {
1306 if let Ok(f) = s_trim.parse::<f64>() {
1307 return Some(f);
1308 }
1309 if let Some((date, _)) = crate::core::date::parse_date(s_trim) {
1310 return Some(crate::core::date::date_to_excel_serial(date));
1311 }
1312 None
1313 } else if let Some((date, _)) = crate::core::date::parse_date(s_trim) {
1314 Some(crate::core::date::date_to_excel_serial(date))
1315 } else {
1316 None
1317 }
1318 }
1319 _ => None,
1320 }
1321 }
1322
1323 fn to_f64_arg(&self, arg_opt: Option<&ResultData>, fn_name: &str) -> Result<f64, EngineError> {
1324 let val = arg_opt.ok_or_else(|| {
1325 EngineError::EvalError(EvalError::UnknownFunction(format!(
1326 "{} requires argument",
1327 fn_name
1328 )))
1329 })?;
1330 if let ResultData::Error(e) = val {
1331 return Err(EngineError::EvalError(EvalError::UnknownFunction(
1332 e.clone(),
1333 )));
1334 }
1335 self.to_f64(val).ok_or_else(|| {
1336 EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
1337 })
1338 }
1339
1340 fn find_error_in_args(args: &[ResultData]) -> Option<ResultData> {
1341 for arg in args {
1342 match arg {
1343 ResultData::Error(_) => return Some(arg.clone()),
1344 ResultData::List(list) => {
1345 if let Some(err) = Self::find_error_in_args(list) {
1346 return Some(err);
1347 }
1348 }
1349 _ => {}
1350 }
1351 }
1352 None
1353 }
1354
1355 fn check_arg_errors(&self, args: &[ResultData], is_direct: &[bool]) -> Option<ResultData> {
1356 for (i, arg) in args.iter().enumerate() {
1357 match arg {
1358 ResultData::Error(_) => return Some(arg.clone()),
1359 ResultData::List(list) => {
1360 if let Some(err) = self.check_arg_errors(list, &[]) {
1361 return Some(err);
1362 }
1363 }
1364 ResultData::String(_)
1365 if is_direct.get(i).copied().unwrap_or(false) && self.to_f64(arg).is_none() =>
1366 {
1367 return Some(ResultData::Error("#VALUE!".to_string()));
1368 }
1369 _ => {}
1370 }
1371 }
1372 None
1373 }
1374
1375 fn sum_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
1376 match arg {
1377 ResultData::Float(f) => *f,
1378 ResultData::Integer(i) => *i as f64,
1379 ResultData::Boolean(b) => {
1380 if is_direct {
1381 if *b { 1.0 } else { 0.0 }
1382 } else {
1383 0.0
1384 }
1385 }
1386 ResultData::String(_) => {
1387 if is_direct {
1388 self.to_f64(arg).unwrap_or(0.0)
1389 } else {
1390 0.0
1391 }
1392 }
1393 ResultData::List(list) => {
1394 let mut sum = 0.0;
1395 for item in list {
1396 sum += self.sum_helper(item, false);
1397 }
1398 sum
1399 }
1400 _ => 0.0,
1401 }
1402 }
1403
1404 fn flatten_finance_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1410 match arg {
1411 ResultData::Float(f) => vec![*f],
1412 ResultData::Integer(i) => vec![*i as f64],
1413 ResultData::Boolean(b) => {
1414 if is_direct {
1415 vec![if *b { 1.0 } else { 0.0 }]
1416 } else {
1417 vec![]
1418 }
1419 }
1420 ResultData::String(_) => {
1421 if is_direct {
1422 self.to_f64(arg).into_iter().collect()
1423 } else {
1424 vec![]
1425 }
1426 }
1427 ResultData::List(list) => list
1428 .iter()
1429 .flat_map(|v| self.flatten_finance_numbers(v, false))
1430 .collect(),
1431 _ => vec![],
1432 }
1433 }
1434
1435 fn flatten_stat_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1436 match arg {
1437 ResultData::Float(f) => vec![*f],
1438 ResultData::Integer(i) => vec![*i as f64],
1439 ResultData::Boolean(b) => {
1440 if is_direct {
1441 vec![if *b { 1.0 } else { 0.0 }]
1442 } else {
1443 vec![]
1444 }
1445 }
1446 ResultData::String(_) => {
1447 if is_direct {
1448 self.to_f64(arg).into_iter().collect()
1449 } else {
1450 vec![]
1451 }
1452 }
1453 ResultData::List(list) => list
1454 .iter()
1455 .flat_map(|v| self.flatten_stat_numbers(v, false))
1456 .collect(),
1457 _ => vec![],
1458 }
1459 }
1460
1461 fn flatten_positional(
1468 &self,
1469 arg: &ResultData,
1470 out: &mut Vec<Option<f64>>,
1471 first_err: &mut Option<String>,
1472 ) {
1473 match arg {
1474 ResultData::List(items) => {
1475 for item in items {
1476 self.flatten_positional(item, out, first_err);
1477 }
1478 }
1479 ResultData::Float(f) => out.push(Some(*f)),
1480 ResultData::Integer(i) => out.push(Some(*i as f64)),
1481 ResultData::Error(e) => {
1482 if first_err.is_none() {
1483 *first_err = Some(e.clone());
1484 }
1485 out.push(None);
1486 }
1487 _ => out.push(None),
1488 }
1489 }
1490
1491 fn positional_numbers(
1492 &self,
1493 arg: Option<&ResultData>,
1494 first_err: &mut Option<String>,
1495 ) -> Vec<Option<f64>> {
1496 let mut out = Vec::new();
1497 if let Some(a) = arg {
1498 self.flatten_positional(a, &mut out, first_err);
1499 }
1500 out
1501 }
1502
1503 fn pair_and_filter(
1526 xs_raw: Vec<Option<f64>>,
1527 ys_raw: Vec<Option<f64>>,
1528 ) -> Result<(Vec<f64>, Vec<f64>), String> {
1529 if xs_raw.len() != ys_raw.len() {
1530 return Err("#N/A".to_string());
1531 }
1532 let mut xs = Vec::with_capacity(xs_raw.len());
1533 let mut ys = Vec::with_capacity(ys_raw.len());
1534 for (x, y) in xs_raw.into_iter().zip(ys_raw) {
1535 if let (Some(x), Some(y)) = (x, y) {
1536 xs.push(x);
1537 ys.push(y);
1538 }
1539 }
1540 Ok((xs, ys))
1541 }
1542
1543 fn paired_args(
1545 &self,
1546 x_arg: Option<&ResultData>,
1547 y_arg: Option<&ResultData>,
1548 ) -> Result<(Vec<f64>, Vec<f64>), String> {
1549 for arg in [x_arg, y_arg].into_iter().flatten() {
1570 let scalar = match arg {
1576 ResultData::List(items) if items.len() == 1 => &items[0],
1577 other => other,
1578 };
1579 if let ResultData::Error(e) = scalar {
1580 return Err(e.clone());
1581 }
1582 if Self::is_empty_scalar_operand(arg) {
1590 return Err("#VALUE!".to_string());
1591 }
1592 }
1593 let mut first_err = None;
1594 let xs_raw = self.positional_numbers(x_arg, &mut first_err);
1595 let ys_raw = self.positional_numbers(y_arg, &mut first_err);
1596 if xs_raw.len() != ys_raw.len() {
1597 return Err("#N/A".to_string());
1598 }
1599 if let Some(e) = first_err {
1600 return Err(e);
1601 }
1602 Self::pair_and_filter(xs_raw, ys_raw)
1603 }
1604
1605 fn flatten_strict_inner(
1631 &self,
1632 arg: &ResultData,
1633 blanks: BlankPolicy,
1634 out: &mut Vec<f64>,
1635 ) -> Result<(), String> {
1636 match arg {
1637 ResultData::List(items) => {
1638 for item in items {
1639 self.flatten_strict_inner(item, blanks, out)?;
1640 }
1641 Ok(())
1642 }
1643 ResultData::Error(e) => Err(e.clone()),
1644 ResultData::Float(f) => {
1645 out.push(*f);
1646 Ok(())
1647 }
1648 ResultData::Integer(i) => {
1649 out.push(*i as f64);
1650 Ok(())
1651 }
1652 ResultData::None => match blanks {
1653 BlankPolicy::Zero => {
1654 out.push(0.0);
1655 Ok(())
1656 }
1657 BlankPolicy::Skip => Ok(()),
1658 BlankPolicy::Reject => Err("#VALUE!".to_string()),
1659 },
1660 ResultData::String(_) => match self.to_f64(arg) {
1666 Some(f) => {
1667 out.push(f);
1668 Ok(())
1669 }
1670 None => Err("#VALUE!".to_string()),
1671 },
1672 _ => Err("#VALUE!".to_string()),
1673 }
1674 }
1675
1676 fn flatten_strict_numbers(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1677 let mut out = Vec::new();
1678 self.flatten_strict_inner(arg, BlankPolicy::Zero, &mut out)?;
1679 Ok(out)
1680 }
1681
1682 fn flatten_skipping_blanks(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1684 let mut out = Vec::new();
1685 if let Some(a) = arg {
1686 self.flatten_strict_inner(a, BlankPolicy::Skip, &mut out)?;
1687 }
1688 Ok(out)
1689 }
1690
1691 fn flatten_numbers_only(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1694 let mut out = Vec::new();
1695 self.flatten_strict_inner(arg, BlankPolicy::Reject, &mut out)?;
1696 Ok(out)
1697 }
1698
1699 fn aggregate_range_number(val: &ResultData) -> Option<f64> {
1708 match val {
1709 ResultData::Float(f) => Some(*f),
1710 ResultData::Integer(i) => Some(*i as f64),
1711 _ => None,
1712 }
1713 }
1714
1715 fn flatten_numbers_only_arg(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1716 match arg {
1717 Some(a) => self.flatten_numbers_only(a),
1718 None => Ok(vec![]),
1719 }
1720 }
1721
1722 fn flatten_args_stat_numbers(
1736 &self,
1737 args: &[ResultData],
1738 is_direct: &[bool],
1739 ) -> Result<Vec<f64>, String> {
1740 let mut out = Vec::new();
1741 for (i, arg) in args.iter().enumerate() {
1742 let direct = is_direct.get(i).copied().unwrap_or(false);
1743 if direct && matches!(arg, ResultData::String(_)) && self.to_f64(arg).is_none() {
1744 return Err("#VALUE!".to_string());
1745 }
1746 out.extend(self.flatten_stat_numbers(arg, direct));
1747 }
1748 Ok(out)
1749 }
1750
1751 fn flatten_stat_numbers_a(
1768 &self,
1769 arg: &ResultData,
1770 is_direct: bool,
1771 ) -> Result<Vec<f64>, String> {
1772 Ok(match arg {
1773 ResultData::Float(f) => vec![*f],
1774 ResultData::Integer(i) => vec![*i as f64],
1775 ResultData::Boolean(b) => vec![if *b { 1.0 } else { 0.0 }],
1776 ResultData::String(_) => {
1777 if is_direct {
1778 match self.to_f64(arg) {
1779 Some(f) => vec![f],
1780 None => return Err("#VALUE!".to_string()),
1781 }
1782 } else {
1783 vec![0.0]
1784 }
1785 }
1786 ResultData::Error(e) => return Err(e.clone()),
1787 ResultData::List(list) => {
1789 let mut out = Vec::new();
1790 for v in list {
1791 out.extend(self.flatten_stat_numbers_a(v, false)?);
1792 }
1793 out
1794 }
1795 ResultData::None => vec![],
1796 _ => vec![0.0],
1797 })
1798 }
1799
1800 fn flatten_args_stat_numbers_a(
1803 &self,
1804 args: &[ResultData],
1805 is_direct: &[bool],
1806 ) -> Result<Vec<f64>, String> {
1807 let mut out = Vec::new();
1808 for (i, arg) in args.iter().enumerate() {
1809 out.extend(
1810 self.flatten_stat_numbers_a(arg, is_direct.get(i).copied().unwrap_or(false))?,
1811 );
1812 }
1813 Ok(out)
1814 }
1815
1816 fn extract_matrix(&self, arg: &ResultData) -> Vec<Vec<f64>> {
1817 match arg {
1818 ResultData::List(list) => {
1819 let mut rows = Vec::new();
1820 for item in list {
1821 match item {
1822 ResultData::List(sub_list) => {
1823 let row: Vec<f64> =
1824 sub_list.iter().flat_map(|v| self.to_f64(v)).collect();
1825 if !row.is_empty() {
1826 rows.push(row);
1827 }
1828 }
1829 _ => {
1830 if let Some(f) = self.to_f64(item) {
1831 rows.push(vec![f]);
1832 }
1833 }
1834 }
1835 }
1836 rows
1837 }
1838 _ => vec![],
1839 }
1840 }
1841
1842 fn matrix_from_arg(
1853 &self,
1854 expr: &crate::core::parser::Expr,
1855 value: &ResultData,
1856 ) -> Vec<Vec<f64>> {
1857 if let ResultData::List(items) = value
1859 && items.iter().any(|i| matches!(i, ResultData::List(_)))
1860 {
1861 return self.extract_matrix(value);
1862 }
1863 fn plain(v: &ResultData) -> Option<f64> {
1868 match v {
1869 ResultData::Float(f) => Some(*f),
1870 ResultData::Integer(i) => Some(*i as f64),
1871 _ => None,
1872 }
1873 }
1874 let items: Vec<&ResultData> = match value {
1875 ResultData::List(items) => items.iter().collect(),
1876 other => vec![other],
1877 };
1878 if items.iter().any(|v| plain(v).is_none()) {
1879 return Vec::new();
1880 }
1881 let flat: Vec<f64> = items.iter().filter_map(|v| plain(v)).collect();
1882 let cols = match Self::range_bounds(expr) {
1883 Some((_, _, start_col, _, end_col)) => end_col.saturating_sub(start_col) + 1,
1884 None => flat.len().max(1),
1885 };
1886 if cols == 0 || !flat.len().is_multiple_of(cols) {
1887 return self.extract_matrix(value);
1888 }
1889 flat.chunks(cols).map(|c| c.to_vec()).collect()
1890 }
1891
1892 fn paired_sum_has_no_numbers(&self, arg: Option<&ResultData>) -> bool {
1910 let mut ignored = None;
1911 let slots = self.positional_numbers(arg, &mut ignored);
1912 slots.iter().all(|v| v.is_none())
1913 }
1914
1915 fn is_empty_scalar_operand(arg: &ResultData) -> bool {
1929 let scalar = match arg {
1930 ResultData::List(items) if items.len() == 1 => &items[0],
1931 other => other,
1932 };
1933 matches!(scalar, ResultData::None)
1934 }
1935
1936 fn first_arg_is_boolean(args: &[ResultData]) -> bool {
1946 matches!(args.first(), Some(ResultData::Boolean(_)))
1947 }
1948
1949 fn opt_f64_arg(&self, args: &[ResultData], i: usize, default: f64) -> Result<f64, EngineError> {
1950 match args.get(i) {
1951 None => Ok(default),
1952 Some(ResultData::None) => Ok(0.0),
1957 Some(v) => self.to_f64(v).ok_or_else(|| {
1958 EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
1959 }),
1960 }
1961 }
1962
1963 fn opt_f64(&self, args: &[ResultData], i: usize, default: f64) -> f64 {
1964 args.get(i).and_then(|v| self.to_f64(v)).unwrap_or(default)
1965 }
1966
1967 fn average_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, usize) {
1968 match arg {
1969 ResultData::Float(f) => (*f, 1),
1970 ResultData::Integer(i) => (*i as f64, 1),
1971 ResultData::Boolean(b) => {
1972 if is_direct {
1973 (if *b { 1.0 } else { 0.0 }, 1)
1974 } else {
1975 (0.0, 0)
1976 }
1977 }
1978 ResultData::String(_) => {
1979 if is_direct {
1980 if let Some(f) = self.to_f64(arg) {
1981 (f, 1)
1982 } else {
1983 (0.0, 0)
1984 }
1985 } else {
1986 (0.0, 0)
1987 }
1988 }
1989 ResultData::List(list) => {
1990 let mut sum = 0.0;
1991 let mut count = 0;
1992 for item in list {
1993 let (s, c) = self.average_helper(item, false);
1994 sum += s;
1995 count += c;
1996 }
1997 (sum, count)
1998 }
1999 _ => (0.0, 0),
2000 }
2001 }
2002
2003 fn count_helper(&self, arg: &ResultData) -> usize {
2004 match arg {
2005 ResultData::Float(_) | ResultData::Integer(_) => 1,
2006 ResultData::List(list) => {
2007 let mut count = 0;
2008 for item in list {
2009 count += self.count_helper(item);
2010 }
2011 count
2012 }
2013 _ => 0,
2014 }
2015 }
2016
2017 fn min_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
2018 match arg {
2019 ResultData::Float(f) => *f,
2020 ResultData::Integer(i) => *i as f64,
2021 ResultData::Boolean(b) => {
2022 if is_direct {
2023 if *b { 1.0 } else { 0.0 }
2024 } else {
2025 f64::INFINITY
2026 }
2027 }
2028 ResultData::String(_) => {
2029 if is_direct {
2030 self.to_f64(arg).unwrap_or(f64::INFINITY)
2031 } else {
2032 f64::INFINITY
2033 }
2034 }
2035 ResultData::List(list) => {
2036 let mut min_val = f64::INFINITY;
2037 for item in list {
2038 min_val = min_val.min(self.min_helper(item, false));
2039 }
2040 min_val
2041 }
2042 _ => f64::INFINITY,
2043 }
2044 }
2045
2046 fn max_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
2047 match arg {
2048 ResultData::Float(f) => *f,
2049 ResultData::Integer(i) => *i as f64,
2050 ResultData::Boolean(b) => {
2051 if is_direct {
2052 if *b { 1.0 } else { 0.0 }
2053 } else {
2054 f64::NEG_INFINITY
2055 }
2056 }
2057 ResultData::String(_) => {
2058 if is_direct {
2059 self.to_f64(arg).unwrap_or(f64::NEG_INFINITY)
2060 } else {
2061 f64::NEG_INFINITY
2062 }
2063 }
2064 ResultData::List(list) => {
2065 let mut max_val = f64::NEG_INFINITY;
2066 for item in list {
2067 max_val = max_val.max(self.max_helper(item, false));
2068 }
2069 max_val
2070 }
2071 _ => f64::NEG_INFINITY,
2072 }
2073 }
2074
2075 fn concat_helper(&self, arg: &ResultData, out: &mut String) {
2076 match arg {
2077 ResultData::List(list) => {
2078 for item in list {
2079 self.concat_helper(item, out);
2080 }
2081 }
2082 other => {
2083 out.push_str(&other.to_string());
2084 }
2085 }
2086 }
2087
2088 fn counta_helper(&self, arg: &ResultData) -> usize {
2089 match arg {
2090 ResultData::None => 0,
2091 ResultData::List(list) => {
2095 let mut count = 0;
2096 for item in list {
2097 count += self.counta_helper(item);
2098 }
2099 count
2100 }
2101 _ => 1,
2102 }
2103 }
2104
2105 fn product_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, bool) {
2106 match arg {
2107 ResultData::Float(f) => (*f, true),
2108 ResultData::Integer(i) => (*i as f64, true),
2109 ResultData::Boolean(b) => {
2110 if is_direct {
2111 (if *b { 1.0 } else { 0.0 }, true)
2112 } else {
2113 (1.0, false)
2114 }
2115 }
2116 ResultData::String(_) => {
2117 if is_direct {
2118 if let Some(f) = self.to_f64(arg) {
2119 (f, true)
2120 } else {
2121 (1.0, false)
2122 }
2123 } else {
2124 (1.0, false)
2125 }
2126 }
2127 ResultData::List(list) => {
2128 let mut prod = 1.0;
2129 let mut has_nums = false;
2130 for item in list {
2131 let (p, h) = self.product_helper(item, false);
2132 if h {
2133 prod *= p;
2136 has_nums = true;
2137 }
2138 }
2139 (prod, has_nums)
2140 }
2141 _ => (1.0, false),
2142 }
2143 }
2144
2145 fn to_bool_opt(&self, val: &ResultData) -> Option<bool> {
2146 match val {
2147 ResultData::Boolean(b) => Some(*b),
2148 ResultData::Integer(i) => Some(*i != 0),
2149 ResultData::Float(f) => Some(*f != 0.0),
2150 ResultData::String(s) => {
2151 let s_trim = s.trim();
2152 if s_trim.eq_ignore_ascii_case("true") {
2153 Some(true)
2154 } else if s_trim.eq_ignore_ascii_case("false") {
2155 Some(false)
2156 } else if let Ok(f) = s_trim.parse::<f64>() {
2157 Some(f != 0.0)
2158 } else {
2159 None
2160 }
2161 }
2162 ResultData::None => Some(false),
2163 _ => None,
2164 }
2165 }
2166
2167 fn to_bool(&self, val: &ResultData) -> bool {
2168 self.to_bool_opt(val).unwrap_or(false)
2169 }
2170
2171 fn range_numeric(val: &ResultData) -> Option<f64> {
2181 match val {
2182 ResultData::Integer(i) => Some(*i as f64),
2183 ResultData::Float(f) => Some(*f),
2184 _ => None,
2185 }
2186 }
2187
2188 fn exact_lookup_matches(lookup: &ResultData, candidate: &ResultData) -> bool {
2198 if matches!(candidate, ResultData::None) {
2199 return false;
2200 }
2201 let lookup_key = match lookup {
2202 ResultData::None => "0".to_string(),
2203 other => other.to_string(),
2204 };
2205 candidate.to_string() == lookup_key
2206 }
2207
2208 fn match_criteria(&self, val: &ResultData, criteria: &ResultData) -> bool {
2209 let crit_str = criteria.to_string();
2210 if let Some(rest) = crit_str.strip_prefix(">=") {
2211 let val_f = match Self::range_numeric(val) {
2217 Some(f) => f,
2218 None => return false,
2219 };
2220 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2221 val_f >= crit_f
2222 } else if let Some(rest) = crit_str.strip_prefix('>') {
2223 let val_f = match Self::range_numeric(val) {
2224 Some(f) => f,
2225 None => return false,
2226 };
2227 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2228 val_f > crit_f
2229 } else if let Some(rest) = crit_str.strip_prefix("<>") {
2230 let remainder = rest.trim().to_string();
2231 val.to_string() != remainder
2232 } else if let Some(rest) = crit_str.strip_prefix("<=") {
2233 let val_f = match Self::range_numeric(val) {
2234 Some(f) => f,
2235 None => return false,
2236 };
2237 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2238 val_f <= crit_f
2239 } else if let Some(rest) = crit_str.strip_prefix('<') {
2240 let val_f = match Self::range_numeric(val) {
2241 Some(f) => f,
2242 None => return false,
2243 };
2244 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2245 val_f < crit_f
2246 } else if let Some(rest) = crit_str.strip_prefix('=') {
2247 let remainder = rest.trim().to_string();
2248 val.to_string() == remainder
2249 } else {
2250 val.to_string() == crit_str
2251 }
2252 }
2253
2254 fn range_bounds(
2260 expr: &crate::core::parser::Expr,
2261 ) -> Option<(Option<String>, usize, usize, usize, usize)> {
2262 use crate::core::parser::Expr;
2263 match expr {
2264 Expr::RangeRef {
2265 sheet,
2266 start_row,
2267 start_col,
2268 end_row,
2269 end_col,
2270 ..
2271 } => Some((sheet.clone(), *start_row, *start_col, *end_row, *end_col)),
2272 Expr::CellRef {
2273 sheet, row, col, ..
2274 } => Some((sheet.clone(), *row, *col, *row, *col)),
2275 _ => None,
2276 }
2277 }
2278
2279 fn materialize_range(
2286 &self,
2287 sheet_opt: &Option<String>,
2288 start_row: usize,
2289 start_col: usize,
2290 end_row: usize,
2291 end_col: usize,
2292 context: Option<&Context>,
2293 ) -> Option<Vec<Vec<ResultData>>> {
2294 let is_self = match sheet_opt {
2295 Some(name) => name == &self.name,
2296 None => true,
2297 };
2298 let source: &Sheet = if is_self {
2299 self
2300 } else {
2301 context?.sheets.get(sheet_opt.as_ref()?)?
2302 };
2303 let actual_end_row = if end_row == usize::MAX {
2304 source.row_count().saturating_sub(1)
2305 } else {
2306 end_row
2307 };
2308 if actual_end_row < start_row || end_col < start_col {
2309 return Some(Vec::new());
2310 }
2311 let mut grid = Vec::with_capacity(actual_end_row - start_row + 1);
2312 for r in start_row..=actual_end_row {
2313 let mut row = Vec::with_capacity(end_col - start_col + 1);
2314 for c in start_col..=end_col {
2315 row.push(source.get_result_data(&CellRef::new(r, c)));
2316 }
2317 grid.push(row);
2318 }
2319 Some(grid)
2320 }
2321
2322 fn evaluate_database_function(
2336 &self,
2337 func_name: &str,
2338 args: &[crate::core::parser::Expr],
2339 evaluated_args: &[ResultData],
2340 context: Option<&Context>,
2341 ) -> Result<ResultData, EngineError> {
2342 if args.len() < 3 || evaluated_args.len() < 3 {
2343 return Ok(ResultData::Error("#VALUE!".to_string()));
2344 }
2345 let (db_sheet, db_sr, db_sc, db_er, db_ec) = match Self::range_bounds(&args[0]) {
2346 Some(v) => v,
2347 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2348 };
2349 let (crit_sheet, crit_sr, crit_sc, crit_er, crit_ec) = match Self::range_bounds(&args[2]) {
2350 Some(v) => v,
2351 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2352 };
2353 let db = match self.materialize_range(&db_sheet, db_sr, db_sc, db_er, db_ec, context) {
2354 Some(g) => g,
2355 None => return Ok(ResultData::Error("#REF!".to_string())),
2356 };
2357 let crit = match self.materialize_range(
2358 &crit_sheet,
2359 crit_sr,
2360 crit_sc,
2361 crit_er,
2362 crit_ec,
2363 context,
2364 ) {
2365 Some(g) => g,
2366 None => return Ok(ResultData::Error("#REF!".to_string())),
2367 };
2368 if db.len() < 2 || crit.len() < 2 {
2369 return Ok(ResultData::Error("#VALUE!".to_string()));
2370 }
2371
2372 let db_headers: Vec<String> = db[0].iter().map(|v| v.to_string()).collect();
2373 let field_idx: usize = match &evaluated_args[1] {
2374 ResultData::String(s) => {
2375 match db_headers.iter().position(|h| h.eq_ignore_ascii_case(s)) {
2376 Some(idx) => idx,
2377 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2378 }
2379 }
2380 other => match self.to_f64(other) {
2381 Some(n) if n >= 1.0 && (n as usize) <= db_headers.len() => n as usize - 1,
2382 _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2383 },
2384 };
2385
2386 let crit_headers: Vec<String> = crit[0].iter().map(|v| v.to_string()).collect();
2387 let crit_to_db: Vec<Option<usize>> = crit_headers
2388 .iter()
2389 .map(|h| db_headers.iter().position(|dh| dh.eq_ignore_ascii_case(h)))
2390 .collect();
2391
2392 let mut matched: Vec<ResultData> = Vec::new();
2393 for row in db.iter().skip(1) {
2394 let row_matches_any_criteria_row = crit.iter().skip(1).any(|crit_row| {
2395 crit_row.iter().enumerate().all(|(ci, cell)| {
2396 if matches!(cell, ResultData::None) {
2397 return true;
2398 }
2399 match crit_to_db.get(ci).copied().flatten() {
2400 Some(db_col) => self.match_criteria(&row[db_col], cell),
2401 None => false,
2402 }
2403 })
2404 });
2405 if row_matches_any_criteria_row {
2406 matched.push(row[field_idx].clone());
2407 }
2408 }
2409
2410 match func_name {
2411 "DGET" => match matched.len() {
2412 0 => Ok(ResultData::Error("#VALUE!".to_string())),
2413 1 => Ok(matched.into_iter().next().unwrap()),
2414 _ => Ok(ResultData::Error("#NUM!".to_string())),
2415 },
2416 "DCOUNT" => Ok(ResultData::Float(
2417 matched
2418 .iter()
2419 .filter(|v| Self::range_numeric(v).is_some())
2420 .count() as f64,
2421 )),
2422 "DCOUNTA" => Ok(ResultData::Float(
2423 matched.iter().map(|v| self.counta_helper(v)).sum::<usize>() as f64,
2424 )),
2425 _ => {
2426 let nums: Vec<f64> = matched.iter().filter_map(Self::range_numeric).collect();
2427 match func_name {
2428 "DSUM" => Ok(ResultData::Float(nums.iter().sum())),
2429 "DPRODUCT" => Ok(ResultData::Float(if nums.is_empty() {
2430 0.0
2431 } else {
2432 nums.iter().product()
2433 })),
2434 "DMAX" => {
2435 let m = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
2436 Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2437 }
2438 "DMIN" => {
2439 let m = nums.iter().cloned().fold(f64::INFINITY, f64::min);
2440 Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2441 }
2442 "DAVERAGE" => {
2443 if nums.is_empty() {
2444 Ok(ResultData::Error("#DIV/0!".to_string()))
2445 } else {
2446 Ok(ResultData::Float(
2447 nums.iter().sum::<f64>() / nums.len() as f64,
2448 ))
2449 }
2450 }
2451 "DSTDEV" => match crate::core::stats::stdev_s(&nums) {
2452 Ok(v) => Ok(ResultData::Float(v)),
2453 Err(e) => Ok(ResultData::Error(e)),
2454 },
2455 "DSTDEVP" => match crate::core::stats::stdev_p(&nums) {
2456 Ok(v) => Ok(ResultData::Float(v)),
2457 Err(e) => Ok(ResultData::Error(e)),
2458 },
2459 "DVAR" => match crate::core::stats::var_s(&nums) {
2460 Ok(v) => Ok(ResultData::Float(v)),
2461 Err(e) => Ok(ResultData::Error(e)),
2462 },
2463 "DVARP" => match crate::core::stats::var_p(&nums) {
2464 Ok(v) => Ok(ResultData::Float(v)),
2465 Err(e) => Ok(ResultData::Error(e)),
2466 },
2467 _ => unreachable!(),
2468 }
2469 }
2470 }
2471 }
2472
2473 fn proper(&self, s: &str) -> String {
2474 let mut c_chars = Vec::new();
2480 let mut capitalize_next = true;
2481 for c in s.chars() {
2482 if c.is_alphabetic() {
2483 if capitalize_next {
2484 c_chars.extend(c.to_uppercase());
2485 } else {
2486 c_chars.extend(c.to_lowercase());
2487 }
2488 capitalize_next = false;
2489 } else {
2490 c_chars.push(c);
2491 capitalize_next = true;
2492 }
2493 }
2494 c_chars.into_iter().collect()
2495 }
2496
2497 fn get_ymd_hms(&self) -> ((i32, u32, u32), (u32, u32, u32)) {
2498 let now = web_time::SystemTime::now()
2499 .duration_since(web_time::SystemTime::UNIX_EPOCH)
2500 .unwrap_or_default()
2501 .as_secs();
2502 let secs_in_day = 86400;
2503 let days_since_epoch = (now / secs_in_day) as i32;
2504 let seconds_of_day = (now % secs_in_day) as u32;
2505
2506 let hour = seconds_of_day / 3600;
2507 let minute = (seconds_of_day % 3600) / 60;
2508 let second = seconds_of_day % 60;
2509
2510 let era = (if days_since_epoch >= -719468 {
2511 days_since_epoch + 719468
2512 } else {
2513 days_since_epoch + 719468 - 146096
2514 }) / 146097;
2515 let doe = (days_since_epoch + 719468 - era * 146097) as u32;
2516 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
2517 let y = (yoe as i32) + era * 400;
2518 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2519 let mp = (5 * doy + 2) / 153;
2520 let d = doy - (153 * mp + 2) / 5 + 1;
2521 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2522 let year = if m <= 2 { y + 1 } else { y };
2523
2524 ((year, m, d), (hour, minute, second))
2525 }
2526
2527 fn evaluate_let(
2534 &self,
2535 args: &[crate::core::parser::Expr],
2536 context: Option<&Context>,
2537 row: Option<usize>,
2538 col: Option<usize>,
2539 deps: &mut Vec<Dependency>,
2540 scope: &LetScope<'_>,
2541 ) -> Result<ResultData, EngineError> {
2542 use crate::core::parser::Expr;
2543
2544 if args.is_empty() || args.len().is_multiple_of(2) {
2545 return Ok(ResultData::Error("#VALUE!".to_string()));
2548 }
2549 if args.len() == 1 {
2550 return self.evaluate_ast(&args[0], context, row, col, deps, scope);
2551 }
2552
2553 let name = match &args[0] {
2554 Expr::Identifier(n) => n.as_str(),
2555 _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2556 };
2557 let remaining_pairs = args.len() / 2 - 1;
2560 let is_duplicate = args[2..]
2561 .iter()
2562 .step_by(2)
2563 .take(remaining_pairs)
2564 .any(|a| matches!(a, Expr::Identifier(n2) if n2.eq_ignore_ascii_case(name)));
2565 if is_duplicate {
2566 return Ok(ResultData::Error("#VALUE!".to_string()));
2567 }
2568
2569 let value = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
2570 let inner_scope = LetScope::Bound {
2571 name,
2572 value: &value,
2573 parent: scope,
2574 };
2575 self.evaluate_let(&args[2..], context, row, col, deps, &inner_scope)
2576 }
2577
2578 fn extract_lambda(
2587 expr: &crate::core::parser::Expr,
2588 ) -> Option<(Vec<&str>, &crate::core::parser::Expr)> {
2589 use crate::core::parser::Expr;
2590 let Expr::FunctionCall { name, args } = expr else {
2591 return None;
2592 };
2593 if !name.eq_ignore_ascii_case("LAMBDA") || args.is_empty() {
2594 return None;
2595 }
2596 let (body, params) = args.split_last().unwrap();
2597 let param_names: Vec<&str> = params
2598 .iter()
2599 .filter_map(|p| match p {
2600 Expr::Identifier(n) => Some(n.as_str()),
2601 _ => None,
2602 })
2603 .collect();
2604 if param_names.len() != params.len() {
2605 return None;
2606 }
2607 Some((param_names, body))
2608 }
2609
2610 #[allow(clippy::too_many_arguments)]
2616 fn invoke_lambda<'v>(
2617 &self,
2618 params: &[&str],
2619 values: &'v [ResultData],
2620 body: &crate::core::parser::Expr,
2621 context: Option<&Context>,
2622 row: Option<usize>,
2623 col: Option<usize>,
2624 deps: &mut Vec<Dependency>,
2625 scope: &LetScope<'v>,
2626 ) -> Result<ResultData, EngineError> {
2627 match (params.split_first(), values.split_first()) {
2628 (Some((&pname, prest)), Some((vfirst, vrest))) => {
2629 let inner_scope = LetScope::Bound {
2630 name: pname,
2631 value: vfirst,
2632 parent: scope,
2633 };
2634 self.invoke_lambda(prest, vrest, body, context, row, col, deps, &inner_scope)
2635 }
2636 _ => self.evaluate_ast(body, context, row, col, deps, scope),
2637 }
2638 }
2639
2640 fn eval_as_array(
2644 &self,
2645 expr: &crate::core::parser::Expr,
2646 context: Option<&Context>,
2647 row: Option<usize>,
2648 col: Option<usize>,
2649 deps: &mut Vec<Dependency>,
2650 scope: &LetScope<'_>,
2651 ) -> Result<Vec<ResultData>, EngineError> {
2652 Ok(
2653 match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2654 ResultData::List(items) => Self::flatten_row_major(items).0,
2655 other => vec![other],
2656 },
2657 )
2658 }
2659
2660 fn flatten_row_major(items: Vec<ResultData>) -> (Vec<ResultData>, Option<usize>) {
2670 if !items.is_empty() && items.iter().all(|v| matches!(v, ResultData::List(_))) {
2671 let cols = match &items[0] {
2672 ResultData::List(inner) => inner.len().max(1),
2673 _ => 1,
2674 };
2675 let flat = items
2676 .into_iter()
2677 .flat_map(|v| match v {
2678 ResultData::List(inner) => inner,
2679 other => vec![other],
2680 })
2681 .collect();
2682 (flat, Some(cols))
2683 } else {
2684 (items, None)
2685 }
2686 }
2687
2688 fn array_shape(
2696 &self,
2697 expr: &crate::core::parser::Expr,
2698 context: Option<&Context>,
2699 row: Option<usize>,
2700 col: Option<usize>,
2701 deps: &mut Vec<Dependency>,
2702 scope: &LetScope<'_>,
2703 ) -> Result<(Vec<ResultData>, usize), EngineError> {
2704 use crate::core::parser::Expr;
2705 let items = match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2706 ResultData::List(items) => items,
2707 other => vec![other],
2708 };
2709 let (flat, nested_cols) = Self::flatten_row_major(items);
2710 if let Some(cols) = nested_cols {
2711 return Ok((flat, cols));
2712 }
2713 let num_cols = match expr {
2714 Expr::RangeRef {
2715 start_col, end_col, ..
2716 } => (end_col - start_col + 1).max(1),
2717 Expr::CellRef { .. } => 1,
2718 Expr::FunctionCall { name, args } => self
2719 .function_call_cols(name, args, context, row, col, deps, scope)
2720 .unwrap_or_else(|| flat.len().max(1)),
2721 _ => flat.len().max(1),
2722 };
2723 Ok((flat, num_cols))
2724 }
2725
2726 #[allow(clippy::too_many_arguments)]
2735 fn function_call_cols(
2736 &self,
2737 name: &str,
2738 args: &[crate::core::parser::Expr],
2739 context: Option<&Context>,
2740 row: Option<usize>,
2741 col: Option<usize>,
2742 deps: &mut Vec<Dependency>,
2743 scope: &LetScope<'_>,
2744 ) -> Option<usize> {
2745 let mut upper = name.to_ascii_uppercase();
2746 if let Some(rest) = upper.strip_prefix("_XLFN.") {
2747 upper = rest.to_string();
2748 }
2749 if let Some(rest) = upper.strip_prefix("_XLWS.") {
2750 upper = rest.to_string();
2751 }
2752 match upper.as_str() {
2753 "TRANSPOSE" => {
2754 let (flat, cols) = self
2755 .array_shape(args.first()?, context, row, col, deps, scope)
2756 .ok()?;
2757 Some((flat.len().checked_div(cols).unwrap_or(0)).max(1))
2758 }
2759 "HSTACK" => {
2760 let mut total = 0usize;
2761 for a in args {
2762 total += self.array_shape(a, context, row, col, deps, scope).ok()?.1;
2763 }
2764 Some(total)
2765 }
2766 "VSTACK" => {
2767 let mut max_cols = 0usize;
2768 for a in args {
2769 max_cols =
2770 max_cols.max(self.array_shape(a, context, row, col, deps, scope).ok()?.1);
2771 }
2772 Some(max_cols)
2773 }
2774 "CHOOSEROWS" => Some(
2775 self.array_shape(args.first()?, context, row, col, deps, scope)
2776 .ok()?
2777 .1,
2778 ),
2779 "CHOOSECOLS" => Some(args.len().saturating_sub(1).max(1)),
2780 "DROP" | "TAKE" => {
2781 let (_, cols) = self
2782 .array_shape(args.first()?, context, row, col, deps, scope)
2783 .ok()?;
2784 let is_take = upper == "TAKE";
2785 match args.get(2) {
2786 Some(e) => {
2787 let n = self
2788 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2789 .unwrap_or(0.0) as isize;
2790 let (s, e2) = Self::drop_take_bounds(cols as isize, n, is_take);
2791 Some((e2 - s).max(0) as usize)
2792 }
2793 None => Some(if is_take { cols } else { 0 }),
2794 }
2795 }
2796 "EXPAND" => {
2797 let (_, cols) = self
2798 .array_shape(args.first()?, context, row, col, deps, scope)
2799 .ok()?;
2800 match args.get(2) {
2801 Some(e) => Some(
2802 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2803 .unwrap_or(cols as f64) as usize,
2804 ),
2805 None => Some(cols),
2806 }
2807 }
2808 "TOCOL" => Some(1),
2809 "WRAPROWS" => {
2810 let n = self
2811 .to_f64(
2812 &self
2813 .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2814 .ok()?,
2815 )
2816 .unwrap_or(1.0)
2817 .max(1.0) as usize;
2818 Some(n)
2819 }
2820 "WRAPCOLS" => {
2821 let (flat, _) = self
2822 .array_shape(args.first()?, context, row, col, deps, scope)
2823 .ok()?;
2824 let wrap = self
2825 .to_f64(
2826 &self
2827 .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2828 .ok()?,
2829 )
2830 .unwrap_or(1.0)
2831 .max(1.0) as usize;
2832 Some(flat.len().div_ceil(wrap).max(1))
2833 }
2834 "UNIQUE" | "SORT" | "SORTBY" | "FILTER" | "TRIMRANGE" => Some(
2835 self.array_shape(args.first()?, context, row, col, deps, scope)
2836 .ok()?
2837 .1,
2838 ),
2839 "SEQUENCE" => match args.get(1) {
2840 Some(e) => Some(
2841 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2842 .unwrap_or(1.0)
2843 .max(1.0) as usize,
2844 ),
2845 None => Some(1),
2846 },
2847 "MUNIT" => {
2848 let n = self
2849 .to_f64(
2850 &self
2851 .evaluate_ast(args.first()?, context, row, col, deps, scope)
2852 .ok()?,
2853 )
2854 .unwrap_or(1.0)
2855 .max(1.0) as usize;
2856 Some(n)
2857 }
2858 "MAKEARRAY" => match args.get(1) {
2859 Some(e) => Some(
2860 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2861 .unwrap_or(1.0)
2862 .max(1.0) as usize,
2863 ),
2864 None => Some(1),
2865 },
2866 _ => None,
2867 }
2868 }
2869
2870 fn drop_take_bounds(total: isize, n: isize, is_take: bool) -> (isize, isize) {
2874 let n = n.clamp(-total, total);
2875 if is_take {
2876 if n >= 0 { (0, n) } else { (total + n, total) }
2877 } else if n >= 0 {
2878 (n, total)
2879 } else {
2880 (0, total + n)
2881 }
2882 }
2883
2884 #[allow(clippy::too_many_arguments)]
2893 fn evaluate_lambda_function(
2894 &self,
2895 func_name: &str,
2896 args: &[crate::core::parser::Expr],
2897 context: Option<&Context>,
2898 row: Option<usize>,
2899 col: Option<usize>,
2900 deps: &mut Vec<Dependency>,
2901 scope: &LetScope<'_>,
2902 ) -> Result<ResultData, EngineError> {
2903 use crate::core::parser::Expr;
2904
2905 match func_name {
2906 "MAP" => {
2907 if args.len() < 2 {
2908 return Ok(ResultData::Error("#VALUE!".to_string()));
2909 }
2910 let (lambda_expr, array_exprs) = args.split_last().unwrap();
2911 let Some((params, body)) = Self::extract_lambda(lambda_expr) else {
2912 return Ok(ResultData::Error("#VALUE!".to_string()));
2913 };
2914 if params.len() != array_exprs.len() {
2915 return Ok(ResultData::Error("#VALUE!".to_string()));
2916 }
2917 let arrays: Vec<Vec<ResultData>> = array_exprs
2918 .iter()
2919 .map(|e| self.eval_as_array(e, context, row, col, deps, scope))
2920 .collect::<Result<_, _>>()?;
2921 let len = arrays.iter().map(|a| a.len()).max().unwrap_or(0);
2922 let mut results = Vec::with_capacity(len);
2923 for i in 0..len {
2924 let values: Vec<ResultData> = arrays
2925 .iter()
2926 .map(|a| a.get(i).cloned().unwrap_or(ResultData::None))
2927 .collect();
2928 results.push(
2929 self.invoke_lambda(¶ms, &values, body, context, row, col, deps, scope)?,
2930 );
2931 }
2932 Ok(ResultData::List(results))
2933 }
2934 "BYROW" | "BYCOL" => {
2935 if args.len() != 2 {
2936 return Ok(ResultData::Error("#VALUE!".to_string()));
2937 }
2938 let Some((params, body)) = Self::extract_lambda(&args[1]) else {
2939 return Ok(ResultData::Error("#VALUE!".to_string()));
2940 };
2941 if params.len() != 1 {
2942 return Ok(ResultData::Error("#VALUE!".to_string()));
2943 }
2944 let num_cols = match &args[0] {
2948 Expr::RangeRef {
2949 start_col, end_col, ..
2950 } => (end_col - start_col + 1).max(1),
2951 _ => 1,
2952 };
2953 let flat = self.eval_as_array(&args[0], context, row, col, deps, scope)?;
2954 let num_rows = if num_cols == 0 {
2955 0
2956 } else {
2957 flat.len().div_ceil(num_cols)
2958 };
2959 let mut results = Vec::new();
2960 if func_name == "BYROW" {
2961 for r in 0..num_rows {
2962 let row_vals: Vec<ResultData> = (0..num_cols)
2963 .filter_map(|c| flat.get(r * num_cols + c).cloned())
2964 .collect();
2965 let arg = vec![ResultData::List(row_vals)];
2966 results.push(
2967 self.invoke_lambda(
2968 ¶ms, &arg, body, context, row, col, deps, scope,
2969 )?,
2970 );
2971 }
2972 } else {
2973 for c in 0..num_cols {
2974 let col_vals: Vec<ResultData> = (0..num_rows)
2975 .filter_map(|r| flat.get(r * num_cols + c).cloned())
2976 .collect();
2977 let arg = vec![ResultData::List(col_vals)];
2978 results.push(
2979 self.invoke_lambda(
2980 ¶ms, &arg, body, context, row, col, deps, scope,
2981 )?,
2982 );
2983 }
2984 }
2985 Ok(ResultData::List(results))
2986 }
2987 "REDUCE" | "SCAN" => {
2988 if args.len() != 2 && args.len() != 3 {
2997 return Ok(ResultData::Error("#VALUE!".to_string()));
2998 }
2999 let lambda_idx = args.len() - 1;
3000 let array_idx = args.len() - 2;
3001 let Some((params, body)) = Self::extract_lambda(&args[lambda_idx]) else {
3002 return Ok(ResultData::Error("#VALUE!".to_string()));
3003 };
3004 if params.len() != 2 {
3005 return Ok(ResultData::Error("#VALUE!".to_string()));
3006 }
3007 let array = self.eval_as_array(&args[array_idx], context, row, col, deps, scope)?;
3008 let (mut acc, rest, mut history): (ResultData, &[ResultData], Vec<ResultData>) =
3014 if args.len() == 3 {
3015 let init = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
3016 (init, &array[..], Vec::new())
3017 } else {
3018 match array.split_first() {
3019 Some((first, rest)) => (first.clone(), rest, vec![first.clone()]),
3020 None => return Ok(ResultData::Error("#VALUE!".to_string())),
3021 }
3022 };
3023 for item in rest {
3024 let call_args = [acc.clone(), item.clone()];
3025 acc = self
3026 .invoke_lambda(¶ms, &call_args, body, context, row, col, deps, scope)?;
3027 history.push(acc.clone());
3028 }
3029 if func_name == "REDUCE" {
3030 Ok(acc)
3031 } else {
3032 Ok(ResultData::List(history))
3033 }
3034 }
3035 "MAKEARRAY" => {
3036 if args.len() != 3 {
3037 return Ok(ResultData::Error("#VALUE!".to_string()));
3038 }
3039 let Some((params, body)) = Self::extract_lambda(&args[2]) else {
3040 return Ok(ResultData::Error("#VALUE!".to_string()));
3041 };
3042 if params.len() != 2 {
3043 return Ok(ResultData::Error("#VALUE!".to_string()));
3044 }
3045 let rows_val = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
3046 let cols_val = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
3047 let num_rows = self.to_f64(&rows_val).unwrap_or(0.0).max(0.0) as usize;
3048 let num_cols = self.to_f64(&cols_val).unwrap_or(0.0).max(0.0) as usize;
3049 let mut results = Vec::with_capacity(num_rows * num_cols);
3050 for r in 1..=num_rows {
3051 for c in 1..=num_cols {
3052 let call_args = [ResultData::Float(r as f64), ResultData::Float(c as f64)];
3053 results.push(self.invoke_lambda(
3054 ¶ms, &call_args, body, context, row, col, deps, scope,
3055 )?);
3056 }
3057 }
3058 Ok(ResultData::List(results))
3059 }
3060 _ => unreachable!(),
3061 }
3062 }
3063
3064 fn parse_a1_reference(text: &str) -> Option<(Option<String>, usize, usize, usize, usize)> {
3073 let text = text.trim();
3074 let (sheet_part, ref_part) = match text.rfind('!') {
3075 Some(idx) => (Some(&text[..idx]), &text[idx + 1..]),
3076 None => (None, text),
3077 };
3078 let sheet = sheet_part.map(|s| s.trim().trim_matches('\'').to_string());
3079
3080 fn parse_cell(s: &str) -> Option<(usize, usize)> {
3081 let s = s.replace('$', "");
3082 let col_end = s.find(|c: char| c.is_ascii_digit())?;
3083 let (col_str, row_str) = s.split_at(col_end);
3084 if col_str.is_empty() || row_str.is_empty() {
3085 return None;
3086 }
3087 let mut col = 0usize;
3088 for ch in col_str.chars() {
3089 if !ch.is_ascii_alphabetic() {
3090 return None;
3091 }
3092 col = col * 26 + (ch.to_ascii_uppercase() as usize - 'A' as usize + 1);
3093 }
3094 let row: usize = row_str.parse().ok()?;
3095 if row == 0 || col == 0 {
3096 return None;
3097 }
3098 Some((row - 1, col - 1))
3099 }
3100
3101 if let Some((start, end)) = ref_part.split_once(':') {
3102 let (r1, c1) = parse_cell(start)?;
3103 let (r2, c2) = parse_cell(end)?;
3104 Some((sheet, r1.min(r2), c1.min(c2), r1.max(r2), c1.max(c2)))
3105 } else {
3106 let (r, c) = parse_cell(ref_part)?;
3107 Some((sheet, r, c, r, c))
3108 }
3109 }
3110
3111 fn read_cell_with_deps(
3118 &self,
3119 sheet_opt: &Option<String>,
3120 r: usize,
3121 c: usize,
3122 context: Option<&Context>,
3123 deps: &mut Vec<Dependency>,
3124 ) -> ResultData {
3125 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3126 if is_self {
3127 deps.push(Dependency::Local(CellRef::new(r, c)));
3128 self.get_result_data(&CellRef::new(r, c))
3129 } else if let Some(ctx) = context {
3130 let name = sheet_opt.clone().unwrap();
3131 deps.push(Dependency::Remote {
3132 sheet: name.clone(),
3133 cell: CellRef::new(r, c),
3134 });
3135 ctx.sheets
3136 .get(&name)
3137 .map(|s| s.get_result_data(&CellRef::new(r, c)))
3138 .unwrap_or(ResultData::None)
3139 } else {
3140 ResultData::None
3141 }
3142 }
3143
3144 #[allow(clippy::too_many_arguments)]
3153 fn evaluate_range_info_function(
3154 &self,
3155 func_name: &str,
3156 args: &[crate::core::parser::Expr],
3157 context: Option<&Context>,
3158 row: Option<usize>,
3159 col: Option<usize>,
3160 deps: &mut Vec<Dependency>,
3161 scope: &LetScope<'_>,
3162 ) -> Result<ResultData, EngineError> {
3163 use crate::core::parser::Expr;
3164
3165 match func_name {
3166 "ROW" => match args.first() {
3167 Some(arg) => match Self::range_bounds(arg) {
3172 Some((_, start_row, _, end_row, _)) if end_row > start_row => {
3173 Ok(ResultData::List(
3174 (start_row..=end_row)
3175 .map(|r| ResultData::Float((r + 1) as f64))
3176 .collect(),
3177 ))
3178 }
3179 Some((_, start_row, _, _, _)) => Ok(ResultData::Float((start_row + 1) as f64)),
3180 None => Ok(ResultData::Error("#VALUE!".to_string())),
3181 },
3182 None => match row {
3183 Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3184 None => Ok(ResultData::Error("#VALUE!".to_string())),
3185 },
3186 },
3187 "COLUMN" => match args.first() {
3188 Some(arg) => match Self::range_bounds(arg) {
3191 Some((_, _, start_col, _, end_col)) if end_col > start_col => {
3192 Ok(ResultData::List(
3193 (start_col..=end_col)
3194 .map(|c| ResultData::Float((c + 1) as f64))
3195 .collect(),
3196 ))
3197 }
3198 Some((_, _, start_col, _, _)) => Ok(ResultData::Float((start_col + 1) as f64)),
3199 None => Ok(ResultData::Error("#VALUE!".to_string())),
3200 },
3201 None => match col {
3202 Some(c) => Ok(ResultData::Float((c + 1) as f64)),
3203 None => Ok(ResultData::Error("#VALUE!".to_string())),
3204 },
3205 },
3206 "ROWS" => {
3207 let Some(arg) = args.first() else {
3208 return Ok(ResultData::Error("#VALUE!".to_string()));
3209 };
3210 let Some((sheet_opt, start_row, _, end_row, _)) = Self::range_bounds(arg) else {
3211 return Ok(ResultData::Error("#VALUE!".to_string()));
3212 };
3213 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3214 let actual_end_row = if end_row == usize::MAX {
3215 if is_self {
3216 self.row_count().saturating_sub(1)
3217 } else {
3218 context
3219 .and_then(|ctx| sheet_opt.as_ref().and_then(|n| ctx.sheets.get(n)))
3220 .map(|s| s.row_count().saturating_sub(1))
3221 .unwrap_or(0)
3222 }
3223 } else {
3224 end_row
3225 };
3226 Ok(ResultData::Float(
3227 (actual_end_row.saturating_sub(start_row) + 1) as f64,
3228 ))
3229 }
3230 "COLUMNS" => {
3231 let Some(arg) = args.first() else {
3232 return Ok(ResultData::Error("#VALUE!".to_string()));
3233 };
3234 match Self::range_bounds(arg) {
3235 Some((_, _, start_col, _, end_col)) => Ok(ResultData::Float(
3236 (end_col.saturating_sub(start_col) + 1) as f64,
3237 )),
3238 None => Ok(ResultData::Error("#VALUE!".to_string())),
3239 }
3240 }
3241 "AREAS" => {
3242 if args.is_empty() {
3246 Ok(ResultData::Error("#VALUE!".to_string()))
3247 } else {
3248 Ok(ResultData::Float(1.0))
3249 }
3250 }
3251 "ISREF" => Ok(ResultData::Boolean(matches!(
3252 args.first(),
3253 Some(Expr::CellRef { .. } | Expr::RangeRef { .. } | Expr::StructuredRef { .. })
3254 ))),
3255 "FORMULATEXT" | "ISFORMULA" => {
3256 let Some(arg) = args.first() else {
3257 return Ok(ResultData::Error("#VALUE!".to_string()));
3258 };
3259 let Some((sheet_opt, r, c, _, _)) = Self::range_bounds(arg) else {
3260 return Ok(ResultData::Error("#VALUE!".to_string()));
3261 };
3262 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3263 let src = if is_self {
3264 deps.push(Dependency::Local(CellRef::new(r, c)));
3265 self.get_src_str(&CellRef::new(r, c))
3266 } else if let Some(ctx) = context {
3267 let name = sheet_opt.unwrap();
3268 deps.push(Dependency::Remote {
3269 sheet: name.clone(),
3270 cell: CellRef::new(r, c),
3271 });
3272 ctx.sheets
3273 .get(&name)
3274 .map(|s| s.get_src_str(&CellRef::new(r, c)))
3275 .unwrap_or_default()
3276 } else {
3277 String::new()
3278 };
3279 let is_formula = src.starts_with('=');
3280 if func_name == "ISFORMULA" {
3281 Ok(ResultData::Boolean(is_formula))
3282 } else if is_formula {
3283 Ok(ResultData::String(src))
3284 } else {
3285 Ok(ResultData::Error("#N/A".to_string()))
3286 }
3287 }
3288 "SHEETS" => Ok(ResultData::Float(
3289 context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3290 )),
3291 "SHEET" => {
3292 let sheet_name = match args.first() {
3298 None => Some(self.name.clone()),
3299 Some(arg) => match Self::range_bounds(arg) {
3300 Some((sheet_opt, ..)) => {
3301 Some(sheet_opt.unwrap_or_else(|| self.name.clone()))
3302 }
3303 None => self
3304 .evaluate_ast(arg, context, row, col, deps, scope)
3305 .ok()
3306 .map(|v| v.to_string()),
3307 },
3308 };
3309
3310 match sheet_name {
3311 Some(name) => {
3312 let ordinal = context
3313 .and_then(|c| {
3314 c.sheet_order
3315 .iter()
3316 .position(|n| n.eq_ignore_ascii_case(&name))
3317 })
3318 .map(|i| i + 1)
3319 .unwrap_or(1);
3325 Ok(ResultData::Float(ordinal as f64))
3326 }
3327 None => Ok(ResultData::Error("#N/A".to_string())),
3328 }
3329 }
3330 "CELL" => {
3331 if args.is_empty() {
3332 return Ok(ResultData::Error("#VALUE!".to_string()));
3333 }
3334 let info_type = self
3335 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3336 .to_string()
3337 .to_lowercase();
3338 let bounds = args.get(1).and_then(Self::range_bounds);
3339 match info_type.as_str() {
3340 "row" => match bounds.map(|b| b.1).or(row) {
3341 Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3342 None => Ok(ResultData::Error("#VALUE!".to_string())),
3343 },
3344 "col" => match bounds {
3345 Some((_, _, c, _, _)) => Ok(ResultData::Float((c + 1) as f64)),
3346 None => Ok(ResultData::Error("#VALUE!".to_string())),
3347 },
3348 "address" => match bounds {
3349 Some((_, r, c, _, _)) => Ok(ResultData::String(format!(
3350 "${}${}",
3351 crate::core::parser::col_idx_to_letters(c),
3352 r + 1
3353 ))),
3354 None => Ok(ResultData::Error("#VALUE!".to_string())),
3355 },
3356 "contents" => match bounds {
3357 Some((sheet_opt, r, c, _, _)) => {
3358 Ok(self.read_cell_with_deps(&sheet_opt, r, c, context, deps))
3359 }
3360 None => Ok(ResultData::Error("#VALUE!".to_string())),
3361 },
3362 _ => Ok(ResultData::Error("#VALUE!".to_string())),
3363 }
3364 }
3365 "INFO" => {
3366 if args.is_empty() {
3367 return Ok(ResultData::Error("#VALUE!".to_string()));
3368 }
3369 let info_type = self
3370 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3371 .to_string()
3372 .to_lowercase();
3373 match info_type.as_str() {
3374 "numfile" => Ok(ResultData::Float(
3375 context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3376 )),
3377 "release" => Ok(ResultData::String("16.0".to_string())),
3378 "system" => Ok(ResultData::String(
3379 if cfg!(target_os = "macos") {
3380 "mac"
3381 } else {
3382 "pcdos"
3383 }
3384 .to_string(),
3385 )),
3386 _ => Ok(ResultData::Error("#VALUE!".to_string())),
3387 }
3388 }
3389 "INDIRECT" => {
3390 if args.is_empty() {
3391 return Ok(ResultData::Error("#VALUE!".to_string()));
3392 }
3393 let text = self
3394 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3395 .to_string();
3396 let a1_style = match args.get(1) {
3397 Some(a) => self.to_bool(&self.evaluate_ast(a, context, row, col, deps, scope)?),
3398 None => true,
3399 };
3400 if !a1_style {
3401 return Ok(ResultData::Error("#VALUE!".to_string()));
3403 }
3404 match Self::parse_a1_reference(&text) {
3405 Some((sheet_opt, start_row, start_col, end_row, end_col)) => {
3406 if start_row == end_row && start_col == end_col {
3407 Ok(self.read_cell_with_deps(
3408 &sheet_opt, start_row, start_col, context, deps,
3409 ))
3410 } else {
3411 match self.materialize_range(
3412 &sheet_opt, start_row, start_col, end_row, end_col, context,
3413 ) {
3414 Some(grid) => {
3415 Ok(ResultData::List(grid.into_iter().flatten().collect()))
3416 }
3417 None => Ok(ResultData::Error("#REF!".to_string())),
3418 }
3419 }
3420 }
3421 None => Ok(ResultData::Error("#REF!".to_string())),
3422 }
3423 }
3424 "OFFSET" => {
3425 if args.len() < 3 {
3426 return Ok(ResultData::Error("#VALUE!".to_string()));
3427 }
3428 let Some((sheet_opt, base_row, base_col, base_end_row, base_end_col)) =
3429 Self::range_bounds(&args[0])
3430 else {
3431 return Ok(ResultData::Error("#VALUE!".to_string()));
3432 };
3433 let row_offset = self
3434 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3435 .unwrap_or(0.0) as isize;
3436 let col_offset = self
3437 .to_f64(&self.evaluate_ast(&args[2], context, row, col, deps, scope)?)
3438 .unwrap_or(0.0) as isize;
3439 let base_height = (base_end_row.saturating_sub(base_row) + 1) as isize;
3440 let base_width = (base_end_col.saturating_sub(base_col) + 1) as isize;
3441 let height = match args.get(3) {
3442 Some(a) => self
3443 .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3444 .unwrap_or(base_height as f64) as isize,
3445 None => base_height,
3446 };
3447 let width = match args.get(4) {
3448 Some(a) => self
3449 .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3450 .unwrap_or(base_width as f64) as isize,
3451 None => base_width,
3452 };
3453 let new_row = base_row as isize + row_offset;
3454 let new_col = base_col as isize + col_offset;
3455 if new_row < 0 || new_col < 0 || height <= 0 || width <= 0 {
3456 return Ok(ResultData::Error("#REF!".to_string()));
3457 }
3458 let (start_row, start_col) = (new_row as usize, new_col as usize);
3459 let (end_row, end_col) = (
3460 start_row + (height - 1) as usize,
3461 start_col + (width - 1) as usize,
3462 );
3463 if start_row == end_row && start_col == end_col {
3464 Ok(self.read_cell_with_deps(&sheet_opt, start_row, start_col, context, deps))
3465 } else {
3466 match self.materialize_range(
3467 &sheet_opt, start_row, start_col, end_row, end_col, context,
3468 ) {
3469 Some(grid) => Ok(ResultData::List(grid.into_iter().flatten().collect())),
3470 None => Ok(ResultData::Error("#REF!".to_string())),
3471 }
3472 }
3473 }
3474 _ => unreachable!(),
3475 }
3476 }
3477
3478 fn evaluate_getpivotdata(
3485 &self,
3486 args: &[crate::core::parser::Expr],
3487 context: Option<&Context>,
3488 row: Option<usize>,
3489 col: Option<usize>,
3490 deps: &mut Vec<Dependency>,
3491 scope: &LetScope<'_>,
3492 ) -> Result<ResultData, EngineError> {
3493 if args.len() < 2 || !(args.len() - 2).is_multiple_of(2) {
3494 return Ok(ResultData::Error("#VALUE!".to_string()));
3495 }
3496
3497 let data_field = self
3498 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3499 .to_string();
3500
3501 let (sheet_opt, target_row, target_col, _, _) = match Self::range_bounds(&args[1]) {
3502 Some(bounds) => bounds,
3503 None => return Ok(ResultData::Error("#REF!".to_string())),
3504 };
3505 self.read_cell_with_deps(&sheet_opt, target_row, target_col, context, deps);
3508
3509 let sheet_id = match &sheet_opt {
3510 None => self.id,
3511 Some(name) if name == &self.name => self.id,
3512 Some(name) => match context.and_then(|c| c.sheets.get(name)) {
3513 Some(s) => s.id,
3514 None => return Ok(ResultData::Error("#REF!".to_string())),
3515 },
3516 };
3517
3518 let pivot_tables = context.map(|c| c.pivot_tables).unwrap_or(&[]);
3519 let pivot = match pivot_tables.iter().find(|p| {
3520 p.dest_sheet_id == sheet_id
3521 && p.last_output_end_row
3522 .is_some_and(|end| target_row >= p.dest_row && target_row <= end)
3523 && p.last_output_end_col
3524 .is_some_and(|end| target_col >= p.dest_col && target_col <= end)
3525 }) {
3526 Some(p) => p,
3527 None => return Ok(ResultData::Error("#REF!".to_string())),
3528 };
3529
3530 let mut criteria: Vec<(String, String)> = Vec::new();
3531 let mut i = 2;
3532 while i < args.len() {
3533 let field = self
3534 .evaluate_ast(&args[i], context, row, col, deps, scope)?
3535 .to_string();
3536 let item = self
3537 .evaluate_ast(&args[i + 1], context, row, col, deps, scope)?
3538 .to_string();
3539 criteria.push((field, item));
3540 i += 2;
3541 }
3542
3543 let mut sheet_refs: Vec<&Sheet> = context
3544 .map(|c| c.sheets.values().copied().collect())
3545 .unwrap_or_default();
3546 sheet_refs.push(self);
3547
3548 match crate::core::pivot::getpivotdata(&sheet_refs, pivot, &data_field, &criteria) {
3549 Ok(v) => Ok(v),
3550 Err(e) => Ok(ResultData::Error(e)),
3551 }
3552 }
3553
3554 #[allow(clippy::too_many_arguments)]
3569 fn evaluate_array_reshape_function(
3570 &self,
3571 func_name: &str,
3572 args: &[crate::core::parser::Expr],
3573 context: Option<&Context>,
3574 row: Option<usize>,
3575 col: Option<usize>,
3576 deps: &mut Vec<Dependency>,
3577 scope: &LetScope<'_>,
3578 ) -> Result<ResultData, EngineError> {
3579 match func_name {
3580 "TRANSPOSE" => {
3581 let Some(arg) = args.first() else {
3582 return Ok(ResultData::Error("#VALUE!".to_string()));
3583 };
3584 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3585 let rows = flat.len().checked_div(cols).unwrap_or(0);
3586 let mut result = Vec::with_capacity(flat.len());
3587 for c in 0..cols {
3588 for r in 0..rows {
3589 result.push(flat[r * cols + c].clone());
3590 }
3591 }
3592 Ok(ResultData::List(result))
3593 }
3594 "HSTACK" | "VSTACK" => {
3595 if args.is_empty() {
3596 return Ok(ResultData::Error("#VALUE!".to_string()));
3597 }
3598 let mut shapes = Vec::with_capacity(args.len());
3599 for a in args {
3600 shapes.push(self.array_shape(a, context, row, col, deps, scope)?);
3601 }
3602 let mut result = Vec::new();
3603 if func_name == "HSTACK" {
3604 let max_rows = shapes
3605 .iter()
3606 .map(|(f, c)| if *c == 0 { 0 } else { f.len() / c })
3607 .max()
3608 .unwrap_or(0);
3609 for r in 0..max_rows {
3610 for (flat, cols) in &shapes {
3611 let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3612 for c in 0..*cols {
3613 result.push(if r < rows {
3614 flat[r * cols + c].clone()
3615 } else {
3616 ResultData::Error("#N/A".to_string())
3617 });
3618 }
3619 }
3620 }
3621 } else {
3622 let max_cols = shapes.iter().map(|(_, c)| *c).max().unwrap_or(0);
3623 for (flat, cols) in &shapes {
3624 let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3625 for r in 0..rows {
3626 for c in 0..max_cols {
3627 result.push(if c < *cols {
3628 flat[r * cols + c].clone()
3629 } else {
3630 ResultData::Error("#N/A".to_string())
3631 });
3632 }
3633 }
3634 }
3635 }
3636 Ok(ResultData::List(result))
3637 }
3638 "CHOOSEROWS" | "CHOOSECOLS" => {
3639 if args.len() < 2 {
3640 return Ok(ResultData::Error("#VALUE!".to_string()));
3641 }
3642 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3643 let rows = flat.len().checked_div(cols).unwrap_or(0);
3644 let total = if func_name == "CHOOSEROWS" {
3645 rows
3646 } else {
3647 cols
3648 } as isize;
3649 let mut indices = Vec::with_capacity(args.len() - 1);
3650 for idx_expr in &args[1..] {
3651 let n = self
3652 .to_f64(&self.evaluate_ast(idx_expr, context, row, col, deps, scope)?)
3653 .unwrap_or(0.0) as isize;
3654 let real_idx = if n < 0 { total + n } else { n - 1 };
3655 if real_idx < 0 || real_idx >= total {
3656 return Ok(ResultData::Error("#VALUE!".to_string()));
3657 }
3658 indices.push(real_idx as usize);
3659 }
3660 let mut result = Vec::new();
3661 if func_name == "CHOOSEROWS" {
3662 for r in indices {
3663 for c in 0..cols {
3664 result.push(flat[r * cols + c].clone());
3665 }
3666 }
3667 } else {
3668 for r in 0..rows {
3669 for &c in &indices {
3670 result.push(flat[r * cols + c].clone());
3671 }
3672 }
3673 }
3674 Ok(ResultData::List(result))
3675 }
3676 "DROP" | "TAKE" => {
3677 if args.len() < 2 {
3678 return Ok(ResultData::Error("#VALUE!".to_string()));
3679 }
3680 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3681 let num_rows = flat.len().checked_div(cols).unwrap_or(0) as isize;
3682 let is_take = func_name == "TAKE";
3683 let rows_n = self
3684 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3685 .unwrap_or(0.0) as isize;
3686 let cols_n = match args.get(2) {
3687 Some(e) => self
3688 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3689 .unwrap_or(0.0) as isize,
3690 None => {
3691 if is_take {
3692 cols as isize
3693 } else {
3694 0
3695 }
3696 }
3697 };
3698 let (row_start, row_end) = Self::drop_take_bounds(num_rows, rows_n, is_take);
3699 let (col_start, col_end) = Self::drop_take_bounds(cols as isize, cols_n, is_take);
3700 if row_start >= row_end || col_start >= col_end {
3701 return Ok(ResultData::Error("#CALC!".to_string()));
3702 }
3703 let mut result = Vec::new();
3704 for r in row_start..row_end {
3705 for c in col_start..col_end {
3706 result.push(flat[(r as usize) * cols + (c as usize)].clone());
3707 }
3708 }
3709 Ok(ResultData::List(result))
3710 }
3711 "EXPAND" => {
3712 if args.len() < 2 {
3713 return Ok(ResultData::Error("#VALUE!".to_string()));
3714 }
3715 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3716 let orig_rows = flat.len().checked_div(cols).unwrap_or(0);
3717 let new_rows = self
3718 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3719 .unwrap_or(orig_rows as f64) as usize;
3720 let new_cols = match args.get(2) {
3721 Some(e) => self
3722 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3723 .unwrap_or(cols as f64) as usize,
3724 None => cols,
3725 };
3726 let pad = match args.get(3) {
3727 Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3728 None => ResultData::Error("#N/A".to_string()),
3729 };
3730 if new_rows < orig_rows || new_cols < cols {
3731 return Ok(ResultData::Error("#VALUE!".to_string()));
3732 }
3733 let mut result = Vec::with_capacity(new_rows * new_cols);
3734 for r in 0..new_rows {
3735 for c in 0..new_cols {
3736 result.push(if r < orig_rows && c < cols {
3737 flat[r * cols + c].clone()
3738 } else {
3739 pad.clone()
3740 });
3741 }
3742 }
3743 Ok(ResultData::List(result))
3744 }
3745 "TOCOL" | "TOROW" => {
3746 let Some(arg) = args.first() else {
3747 return Ok(ResultData::Error("#VALUE!".to_string()));
3748 };
3749 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3750 let rows = flat.len().checked_div(cols).unwrap_or(0);
3751 let ignore = match args.get(1) {
3752 Some(e) => self
3753 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3754 .unwrap_or(0.0) as i64,
3755 None => 0,
3756 };
3757 let scan_by_col = match args.get(2) {
3758 Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3759 None => false,
3760 };
3761 let ordered: Vec<ResultData> = if scan_by_col {
3762 let mut v = Vec::with_capacity(flat.len());
3763 for c in 0..cols {
3764 for r in 0..rows {
3765 v.push(flat[r * cols + c].clone());
3766 }
3767 }
3768 v
3769 } else {
3770 flat
3771 };
3772 let filtered: Vec<ResultData> = ordered
3773 .into_iter()
3774 .filter(|v| match ignore {
3775 1 => !matches!(v, ResultData::None),
3776 2 => !matches!(v, ResultData::Error(_)),
3777 3 => !matches!(v, ResultData::None | ResultData::Error(_)),
3778 _ => true,
3779 })
3780 .collect();
3781 Ok(ResultData::List(filtered))
3782 }
3783 "WRAPROWS" | "WRAPCOLS" => {
3784 if args.len() < 2 {
3785 return Ok(ResultData::Error("#VALUE!".to_string()));
3786 }
3787 let (flat, _cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3788 let wrap = self
3789 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3790 .unwrap_or(1.0)
3791 .max(1.0) as usize;
3792 let pad = match args.get(2) {
3793 Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3794 None => ResultData::Error("#N/A".to_string()),
3795 };
3796 if func_name == "WRAPROWS" {
3797 let mut result = flat;
3800 let rem = result.len() % wrap;
3801 if rem != 0 {
3802 result.extend(std::iter::repeat_n(pad, wrap - rem));
3803 }
3804 Ok(ResultData::List(result))
3805 } else {
3806 let num_result_cols = flat.len().div_ceil(wrap).max(1);
3807 let total = wrap * num_result_cols;
3808 let mut result = Vec::with_capacity(total);
3809 for i in 0..total {
3810 let col = i / wrap;
3811 let r = i % wrap;
3812 let target = r * num_result_cols + col;
3813 while result.len() <= target {
3814 result.push(pad.clone());
3815 }
3816 if i < flat.len() {
3817 result[target] = flat[i].clone();
3818 }
3819 }
3820 Ok(ResultData::List(result))
3821 }
3822 }
3823 "UNIQUE" => {
3824 let Some(arg) = args.first() else {
3825 return Ok(ResultData::Error("#VALUE!".to_string()));
3826 };
3827 let (flat, _cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3828 let exactly_once = match args.get(2) {
3829 Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3830 None => false,
3831 };
3832 let mut seen: Vec<(String, ResultData, usize)> = Vec::new();
3833 for v in &flat {
3834 let key = v.to_string();
3835 match seen.iter_mut().find(|(k, ..)| k == &key) {
3836 Some(entry) => entry.2 += 1,
3837 None => seen.push((key, v.clone(), 1)),
3838 }
3839 }
3840 let result: Vec<ResultData> = seen
3841 .into_iter()
3842 .filter(|(_, _, count)| !exactly_once || *count == 1)
3843 .map(|(_, v, _)| v)
3844 .collect();
3845 Ok(ResultData::List(result))
3846 }
3847 "SORT" => {
3848 let Some(arg) = args.first() else {
3849 return Ok(ResultData::Error("#VALUE!".to_string()));
3850 };
3851 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3852 let rows = flat.len().checked_div(cols).unwrap_or(0);
3853 let sort_index = match args.get(1) {
3854 Some(e) => self
3855 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3856 .unwrap_or(1.0) as usize,
3857 None => 1,
3858 };
3859 let sort_order = match args.get(2) {
3860 Some(e) => self
3861 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3862 .unwrap_or(1.0),
3863 None => 1.0,
3864 };
3865 let col_idx = sort_index.saturating_sub(1).min(cols.saturating_sub(1));
3866 let mut row_indices: Vec<usize> = (0..rows).collect();
3867 row_indices.sort_by(|&a, &b| {
3868 Self::sort_compare_blanks_last(
3869 &flat[a * cols + col_idx],
3870 &flat[b * cols + col_idx],
3871 sort_order,
3872 )
3873 });
3874 let mut result = Vec::with_capacity(flat.len());
3875 for r in row_indices {
3876 for c in 0..cols {
3877 result.push(flat[r * cols + c].clone());
3878 }
3879 }
3880 Ok(ResultData::List(result))
3881 }
3882 "SORTBY" => {
3883 if args.len() < 2 {
3884 return Ok(ResultData::Error("#VALUE!".to_string()));
3885 }
3886 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3887 let rows = flat.len().checked_div(cols).unwrap_or(0);
3888 let by = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
3889 let order = match args.get(2) {
3890 Some(e) => self
3891 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3892 .unwrap_or(1.0),
3893 None => 1.0,
3894 };
3895 let mut row_indices: Vec<usize> = (0..rows).collect();
3896 row_indices.sort_by(|&a, &b| {
3897 let va = by.get(a).cloned().unwrap_or(ResultData::None);
3898 let vb = by.get(b).cloned().unwrap_or(ResultData::None);
3899 Self::sort_compare_blanks_last(&va, &vb, order)
3900 });
3901 let mut result = Vec::with_capacity(flat.len());
3902 for r in row_indices {
3903 for c in 0..cols {
3904 result.push(flat[r * cols + c].clone());
3905 }
3906 }
3907 Ok(ResultData::List(result))
3908 }
3909 "FILTER" => {
3910 if args.len() < 2 {
3911 return Ok(ResultData::Error("#VALUE!".to_string()));
3912 }
3913 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3914 let rows = flat.len().checked_div(cols).unwrap_or(0);
3915 let include = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
3916 let mut result = Vec::new();
3917 for r in 0..rows {
3918 let keep = include.get(r).map(|v| self.to_bool(v)).unwrap_or(false);
3919 if keep {
3920 for c in 0..cols {
3921 result.push(flat[r * cols + c].clone());
3922 }
3923 }
3924 }
3925 if result.is_empty() {
3926 match args.get(2) {
3927 Some(e) => Ok(self.evaluate_ast(e, context, row, col, deps, scope)?),
3928 None => Ok(ResultData::Error("#CALC!".to_string())),
3929 }
3930 } else {
3931 Ok(ResultData::List(result))
3932 }
3933 }
3934 "TRIMRANGE" => {
3935 let Some(arg) = args.first() else {
3936 return Ok(ResultData::Error("#VALUE!".to_string()));
3937 };
3938 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3939 let rows = flat.len().checked_div(cols).unwrap_or(0);
3940 let is_blank = |v: &ResultData| {
3941 matches!(v, ResultData::None)
3942 || matches!(v, ResultData::String(s) if s.is_empty())
3943 };
3944 let row_blank = |r: usize| (0..cols).all(|c| is_blank(&flat[r * cols + c]));
3945 let col_blank = |c: usize| (0..rows).all(|r| is_blank(&flat[r * cols + c]));
3946 let mut r_start = 0;
3947 while r_start < rows && row_blank(r_start) {
3948 r_start += 1;
3949 }
3950 let mut r_end = rows;
3951 while r_end > r_start && row_blank(r_end - 1) {
3952 r_end -= 1;
3953 }
3954 let mut c_start = 0;
3955 while c_start < cols && col_blank(c_start) {
3956 c_start += 1;
3957 }
3958 let mut c_end = cols;
3959 while c_end > c_start && col_blank(c_end - 1) {
3960 c_end -= 1;
3961 }
3962 let mut result = Vec::new();
3963 for r in r_start..r_end {
3964 for c in c_start..c_end {
3965 result.push(flat[r * cols + c].clone());
3966 }
3967 }
3968 Ok(ResultData::List(result))
3969 }
3970 _ => unreachable!(),
3971 }
3972 }
3973
3974 pub fn get_src(&self, cell: &CellRef) -> Option<&String> {
3981 let col = self.columns.get(cell.col);
3982 if let Some(col) = col {
3983 col.src.get(cell.row)
3984 } else {
3985 None
3986 }
3987 }
3988
3989 pub fn get_src_str(&self, cell: &CellRef) -> String {
3992 let col = self.columns.get(cell.col);
3993 if let Some(col) = col {
3994 col.src.get(cell.row).cloned().unwrap_or("".to_string())
3995 } else {
3996 "".to_string()
3997 }
3998 }
3999
4000 pub fn get_src_str_ref(&self, cell: &CellRef) -> Option<&str> {
4002 let col = self.columns.get(cell.col)?;
4003 col.src.get(cell.row).map(|s| s.as_str())
4004 }
4005
4006 pub fn get_word_boundaries(&self, cell: &CellRef, char_offset: usize) -> (usize, usize) {
4010 let text = self.get_src_str(cell);
4011 get_word_boundaries_from_str(&text, char_offset)
4012 }
4013}