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() {
358 ResultData::None
359 } else if src.starts_with('"') && src.ends_with('"') && src.len() >= 2 {
360 ResultData::String(src[1..src.len() - 1].to_string())
361 } else if let Ok(i) = src.trim().parse::<i64>() {
362 ResultData::Integer(i)
363 } else if let Ok(f) = src.trim().parse::<f64>()
364 && f.is_finite()
372 {
373 ResultData::Float(f)
374 } else if crate::core::engine::result_data::is_excel_error_code(src) {
375 ResultData::Error(src.to_uppercase())
378 } else if src.eq_ignore_ascii_case("true") {
379 ResultData::Boolean(true)
380 } else if src.eq_ignore_ascii_case("false") {
381 ResultData::Boolean(false)
382 } else if let Some((date, format)) =
383 crate::core::date::parse_date(src.trim_matches(' '))
384 {
385 detected_num_format = Some(format.to_format_code());
389 ResultData::Float(crate::core::date::date_to_excel_serial(date))
390 } else if let Some(f) = crate::core::date_fn::parse_time_fraction(src) {
391 ResultData::Float(f)
395 } else {
396 ResultData::String(src.to_string())
397 };
398 (res, vec![], None)
399 } else {
400 let compiled =
401 crate::core::parser::compile_formula(src, &tables_for_compilation);
402 let eval_src =
403 crate::core::parser::serialize_formula(&compiled, &tables_for_compilation);
404 let (res, deps) = match self.eval_with_row(
405 &eval_src,
406 context,
407 Some(cell_ref.row),
408 Some(cell_ref.col),
409 ) {
410 Ok(r) => r,
411 Err(e) => (ResultData::Error(e.to_string()), vec![]),
412 };
413 let final_res = if let ResultData::None = res {
414 ResultData::Float(0.0)
415 } else {
416 res
417 };
418 (final_res, deps, Some(compiled))
419 }
420 };
421
422 if let Some(col) = self.columns.get_mut(cell_ref.col)
424 && cell_ref.row < col.compiled_src.len()
425 {
426 col.compiled_src[cell_ref.row] = compiled_to_cache.unwrap_or_default();
427 }
428
429 if let Some(old_deps) = self.dependencies_rev.remove(&cell_ref) {
432 for provider in old_deps {
433 if let Some(dependents) = self.dependencies.get_mut(&provider) {
434 dependents.remove(&cell_ref);
435 }
436 }
437 }
438
439 if !new_deps.is_empty() {
441 let mut new_deps_set = HashSet::new();
442 for provider in new_deps {
443 new_deps_set.insert(provider.clone());
444 self.dependencies
445 .entry(provider)
446 .or_default()
447 .insert(cell_ref);
448 }
449 self.dependencies_rev.insert(cell_ref, new_deps_set);
450 }
451
452 let inherited = if detected_num_format.is_some()
457 || !matches!(result, ResultData::Float(_) | ResultData::Integer(_))
458 {
459 None
460 } else {
461 self.get_src_str_ref(&cell_ref)
462 .and_then(|src| src.strip_prefix('='))
463 .and_then(|body| crate::core::parser::parse_excel_formula(body).ok())
464 .and_then(|ast| self.inherited_date_format(&ast))
465 };
466 if let Some(code) = detected_num_format.or(inherited) {
469 let existing = self
470 .get_cell_style(cell_ref.row, cell_ref.col)
471 .and_then(|s| s.num_format.clone());
472 if existing.is_none() {
473 self.update_cell_style(cell_ref.row, cell_ref.col, |style| {
474 style.num_format = Some(code);
475 });
476 }
477 }
478
479 if let Some(col) = self.columns.get_mut(cell_ref.col)
481 && cell_ref.row < col.data.len()
482 {
483 col.data.set(cell_ref.row, result.clone());
484 updated_cells.insert(cell_ref);
485 }
486 if let Some(comp_sheet) = tables_for_compilation
487 .iter_mut()
488 .find(|s| s.name == self.name)
489 && let Some(col) = comp_sheet.columns.get_mut(cell_ref.col)
490 && cell_ref.row < col.data.len()
491 {
492 col.data.set(cell_ref.row, result);
493 }
494
495 let local_dep_key = Dependency::Local(cell_ref);
499 if let Some(dependents) = self.dependencies.get(&local_dep_key) {
500 for dependent in dependents {
501 if !queue_set.contains(dependent) {
502 queue.push_back(*dependent);
503 queue_set.insert(*dependent);
504 }
505 }
506 }
507
508 let local_col_dep_key = Dependency::LocalColumn(cell_ref.col);
510 if let Some(dependents) = self.dependencies.get(&local_col_dep_key) {
511 for dependent in dependents {
512 if !queue_set.contains(dependent) {
513 queue.push_back(*dependent);
514 queue_set.insert(*dependent);
515 }
516 }
517 }
518 }
519 if initial_queue_len > 0 {
520 log::info!(
521 "Sheet '{}' commit finished. Processed {} cell updates. Total time: {:.2?}",
522 self.name,
523 ops,
524 start_commit.elapsed()
525 );
526 }
527 Ok(updated_cells)
528 }
529
530 pub fn eval_with_row(
543 &self,
544 input: &str,
545 context: Option<&Context>,
546 row: Option<usize>,
547 col: Option<usize>,
548 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
549 if input.is_empty() {
550 return Ok((ResultData::None, vec![]));
551 }
552 if let Some(formula) = input.strip_prefix('=') {
553 self.eval_excel(formula, context, row, col)
554 } else {
555 if let Ok(i) = input.parse::<i64>() {
556 Ok((ResultData::Integer(i), vec![]))
557 } else if let Ok(f) = input.parse::<f64>() {
558 Ok((ResultData::Float(f), vec![]))
559 } else if let Ok(b) = input.parse::<bool>() {
560 Ok((ResultData::Boolean(b), vec![]))
561 } else {
562 Ok((ResultData::String(input.to_string()), vec![]))
563 }
564 }
565 }
566
567 pub fn eval(
580 &self,
581 input: &str,
582 context: Option<&Context>,
583 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
584 self.eval_with_row(input, context, None, None)
585 }
586
587 fn eval_excel(
588 &self,
589 code: &str,
590 context: Option<&Context>,
591 row: Option<usize>,
592 col: Option<usize>,
593 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
594 let ast = crate::core::parser::parse_excel_formula(code)
595 .map_err(|e| EngineError::EvalError(EvalError::UnknownFunction(e)))?;
596
597 let mut deps = Vec::new();
598 let result = match self.evaluate_ast(&ast, context, row, col, &mut deps, &LetScope::Empty) {
599 Ok(r) => r,
600 Err(EngineError::EvalError(EvalError::UnknownFunction(err_str)))
601 if err_str.starts_with('#') =>
602 {
603 ResultData::Error(err_str)
604 }
605 Err(e) => return Err(e),
606 };
607 Ok((result, deps))
608 }
609
610 fn evaluate_ast(
611 &self,
612 ast: &crate::core::parser::Expr,
613 context: Option<&Context>,
614 row: Option<usize>,
615 col: Option<usize>,
616 deps: &mut Vec<Dependency>,
617 scope: &LetScope<'_>,
618 ) -> Result<ResultData, EngineError> {
619 use crate::core::SheetSection;
620 use crate::core::parser::Expr;
621 use crate::core::parser::Op;
622
623 match ast {
624 Expr::Number(n) => Ok(ResultData::Float(*n)),
625 Expr::String(s) => Ok(ResultData::String(s.clone())),
626 Expr::Boolean(b) => Ok(ResultData::Boolean(*b)),
627 Expr::Error(code) => Ok(ResultData::Error(code.to_string())),
628 Expr::Identifier(name) => match scope.get(name) {
629 Some(val) => Ok(val.clone()),
630 None => Ok(ResultData::Error("#NAME?".to_string())),
631 },
632 Expr::StructuredRef {
633 sheet,
634 column,
635 is_this_row,
636 section,
637 } => {
638 let ref_name = match sheet {
639 Some(name) => name.clone(),
640 None => self.name.clone(),
641 };
642
643 let mut found: Option<(&Sheet, &crate::core::table::ExcelTable)> =
651 self.find_table(&ref_name).map(|t| (self, t));
652 if found.is_none()
653 && let Some(ctx) = context
654 {
655 for s in ctx.sheets.values() {
656 if let Some(t) = s.find_table(&ref_name) {
657 found = Some((s, t));
658 break;
659 }
660 }
661 }
662
663 if let Some((table_sheet, excel_table)) = found {
664 let is_self = table_sheet.name == self.name;
665 let sheet_name = table_sheet.name.clone();
666
667 let col_indices: Vec<(usize, usize)> = if let Some(col_name) = column {
669 let local = excel_table.local_column_index(col_name).ok_or_else(|| {
670 EngineError::EvalError(EvalError::UnknownFunction(format!(
671 "Column not found: {}",
672 col_name
673 )))
674 })?;
675 vec![(local, excel_table.start_col + local)]
676 } else {
677 (0..excel_table.columns.len())
678 .map(|local| (local, excel_table.start_col + local))
679 .collect()
680 };
681 let is_whole_table = column.is_none();
682
683 match section {
684 SheetSection::Headers => {
685 let names: Vec<ResultData> = col_indices
686 .iter()
687 .map(|&(local, _)| {
688 ResultData::String(
689 excel_table.columns.get(local).cloned().unwrap_or_default(),
690 )
691 })
692 .collect();
693 if is_whole_table {
694 Ok(ResultData::List(names))
695 } else {
696 Ok(names.into_iter().next().unwrap_or(ResultData::None))
697 }
698 }
699 SheetSection::Totals => {
700 if let Some(totals_row) = excel_table.totals_row() {
701 let mut results = Vec::new();
702 for &(_, col_idx) in &col_indices {
703 let cell_ref = CellRef::new(totals_row, col_idx);
704 if is_self {
705 deps.push(Dependency::Local(cell_ref));
706 } else {
707 deps.push(Dependency::Remote {
708 sheet: sheet_name.clone(),
709 cell: cell_ref,
710 });
711 }
712 results.push(table_sheet.get_result_data(&cell_ref));
713 }
714 if is_whole_table {
715 Ok(ResultData::List(results))
716 } else {
717 Ok(results.into_iter().next().unwrap_or(ResultData::None))
718 }
719 } else {
720 Ok(ResultData::None)
721 }
722 }
723 SheetSection::Data | SheetSection::All => {
724 if *is_this_row {
725 let r = row.ok_or_else(|| {
726 EngineError::EvalError(EvalError::UnknownFunction(
727 "This row reference cannot be evaluated without row context"
728 .to_string(),
729 ))
730 })?;
731 let mut results = Vec::new();
732 for &(_, col_idx) in &col_indices {
733 let cell_ref = CellRef::new(r, col_idx);
734 if is_self {
735 deps.push(Dependency::Local(cell_ref));
736 } else {
737 deps.push(Dependency::Remote {
738 sheet: sheet_name.clone(),
739 cell: cell_ref,
740 });
741 }
742 results.push(table_sheet.get_result_data(&cell_ref));
743 }
744 if is_whole_table {
745 Ok(ResultData::List(results))
746 } else {
747 Ok(results.into_iter().next().unwrap_or(ResultData::None))
748 }
749 } else {
750 let mut results = Vec::new();
764 for &(_, col_idx) in &col_indices {
765 for r in
766 excel_table.data_start_row()..=excel_table.data_end_row()
767 {
768 let cell_ref = CellRef::new(r, col_idx);
769 if is_self {
770 deps.push(Dependency::Local(cell_ref));
771 } else {
772 deps.push(Dependency::Remote {
773 sheet: sheet_name.clone(),
774 cell: cell_ref,
775 });
776 }
777 results.push(table_sheet.get_result_data(&cell_ref));
778 }
779 }
780 Ok(ResultData::List(results))
781 }
782 }
783 }
784 } else {
785 let sheet_name = ref_name;
789 let is_self = sheet_name == self.name;
790
791 let target_table = if is_self {
792 self
793 } else if let Some(ctx) = context {
794 if let Some(t) = ctx.sheets.get(&sheet_name) {
795 t
796 } else {
797 return Err(EngineError::EvalError(EvalError::UnknownFunction(
798 format!("Sheet not found: {}", sheet_name),
799 )));
800 }
801 } else {
802 return Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
803 "No context to resolve sheet reference: {}",
804 sheet_name
805 ))));
806 };
807
808 let col_indices: Vec<usize> = if let Some(col_name) = column {
812 let pos = target_table
813 .columns
814 .iter()
815 .position(|c| c.name == *col_name)
816 .ok_or_else(|| {
817 EngineError::EvalError(EvalError::UnknownFunction(format!(
818 "Column not found: {}",
819 col_name
820 )))
821 })?;
822 vec![pos]
823 } else {
824 (0..target_table.columns.len()).collect()
825 };
826 let is_whole_table = column.is_none();
827
828 match section {
829 SheetSection::Headers => {
830 let names: Vec<ResultData> = col_indices
831 .iter()
832 .map(|&idx| {
833 ResultData::String(
834 target_table
835 .columns
836 .get(idx)
837 .map(|c| c.name.clone())
838 .unwrap_or_default(),
839 )
840 })
841 .collect();
842 if is_whole_table {
843 Ok(ResultData::List(names))
844 } else {
845 Ok(names.into_iter().next().unwrap_or(ResultData::None))
846 }
847 }
848 SheetSection::Totals => Ok(ResultData::None),
849 SheetSection::Data | SheetSection::All => {
850 if *is_this_row {
851 let r = row.ok_or_else(|| {
852 EngineError::EvalError(EvalError::UnknownFunction(
853 "This row reference cannot be evaluated without row context"
854 .to_string(),
855 ))
856 })?;
857 let mut results = Vec::new();
858 for &col_idx in &col_indices {
859 let cell_ref = CellRef::new(r, col_idx);
860 if is_self {
861 deps.push(Dependency::Local(cell_ref));
862 } else {
863 deps.push(Dependency::Remote {
864 sheet: sheet_name.clone(),
865 cell: cell_ref,
866 });
867 }
868 results.push(target_table.get_result_data(&cell_ref));
869 }
870 if is_whole_table {
871 Ok(ResultData::List(results))
872 } else {
873 Ok(results.into_iter().next().unwrap_or(ResultData::None))
874 }
875 } else {
876 let mut results = Vec::new();
877 for &col_idx in &col_indices {
878 if is_self {
879 deps.push(Dependency::LocalColumn(col_idx));
880 } else {
881 deps.push(Dependency::RemoteColumn {
882 sheet: sheet_name.clone(),
883 col: col_idx,
884 });
885 }
886 for r in 0..target_table.row_count() {
887 let cell_ref = CellRef::new(r, col_idx);
888 results.push(target_table.get_result_data(&cell_ref));
889 }
890 }
891 Ok(ResultData::List(results))
892 }
893 }
894 }
895 }
896 }
897 Expr::CellRef {
898 sheet,
899 row: r_val,
900 col,
901 ..
902 } => {
903 let cell_ref = CellRef::new(*r_val, *col);
904 let is_self = match sheet {
905 Some(name) => name == &self.name,
906 None => true,
907 };
908
909 if is_self {
910 deps.push(Dependency::Local(cell_ref));
911 Ok(self.get_result_data(&cell_ref))
912 } else {
913 let name = sheet.as_ref().unwrap().clone();
914 deps.push(Dependency::Remote {
915 sheet: name.clone(),
916 cell: cell_ref,
917 });
918
919 if let Some(ctx) = context {
920 if let Some(t) = ctx.sheets.get(&name) {
921 Ok(t.get_result_data(&cell_ref))
922 } else {
923 Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
924 "Sheet not found: {}",
925 name
926 ))))
927 }
928 } else {
929 Err(EngineError::EvalError(EvalError::UnknownFunction(
930 "No context to resolve sheet reference".to_string(),
931 )))
932 }
933 }
934 }
935 Expr::RangeRef {
936 sheet,
937 start_row,
938 start_col,
939 end_row,
940 end_col,
941 ..
942 } => {
943 let is_self = match sheet {
944 Some(name) => name == &self.name,
945 None => true,
946 };
947
948 let target_sheet = if is_self {
949 Some(self)
950 } else {
951 context.and_then(|ctx| ctx.sheets.get(sheet.as_ref().unwrap()).copied())
952 };
953
954 let actual_end_row = if *end_row == usize::MAX {
955 target_sheet
956 .map(|t| t.row_count().saturating_sub(1))
957 .unwrap_or(0)
958 } else {
959 *end_row
960 };
961 let actual_end_col = if *end_col == usize::MAX {
962 target_sheet
963 .map(|t| t.col_count().saturating_sub(1))
964 .unwrap_or(0)
965 } else {
966 *end_col
967 };
968
969 let is_col_range = *end_row == usize::MAX;
970
971 let mut seen_col_deps: HashSet<usize> = HashSet::new();
987
988 let mut results = Vec::new();
989 for r in *start_row..=actual_end_row {
990 for c in *start_col..=actual_end_col {
991 let cell_ref = CellRef::new(r, c);
992 if is_self {
993 if is_col_range {
994 if seen_col_deps.insert(c) {
995 let col_dep = Dependency::LocalColumn(c);
996 if !deps.contains(&col_dep) {
997 deps.push(col_dep);
998 }
999 }
1000 } else {
1001 deps.push(Dependency::Local(cell_ref));
1002 }
1003 if row == Some(r) && col == Some(c) {
1020 results.push(ResultData::None);
1021 } else {
1022 results.push(self.get_result_data(&cell_ref));
1023 }
1024 } else {
1025 let name = sheet.as_ref().unwrap().clone();
1026 if is_col_range {
1027 if seen_col_deps.insert(c) {
1028 let col_dep = Dependency::RemoteColumn {
1029 sheet: name.clone(),
1030 col: c,
1031 };
1032 if !deps.contains(&col_dep) {
1033 deps.push(col_dep);
1034 }
1035 }
1036 } else {
1037 deps.push(Dependency::Remote {
1038 sheet: name.clone(),
1039 cell: cell_ref,
1040 });
1041 }
1042 if let Some(ctx) = context {
1043 if let Some(t) = ctx.sheets.get(&name) {
1044 results.push(t.get_result_data(&cell_ref));
1045 } else {
1046 return Err(EngineError::EvalError(
1047 EvalError::UnknownFunction(format!(
1048 "Sheet not found: {}",
1049 name
1050 )),
1051 ));
1052 }
1053 } else {
1054 return Err(EngineError::EvalError(EvalError::UnknownFunction(
1055 "No context to resolve sheet reference".to_string(),
1056 )));
1057 }
1058 }
1059 }
1060 }
1061 Ok(ResultData::List(results))
1062 }
1063 Expr::List(list) => {
1064 let mut results = Vec::new();
1065 for item in list {
1066 results.push(self.evaluate_ast(item, context, row, col, deps, scope)?);
1067 }
1068 Ok(ResultData::List(results))
1069 }
1070 Expr::UnaryOp { op, expr } => {
1071 let val = self.evaluate_ast(expr, context, row, col, deps, scope)?;
1072 match op {
1073 Op::Sub => match val {
1074 ResultData::Float(f) => Ok(ResultData::Float(-f)),
1075 ResultData::Integer(i) => Ok(ResultData::Integer(-i)),
1076 _ => Err(EngineError::EvalError(EvalError::UnknownFunction(
1077 "Unary minus expects number".to_string(),
1078 ))),
1079 },
1080 _ => Ok(val),
1081 }
1082 }
1083 Expr::BinaryOp { op, left, right } => {
1084 let l_val = self.evaluate_ast(left, context, row, col, deps, scope)?;
1085
1086 match op {
1087 Op::Eq | Op::Ne | Op::Lt | Op::Gt | Op::Le | Op::Ge => {
1088 if let ResultData::Error(_) = &l_val {
1089 return Ok(l_val);
1090 }
1091 let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1092 if let ResultData::Error(_) = &r_val {
1093 return Ok(r_val);
1094 }
1095 let ord = Self::compare_excel_values(&l_val, &r_val);
1096 let b = match op {
1097 Op::Eq => ord.is_eq(),
1098 Op::Ne => !ord.is_eq(),
1099 Op::Lt => ord.is_lt(),
1100 Op::Gt => ord.is_gt(),
1101 Op::Le => ord.is_le(),
1102 Op::Ge => ord.is_ge(),
1103 _ => unreachable!(),
1104 };
1105 Ok(ResultData::Boolean(b))
1106 }
1107 _ => {
1108 if let ResultData::Error(_) = &l_val {
1109 return Ok(l_val);
1110 }
1111 let lf = match self.to_f64(&l_val) {
1112 Some(f) => f,
1113 None => return Ok(ResultData::Error("#VALUE!".to_string())),
1114 };
1115 let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1116 if let ResultData::Error(_) = &r_val {
1117 return Ok(r_val);
1118 }
1119 let rf = match self.to_f64(&r_val) {
1120 Some(f) => f,
1121 None => return Ok(ResultData::Error("#VALUE!".to_string())),
1122 };
1123 match op {
1124 Op::Add => Ok(ResultData::Float(lf + rf)),
1125 Op::Sub => Ok(ResultData::Float(lf - rf)),
1126 Op::Mul => Ok(ResultData::Float(lf * rf)),
1127 Op::Div => {
1128 if rf == 0.0 {
1129 return Ok(ResultData::Error("#DIV/0!".to_string()));
1130 }
1131 Ok(ResultData::Float(lf / rf))
1132 }
1133 Op::Exp => {
1134 if lf == 0.0 && rf == 0.0 {
1135 return Ok(ResultData::Error("#NUM!".to_string()));
1136 }
1137 if lf == 0.0 && rf < 0.0 {
1138 return Ok(ResultData::Error("#DIV/0!".to_string()));
1139 }
1140 if lf < 0.0 {
1141 if rf.fract() != 0.0 || rf.abs() > 1e6 {
1142 return Ok(ResultData::Error("#NUM!".to_string()));
1143 }
1144 let res = lf.powi(rf as i32);
1145 if res.is_nan() || res.is_infinite() {
1146 return Ok(ResultData::Error("#NUM!".to_string()));
1147 }
1148 return Ok(ResultData::Float(res));
1149 }
1150 let res = lf.powf(rf);
1151 if res.is_nan() || res.is_infinite() {
1152 return Ok(ResultData::Error("#NUM!".to_string()));
1153 }
1154 Ok(ResultData::Float(res))
1155 }
1156 _ => unreachable!(),
1157 }
1158 }
1159 }
1160 }
1161 Expr::FunctionCall { name, args } => {
1162 self.evaluate_function(name, args, context, row, col, deps, scope)
1163 }
1164 }
1165 }
1166
1167 fn excel_type_rank(val: &ResultData) -> u8 {
1168 match val {
1169 ResultData::None => 0,
1170 ResultData::Integer(_) | ResultData::Float(_) => 1,
1171 ResultData::String(_) => 2,
1172 ResultData::Boolean(_) => 3,
1173 _ => 4,
1174 }
1175 }
1176
1177 fn compare_excel_values(l: &ResultData, r: &ResultData) -> std::cmp::Ordering {
1178 match (l, r) {
1180 (ResultData::None, ResultData::None) => return std::cmp::Ordering::Equal,
1181 (ResultData::None, ResultData::Integer(b)) => {
1182 return 0.0
1183 .partial_cmp(&(*b as f64))
1184 .unwrap_or(std::cmp::Ordering::Equal);
1185 }
1186 (ResultData::None, ResultData::Float(b)) => {
1187 return 0.0.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal);
1188 }
1189 (ResultData::Integer(a), ResultData::None) => {
1190 return (*a as f64)
1191 .partial_cmp(&0.0)
1192 .unwrap_or(std::cmp::Ordering::Equal);
1193 }
1194 (ResultData::Float(a), ResultData::None) => {
1195 return a.partial_cmp(&0.0).unwrap_or(std::cmp::Ordering::Equal);
1196 }
1197 (ResultData::None, ResultData::String(b)) => {
1198 return "".cmp(b.to_lowercase().as_str());
1199 }
1200 (ResultData::String(a), ResultData::None) => {
1201 return a.to_lowercase().as_str().cmp("");
1202 }
1203 (ResultData::None, ResultData::Boolean(b)) => {
1204 return false.cmp(b);
1205 }
1206 (ResultData::Boolean(a), ResultData::None) => {
1207 return a.cmp(&false);
1208 }
1209 _ => {}
1210 }
1211
1212 let rank_l = Self::excel_type_rank(l);
1213 let rank_r = Self::excel_type_rank(r);
1214 if rank_l != rank_r {
1215 return rank_l.cmp(&rank_r);
1216 }
1217 match (l, r) {
1218 (ResultData::Integer(a), ResultData::Integer(b)) => a.cmp(b),
1219 (ResultData::Float(a), ResultData::Float(b)) => {
1220 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
1221 }
1222 (ResultData::Integer(a), ResultData::Float(b)) => (*a as f64)
1223 .partial_cmp(b)
1224 .unwrap_or(std::cmp::Ordering::Equal),
1225 (ResultData::Float(a), ResultData::Integer(b)) => a
1226 .partial_cmp(&(*b as f64))
1227 .unwrap_or(std::cmp::Ordering::Equal),
1228 (ResultData::Boolean(a), ResultData::Boolean(b)) => a.cmp(b),
1229 (ResultData::String(a), ResultData::String(b)) => Self::compare_excel_strings(a, b),
1230 _ => std::cmp::Ordering::Equal,
1231 }
1232 }
1233
1234 fn sort_compare_blanks_last(
1244 l: &ResultData,
1245 r: &ResultData,
1246 sort_order: f64,
1247 ) -> std::cmp::Ordering {
1248 match (matches!(l, ResultData::None), matches!(r, ResultData::None)) {
1249 (true, true) => std::cmp::Ordering::Equal,
1250 (true, false) => std::cmp::Ordering::Greater,
1251 (false, true) => std::cmp::Ordering::Less,
1252 (false, false) => {
1253 let ord = Self::compare_excel_values(l, r);
1254 if sort_order < 0.0 { ord.reverse() } else { ord }
1255 }
1256 }
1257 }
1258
1259 fn is_excel_number_str(s: &str) -> bool {
1260 let s = s.trim();
1261 if s.is_empty() {
1262 return false;
1263 }
1264 let bytes = s.as_bytes();
1265 let first = bytes[0];
1266 if first == b'e' || first == b'E' {
1267 return false;
1268 }
1269 if (first == b'+' || first == b'-') && bytes.len() > 1 {
1270 let second = bytes[1];
1271 if second == b'e' || second == b'E' {
1272 return false;
1273 }
1274 }
1275 true
1276 }
1277
1278 fn compare_excel_strings(a: &str, b: &str) -> std::cmp::Ordering {
1299 let a_low = a.to_lowercase();
1300 let b_low = b.to_lowercase();
1301 let strip_hyphens = |s: &str| -> String { s.chars().filter(|&c| c != '-').collect() };
1302 let a_stripped = strip_hyphens(&a_low);
1303 let b_stripped = strip_hyphens(&b_low);
1304 let char_weight = |ch: char| -> u32 {
1305 match ch {
1306 '(' => 2,
1307 ')' => 3,
1308 _ => (ch as u32) + 10,
1309 }
1310 };
1311 for (ca, cb) in a_stripped.chars().zip(b_stripped.chars()) {
1312 if ca != cb {
1313 return char_weight(ca).cmp(&char_weight(cb));
1314 }
1315 }
1316 match a_stripped.len().cmp(&b_stripped.len()) {
1317 std::cmp::Ordering::Equal => a_low.len().cmp(&b_low.len()),
1318 other => other,
1319 }
1320 }
1321
1322 pub(crate) fn clean_float(val: f64) -> f64 {
1329 if val == 0.0 || !val.is_finite() {
1330 return val;
1331 }
1332 let abs_val = val.abs();
1333 let exp = abs_val.log10().floor() as i32;
1334 let factor = 10.0f64.powi(15 - 1 - exp);
1335 if factor.is_finite() && factor != 0.0 {
1336 let rounded = (val * factor).round() / factor;
1337 if (val - rounded).abs() <= 1e-14 * abs_val {
1338 return rounded;
1339 }
1340 }
1341 val
1342 }
1343
1344 pub(crate) fn to_f64(&self, val: &ResultData) -> Option<f64> {
1354 match val {
1355 ResultData::None => Some(0.0),
1356 ResultData::Float(f) => Some(*f),
1357 ResultData::Integer(i) => Some(*i as f64),
1358 ResultData::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
1359 ResultData::String(s) => {
1360 let s_trim = s.trim();
1361 if Self::is_excel_number_str(s_trim) {
1362 if let Ok(f) = s_trim.parse::<f64>() {
1363 return Some(f);
1364 }
1365 if let Some((date, _)) = crate::core::date::parse_date(s_trim) {
1366 return Some(crate::core::date::date_to_excel_serial(date));
1367 }
1368 None
1369 } else if let Some((date, _)) = crate::core::date::parse_date(s_trim) {
1370 Some(crate::core::date::date_to_excel_serial(date))
1371 } else {
1372 None
1373 }
1374 }
1375 _ => None,
1376 }
1377 }
1378
1379 fn to_f64_arg(&self, arg_opt: Option<&ResultData>, fn_name: &str) -> Result<f64, EngineError> {
1380 let val = arg_opt.ok_or_else(|| {
1381 EngineError::EvalError(EvalError::UnknownFunction(format!(
1382 "{} requires argument",
1383 fn_name
1384 )))
1385 })?;
1386 if let ResultData::Error(e) = val {
1387 return Err(EngineError::EvalError(EvalError::UnknownFunction(
1388 e.clone(),
1389 )));
1390 }
1391 self.to_f64(val).ok_or_else(|| {
1392 EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
1393 })
1394 }
1395
1396 fn find_error_in_args(args: &[ResultData]) -> Option<ResultData> {
1397 for arg in args {
1398 match arg {
1399 ResultData::Error(_) => return Some(arg.clone()),
1400 ResultData::List(list) => {
1401 if let Some(err) = Self::find_error_in_args(list) {
1402 return Some(err);
1403 }
1404 }
1405 _ => {}
1406 }
1407 }
1408 None
1409 }
1410
1411 fn check_arg_errors(&self, args: &[ResultData], is_direct: &[bool]) -> Option<ResultData> {
1412 for (i, arg) in args.iter().enumerate() {
1413 match arg {
1414 ResultData::Error(_) => return Some(arg.clone()),
1415 ResultData::List(list) => {
1416 if let Some(err) = self.check_arg_errors(list, &[]) {
1417 return Some(err);
1418 }
1419 }
1420 ResultData::String(_)
1421 if is_direct.get(i).copied().unwrap_or(false) && self.to_f64(arg).is_none() =>
1422 {
1423 return Some(ResultData::Error("#VALUE!".to_string()));
1424 }
1425 _ => {}
1426 }
1427 }
1428 None
1429 }
1430
1431 fn sum_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
1432 match arg {
1433 ResultData::Float(f) => *f,
1434 ResultData::Integer(i) => *i as f64,
1435 ResultData::Boolean(b) => {
1436 if is_direct {
1437 if *b { 1.0 } else { 0.0 }
1438 } else {
1439 0.0
1440 }
1441 }
1442 ResultData::String(_) => {
1443 if is_direct {
1444 self.to_f64(arg).unwrap_or(0.0)
1445 } else {
1446 0.0
1447 }
1448 }
1449 ResultData::List(list) => {
1450 let mut sum = 0.0;
1451 for item in list {
1452 sum += self.sum_helper(item, false);
1453 }
1454 sum
1455 }
1456 _ => 0.0,
1457 }
1458 }
1459
1460 fn flatten_finance_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1466 match arg {
1467 ResultData::Float(f) => vec![*f],
1468 ResultData::Integer(i) => vec![*i as f64],
1469 ResultData::Boolean(b) => {
1470 if is_direct {
1471 vec![if *b { 1.0 } else { 0.0 }]
1472 } else {
1473 vec![]
1474 }
1475 }
1476 ResultData::String(_) => {
1477 if is_direct {
1478 self.to_f64(arg).into_iter().collect()
1479 } else {
1480 vec![]
1481 }
1482 }
1483 ResultData::List(list) => list
1484 .iter()
1485 .flat_map(|v| self.flatten_finance_numbers(v, false))
1486 .collect(),
1487 _ => vec![],
1488 }
1489 }
1490
1491 fn flatten_stat_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1492 match arg {
1493 ResultData::Float(f) => vec![*f],
1494 ResultData::Integer(i) => vec![*i as f64],
1495 ResultData::Boolean(b) => {
1496 if is_direct {
1497 vec![if *b { 1.0 } else { 0.0 }]
1498 } else {
1499 vec![]
1500 }
1501 }
1502 ResultData::String(_) => {
1503 if is_direct {
1504 self.to_f64(arg).into_iter().collect()
1505 } else {
1506 vec![]
1507 }
1508 }
1509 ResultData::List(list) => list
1510 .iter()
1511 .flat_map(|v| self.flatten_stat_numbers(v, false))
1512 .collect(),
1513 _ => vec![],
1514 }
1515 }
1516
1517 fn flatten_positional(
1524 &self,
1525 arg: &ResultData,
1526 out: &mut Vec<Option<f64>>,
1527 first_err: &mut Option<String>,
1528 ) {
1529 match arg {
1530 ResultData::List(items) => {
1531 for item in items {
1532 self.flatten_positional(item, out, first_err);
1533 }
1534 }
1535 ResultData::Float(f) => out.push(Some(*f)),
1536 ResultData::Integer(i) => out.push(Some(*i as f64)),
1537 ResultData::Error(e) => {
1538 if first_err.is_none() {
1539 *first_err = Some(e.clone());
1540 }
1541 out.push(None);
1542 }
1543 _ => out.push(None),
1544 }
1545 }
1546
1547 fn positional_numbers(
1548 &self,
1549 arg: Option<&ResultData>,
1550 first_err: &mut Option<String>,
1551 ) -> Vec<Option<f64>> {
1552 let mut out = Vec::new();
1553 if let Some(a) = arg {
1554 self.flatten_positional(a, &mut out, first_err);
1555 }
1556 out
1557 }
1558
1559 fn pair_and_filter(
1582 xs_raw: Vec<Option<f64>>,
1583 ys_raw: Vec<Option<f64>>,
1584 ) -> Result<(Vec<f64>, Vec<f64>), String> {
1585 if xs_raw.len() != ys_raw.len() {
1586 return Err("#N/A".to_string());
1587 }
1588 let mut xs = Vec::with_capacity(xs_raw.len());
1589 let mut ys = Vec::with_capacity(ys_raw.len());
1590 for (x, y) in xs_raw.into_iter().zip(ys_raw) {
1591 if let (Some(x), Some(y)) = (x, y) {
1592 xs.push(x);
1593 ys.push(y);
1594 }
1595 }
1596 Ok((xs, ys))
1597 }
1598
1599 fn paired_args(
1601 &self,
1602 x_arg: Option<&ResultData>,
1603 y_arg: Option<&ResultData>,
1604 ) -> Result<(Vec<f64>, Vec<f64>), String> {
1605 for arg in [x_arg, y_arg].into_iter().flatten() {
1626 let scalar = match arg {
1632 ResultData::List(items) if items.len() == 1 => &items[0],
1633 other => other,
1634 };
1635 if let ResultData::Error(e) = scalar {
1636 return Err(e.clone());
1637 }
1638 if Self::is_empty_scalar_operand(arg) {
1646 return Err("#VALUE!".to_string());
1647 }
1648 }
1649 let mut first_err = None;
1650 let xs_raw = self.positional_numbers(x_arg, &mut first_err);
1651 let ys_raw = self.positional_numbers(y_arg, &mut first_err);
1652 if xs_raw.len() != ys_raw.len() {
1653 return Err("#N/A".to_string());
1654 }
1655 if let Some(e) = first_err {
1656 return Err(e);
1657 }
1658 Self::pair_and_filter(xs_raw, ys_raw)
1659 }
1660
1661 fn flatten_strict_inner(
1692 &self,
1693 arg: &ResultData,
1694 blanks: BlankPolicy,
1695 coerce_text: bool,
1696 out: &mut Vec<f64>,
1697 ) -> Result<(), String> {
1698 match arg {
1699 ResultData::List(items) => {
1700 for item in items {
1701 self.flatten_strict_inner(item, blanks, coerce_text, out)?;
1702 }
1703 Ok(())
1704 }
1705 ResultData::Error(e) => Err(e.clone()),
1706 ResultData::Float(f) => {
1707 out.push(*f);
1708 Ok(())
1709 }
1710 ResultData::Integer(i) => {
1711 out.push(*i as f64);
1712 Ok(())
1713 }
1714 ResultData::None => match blanks {
1715 BlankPolicy::Zero => {
1716 out.push(0.0);
1717 Ok(())
1718 }
1719 BlankPolicy::Skip => Ok(()),
1720 BlankPolicy::Reject => Err("#VALUE!".to_string()),
1721 },
1722 ResultData::String(_) if coerce_text => match self.to_f64(arg) {
1729 Some(f) => {
1730 out.push(f);
1731 Ok(())
1732 }
1733 None => Err("#VALUE!".to_string()),
1734 },
1735 _ => Err("#VALUE!".to_string()),
1736 }
1737 }
1738
1739 fn flatten_strict_numbers(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1740 let mut out = Vec::new();
1741 self.flatten_strict_inner(arg, BlankPolicy::Zero, true, &mut out)?;
1742 Ok(out)
1743 }
1744
1745 fn flatten_skipping_blanks(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1748 let mut out = Vec::new();
1749 if let Some(a) = arg {
1750 self.flatten_strict_inner(a, BlankPolicy::Skip, true, &mut out)?;
1751 }
1752 Ok(out)
1753 }
1754
1755 fn flatten_skipping_blanks_no_text_coercion(
1762 &self,
1763 arg: Option<&ResultData>,
1764 ) -> Result<Vec<f64>, String> {
1765 let mut out = Vec::new();
1766 if let Some(a) = arg {
1767 self.flatten_strict_inner(a, BlankPolicy::Skip, false, &mut out)?;
1768 }
1769 Ok(out)
1770 }
1771
1772 fn flatten_numbers_only(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1775 let mut out = Vec::new();
1776 self.flatten_strict_inner(arg, BlankPolicy::Reject, false, &mut out)?;
1777 Ok(out)
1778 }
1779
1780 fn aggregate_range_number(val: &ResultData) -> Option<f64> {
1789 match val {
1790 ResultData::Float(f) => Some(*f),
1791 ResultData::Integer(i) => Some(*i as f64),
1792 _ => None,
1793 }
1794 }
1795
1796 fn flatten_numbers_only_arg(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1797 match arg {
1798 Some(a) => self.flatten_numbers_only(a),
1799 None => Ok(vec![]),
1800 }
1801 }
1802
1803 fn flatten_args_stat_numbers(
1817 &self,
1818 args: &[ResultData],
1819 is_direct: &[bool],
1820 ) -> Result<Vec<f64>, String> {
1821 let mut out = Vec::new();
1822 for (i, arg) in args.iter().enumerate() {
1823 let direct = is_direct.get(i).copied().unwrap_or(false);
1824 if direct && matches!(arg, ResultData::String(_)) && self.to_f64(arg).is_none() {
1825 return Err("#VALUE!".to_string());
1826 }
1827 out.extend(self.flatten_stat_numbers(arg, direct));
1828 }
1829 Ok(out)
1830 }
1831
1832 fn flatten_stat_numbers_a(
1849 &self,
1850 arg: &ResultData,
1851 is_direct: bool,
1852 ) -> Result<Vec<f64>, String> {
1853 Ok(match arg {
1854 ResultData::Float(f) => vec![*f],
1855 ResultData::Integer(i) => vec![*i as f64],
1856 ResultData::Boolean(b) => vec![if *b { 1.0 } else { 0.0 }],
1857 ResultData::String(_) => {
1858 if is_direct {
1859 match self.to_f64(arg) {
1860 Some(f) => vec![f],
1861 None => return Err("#VALUE!".to_string()),
1862 }
1863 } else {
1864 vec![0.0]
1865 }
1866 }
1867 ResultData::Error(e) => return Err(e.clone()),
1868 ResultData::List(list) => {
1870 let mut out = Vec::new();
1871 for v in list {
1872 out.extend(self.flatten_stat_numbers_a(v, false)?);
1873 }
1874 out
1875 }
1876 ResultData::None => vec![],
1877 _ => vec![0.0],
1878 })
1879 }
1880
1881 fn flatten_args_stat_numbers_a(
1884 &self,
1885 args: &[ResultData],
1886 is_direct: &[bool],
1887 ) -> Result<Vec<f64>, String> {
1888 let mut out = Vec::new();
1889 for (i, arg) in args.iter().enumerate() {
1890 out.extend(
1891 self.flatten_stat_numbers_a(arg, is_direct.get(i).copied().unwrap_or(false))?,
1892 );
1893 }
1894 Ok(out)
1895 }
1896
1897 fn extract_matrix(&self, arg: &ResultData) -> Vec<Vec<f64>> {
1898 match arg {
1899 ResultData::List(list) => {
1900 let mut rows = Vec::new();
1901 for item in list {
1902 match item {
1903 ResultData::List(sub_list) => {
1904 let row: Vec<f64> =
1905 sub_list.iter().flat_map(|v| self.to_f64(v)).collect();
1906 if !row.is_empty() {
1907 rows.push(row);
1908 }
1909 }
1910 _ => {
1911 if let Some(f) = self.to_f64(item) {
1912 rows.push(vec![f]);
1913 }
1914 }
1915 }
1916 }
1917 rows
1918 }
1919 _ => vec![],
1920 }
1921 }
1922
1923 fn matrix_from_arg(
1934 &self,
1935 expr: &crate::core::parser::Expr,
1936 value: &ResultData,
1937 ) -> Vec<Vec<f64>> {
1938 if let ResultData::List(items) = value
1940 && items.iter().any(|i| matches!(i, ResultData::List(_)))
1941 {
1942 return self.extract_matrix(value);
1943 }
1944 fn plain(v: &ResultData) -> Option<f64> {
1949 match v {
1950 ResultData::Float(f) => Some(*f),
1951 ResultData::Integer(i) => Some(*i as f64),
1952 _ => None,
1953 }
1954 }
1955 let items: Vec<&ResultData> = match value {
1956 ResultData::List(items) => items.iter().collect(),
1957 other => vec![other],
1958 };
1959 if items.iter().any(|v| plain(v).is_none()) {
1960 return Vec::new();
1961 }
1962 let flat: Vec<f64> = items.iter().filter_map(|v| plain(v)).collect();
1963 let cols = match Self::range_bounds(expr) {
1964 Some((_, _, start_col, _, end_col)) => end_col.saturating_sub(start_col) + 1,
1965 None => flat.len().max(1),
1966 };
1967 if cols == 0 || !flat.len().is_multiple_of(cols) {
1968 return self.extract_matrix(value);
1969 }
1970 flat.chunks(cols).map(|c| c.to_vec()).collect()
1971 }
1972
1973 fn paired_sum_has_no_numbers(&self, arg: Option<&ResultData>) -> bool {
1991 let mut ignored = None;
1992 let slots = self.positional_numbers(arg, &mut ignored);
1993 slots.iter().all(|v| v.is_none())
1994 }
1995
1996 fn is_empty_scalar_operand(arg: &ResultData) -> bool {
2010 let scalar = match arg {
2011 ResultData::List(items) if items.len() == 1 => &items[0],
2012 other => other,
2013 };
2014 matches!(scalar, ResultData::None)
2015 }
2016
2017 fn first_arg_is_boolean(args: &[ResultData]) -> bool {
2027 matches!(args.first(), Some(ResultData::Boolean(_)))
2028 }
2029
2030 fn opt_f64_arg(&self, args: &[ResultData], i: usize, default: f64) -> Result<f64, EngineError> {
2031 match args.get(i) {
2032 None => Ok(default),
2033 Some(ResultData::None) => Ok(0.0),
2038 Some(ResultData::Error(e)) => Err(EngineError::EvalError(EvalError::UnknownFunction(
2039 e.clone(),
2040 ))),
2041 Some(v) => self.to_f64(v).ok_or_else(|| {
2042 EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
2043 }),
2044 }
2045 }
2046
2047 fn opt_f64(&self, args: &[ResultData], i: usize, default: f64) -> f64 {
2048 args.get(i).and_then(|v| self.to_f64(v)).unwrap_or(default)
2049 }
2050
2051 fn average_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, usize) {
2052 match arg {
2053 ResultData::Float(f) => (*f, 1),
2054 ResultData::Integer(i) => (*i as f64, 1),
2055 ResultData::Boolean(b) => {
2056 if is_direct {
2057 (if *b { 1.0 } else { 0.0 }, 1)
2058 } else {
2059 (0.0, 0)
2060 }
2061 }
2062 ResultData::String(_) => {
2063 if is_direct {
2064 if let Some(f) = self.to_f64(arg) {
2065 (f, 1)
2066 } else {
2067 (0.0, 0)
2068 }
2069 } else {
2070 (0.0, 0)
2071 }
2072 }
2073 ResultData::List(list) => {
2074 let mut sum = 0.0;
2075 let mut count = 0;
2076 for item in list {
2077 let (s, c) = self.average_helper(item, false);
2078 sum += s;
2079 count += c;
2080 }
2081 (sum, count)
2082 }
2083 _ => (0.0, 0),
2084 }
2085 }
2086
2087 fn count_helper(&self, arg: &ResultData) -> usize {
2088 match arg {
2089 ResultData::Float(_) | ResultData::Integer(_) => 1,
2090 ResultData::List(list) => {
2091 let mut count = 0;
2092 for item in list {
2093 count += self.count_helper(item);
2094 }
2095 count
2096 }
2097 _ => 0,
2098 }
2099 }
2100
2101 fn min_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
2102 match arg {
2103 ResultData::Float(f) => *f,
2104 ResultData::Integer(i) => *i as f64,
2105 ResultData::Boolean(b) => {
2106 if is_direct {
2107 if *b { 1.0 } else { 0.0 }
2108 } else {
2109 f64::INFINITY
2110 }
2111 }
2112 ResultData::String(_) => {
2113 if is_direct {
2114 self.to_f64(arg).unwrap_or(f64::INFINITY)
2115 } else {
2116 f64::INFINITY
2117 }
2118 }
2119 ResultData::List(list) => {
2120 let mut min_val = f64::INFINITY;
2121 for item in list {
2122 min_val = min_val.min(self.min_helper(item, false));
2123 }
2124 min_val
2125 }
2126 _ => f64::INFINITY,
2127 }
2128 }
2129
2130 fn max_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
2131 match arg {
2132 ResultData::Float(f) => *f,
2133 ResultData::Integer(i) => *i as f64,
2134 ResultData::Boolean(b) => {
2135 if is_direct {
2136 if *b { 1.0 } else { 0.0 }
2137 } else {
2138 f64::NEG_INFINITY
2139 }
2140 }
2141 ResultData::String(_) => {
2142 if is_direct {
2143 self.to_f64(arg).unwrap_or(f64::NEG_INFINITY)
2144 } else {
2145 f64::NEG_INFINITY
2146 }
2147 }
2148 ResultData::List(list) => {
2149 let mut max_val = f64::NEG_INFINITY;
2150 for item in list {
2151 max_val = max_val.max(self.max_helper(item, false));
2152 }
2153 max_val
2154 }
2155 _ => f64::NEG_INFINITY,
2156 }
2157 }
2158
2159 fn concat_helper(&self, arg: &ResultData, out: &mut String) {
2160 match arg {
2161 ResultData::List(list) => {
2162 for item in list {
2163 self.concat_helper(item, out);
2164 }
2165 }
2166 other => {
2167 out.push_str(&other.to_string());
2168 }
2169 }
2170 }
2171
2172 fn counta_helper(&self, arg: &ResultData) -> usize {
2173 match arg {
2174 ResultData::None => 0,
2175 ResultData::List(list) => {
2179 let mut count = 0;
2180 for item in list {
2181 count += self.counta_helper(item);
2182 }
2183 count
2184 }
2185 _ => 1,
2186 }
2187 }
2188
2189 fn product_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, bool) {
2190 match arg {
2191 ResultData::Float(f) => (*f, true),
2192 ResultData::Integer(i) => (*i as f64, true),
2193 ResultData::Boolean(b) => {
2194 if is_direct {
2195 (if *b { 1.0 } else { 0.0 }, true)
2196 } else {
2197 (1.0, false)
2198 }
2199 }
2200 ResultData::String(_) => {
2201 if is_direct {
2202 if let Some(f) = self.to_f64(arg) {
2203 (f, true)
2204 } else {
2205 (1.0, false)
2206 }
2207 } else {
2208 (1.0, false)
2209 }
2210 }
2211 ResultData::List(list) => {
2212 let mut prod = 1.0;
2213 let mut has_nums = false;
2214 for item in list {
2215 let (p, h) = self.product_helper(item, false);
2216 if h {
2217 prod *= p;
2220 has_nums = true;
2221 }
2222 }
2223 (prod, has_nums)
2224 }
2225 _ => (1.0, false),
2226 }
2227 }
2228
2229 fn to_bool_opt(&self, val: &ResultData) -> Option<bool> {
2230 match val {
2231 ResultData::Boolean(b) => Some(*b),
2232 ResultData::Integer(i) => Some(*i != 0),
2233 ResultData::Float(f) => Some(*f != 0.0),
2234 ResultData::String(s) => {
2235 let s_trim = s.trim();
2236 if s_trim.eq_ignore_ascii_case("true") {
2237 Some(true)
2238 } else if s_trim.eq_ignore_ascii_case("false") {
2239 Some(false)
2240 } else if let Ok(f) = s_trim.parse::<f64>() {
2241 Some(f != 0.0)
2242 } else {
2243 None
2244 }
2245 }
2246 ResultData::None => Some(false),
2247 _ => None,
2248 }
2249 }
2250
2251 fn to_bool(&self, val: &ResultData) -> bool {
2252 self.to_bool_opt(val).unwrap_or(false)
2253 }
2254
2255 fn range_numeric(val: &ResultData) -> Option<f64> {
2265 match val {
2266 ResultData::Integer(i) => Some(*i as f64),
2267 ResultData::Float(f) => Some(*f),
2268 _ => None,
2269 }
2270 }
2271
2272 fn exact_lookup_matches(lookup: &ResultData, candidate: &ResultData) -> bool {
2282 if matches!(candidate, ResultData::None) {
2283 return false;
2284 }
2285 let lookup_key = match lookup {
2286 ResultData::None => "0".to_string(),
2287 other => other.to_string(),
2288 };
2289 candidate.to_string() == lookup_key
2290 }
2291
2292 fn wildcard_criteria_matches(pattern: &str, text: &str) -> bool {
2293 fn rec(pat: &[char], txt: &[char]) -> bool {
2294 if pat.is_empty() {
2295 return txt.is_empty();
2296 }
2297 match pat[0] {
2298 '*' => rec(&pat[1..], txt) || (!txt.is_empty() && rec(pat, &txt[1..])),
2299 '?' => !txt.is_empty() && rec(&pat[1..], &txt[1..]),
2300 '~' if pat.len() > 1 && matches!(pat[1], '*' | '?' | '~') => {
2301 !txt.is_empty() && pat[1] == txt[0] && rec(&pat[2..], &txt[1..])
2302 }
2303 ch => !txt.is_empty() && ch == txt[0] && rec(&pat[1..], &txt[1..]),
2304 }
2305 }
2306
2307 let pat = pattern.to_lowercase().chars().collect::<Vec<_>>();
2308 let txt = text.to_lowercase().chars().collect::<Vec<_>>();
2309 rec(&pat, &txt)
2310 }
2311
2312 fn criteria_text_eq(val: &ResultData, pattern: &str) -> bool {
2313 let text = val.to_string();
2314 if pattern.contains('*') || pattern.contains('?') {
2315 matches!(val, ResultData::String(_)) && Self::wildcard_criteria_matches(pattern, &text)
2318 } else {
2319 text.to_lowercase() == pattern.to_lowercase()
2320 }
2321 }
2322
2323 fn match_criteria(&self, val: &ResultData, criteria: &ResultData) -> bool {
2324 let crit_str = criteria.to_string();
2325 if let Some(rest) = crit_str.strip_prefix(">=") {
2326 let val_f = match Self::range_numeric(val) {
2332 Some(f) => f,
2333 None => return false,
2334 };
2335 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2336 val_f >= crit_f
2337 } else if let Some(rest) = crit_str.strip_prefix('>') {
2338 let val_f = match Self::range_numeric(val) {
2339 Some(f) => f,
2340 None => return false,
2341 };
2342 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2343 val_f > crit_f
2344 } else if let Some(rest) = crit_str.strip_prefix("<>") {
2345 let remainder = rest.trim();
2346 !Self::criteria_text_eq(val, remainder)
2347 } else if let Some(rest) = crit_str.strip_prefix("<=") {
2348 let val_f = match Self::range_numeric(val) {
2349 Some(f) => f,
2350 None => return false,
2351 };
2352 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2353 val_f <= crit_f
2354 } else if let Some(rest) = crit_str.strip_prefix('<') {
2355 let val_f = match Self::range_numeric(val) {
2356 Some(f) => f,
2357 None => return false,
2358 };
2359 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2360 val_f < crit_f
2361 } else if let Some(rest) = crit_str.strip_prefix('=') {
2362 let remainder = rest.trim();
2363 Self::criteria_text_eq(val, remainder)
2364 } else {
2365 Self::criteria_text_eq(val, &crit_str)
2366 }
2367 }
2368
2369 fn range_bounds(
2375 expr: &crate::core::parser::Expr,
2376 ) -> Option<(Option<String>, usize, usize, usize, usize)> {
2377 use crate::core::parser::Expr;
2378 match expr {
2379 Expr::RangeRef {
2380 sheet,
2381 start_row,
2382 start_col,
2383 end_row,
2384 end_col,
2385 ..
2386 } => Some((sheet.clone(), *start_row, *start_col, *end_row, *end_col)),
2387 Expr::CellRef {
2388 sheet, row, col, ..
2389 } => Some((sheet.clone(), *row, *col, *row, *col)),
2390 _ => None,
2391 }
2392 }
2393
2394 fn materialize_range(
2401 &self,
2402 sheet_opt: &Option<String>,
2403 start_row: usize,
2404 start_col: usize,
2405 end_row: usize,
2406 end_col: usize,
2407 context: Option<&Context>,
2408 ) -> Option<Vec<Vec<ResultData>>> {
2409 let is_self = match sheet_opt {
2410 Some(name) => name == &self.name,
2411 None => true,
2412 };
2413 let source: &Sheet = if is_self {
2414 self
2415 } else {
2416 context?.sheets.get(sheet_opt.as_ref()?)?
2417 };
2418 let actual_end_row = if end_row == usize::MAX {
2419 source.row_count().saturating_sub(1)
2420 } else {
2421 end_row
2422 };
2423 let actual_end_col = if end_col == usize::MAX {
2424 source.col_count().saturating_sub(1)
2425 } else {
2426 end_col
2427 };
2428 if actual_end_row < start_row || actual_end_col < start_col {
2429 return Some(Vec::new());
2430 }
2431 let mut grid = Vec::with_capacity(actual_end_row - start_row + 1);
2432 for r in start_row..=actual_end_row {
2433 let mut row = Vec::with_capacity(actual_end_col - start_col + 1);
2434 for c in start_col..=actual_end_col {
2435 row.push(source.get_result_data(&CellRef::new(r, c)));
2436 }
2437 grid.push(row);
2438 }
2439 Some(grid)
2440 }
2441
2442 fn evaluate_database_function(
2456 &self,
2457 func_name: &str,
2458 args: &[crate::core::parser::Expr],
2459 evaluated_args: &[ResultData],
2460 context: Option<&Context>,
2461 ) -> Result<ResultData, EngineError> {
2462 if args.len() < 3 || evaluated_args.len() < 3 {
2463 return Ok(ResultData::Error("#VALUE!".to_string()));
2464 }
2465 let (db_sheet, db_sr, db_sc, db_er, db_ec) = match Self::range_bounds(&args[0]) {
2466 Some(v) => v,
2467 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2468 };
2469 let (crit_sheet, crit_sr, crit_sc, crit_er, crit_ec) = match Self::range_bounds(&args[2]) {
2470 Some(v) => v,
2471 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2472 };
2473 let db = match self.materialize_range(&db_sheet, db_sr, db_sc, db_er, db_ec, context) {
2474 Some(g) => g,
2475 None => return Ok(ResultData::Error("#REF!".to_string())),
2476 };
2477 let crit = match self.materialize_range(
2478 &crit_sheet,
2479 crit_sr,
2480 crit_sc,
2481 crit_er,
2482 crit_ec,
2483 context,
2484 ) {
2485 Some(g) => g,
2486 None => return Ok(ResultData::Error("#REF!".to_string())),
2487 };
2488 if db.len() < 2 || crit.len() < 2 {
2489 return Ok(ResultData::Error("#VALUE!".to_string()));
2490 }
2491
2492 let db_headers: Vec<String> = db[0].iter().map(|v| v.to_string()).collect();
2493 let field_idx: usize = match &evaluated_args[1] {
2494 ResultData::String(s) => {
2495 match db_headers.iter().position(|h| h.eq_ignore_ascii_case(s)) {
2496 Some(idx) => idx,
2497 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2498 }
2499 }
2500 other => match self.to_f64(other) {
2501 Some(n) if n >= 1.0 && (n as usize) <= db_headers.len() => n as usize - 1,
2502 _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2503 },
2504 };
2505
2506 let crit_headers: Vec<String> = crit[0].iter().map(|v| v.to_string()).collect();
2507 let crit_to_db: Vec<Option<usize>> = crit_headers
2508 .iter()
2509 .map(|h| db_headers.iter().position(|dh| dh.eq_ignore_ascii_case(h)))
2510 .collect();
2511
2512 let mut matched: Vec<ResultData> = Vec::new();
2513 for row in db.iter().skip(1) {
2514 let row_matches_any_criteria_row = crit.iter().skip(1).any(|crit_row| {
2515 crit_row.iter().enumerate().all(|(ci, cell)| {
2516 if matches!(cell, ResultData::None) {
2517 return true;
2518 }
2519 match crit_to_db.get(ci).copied().flatten() {
2520 Some(db_col) => self.match_criteria(&row[db_col], cell),
2521 None => false,
2522 }
2523 })
2524 });
2525 if row_matches_any_criteria_row {
2526 matched.push(row[field_idx].clone());
2527 }
2528 }
2529
2530 match func_name {
2531 "DGET" => match matched.len() {
2532 0 => Ok(ResultData::Error("#VALUE!".to_string())),
2533 1 => Ok(matched.into_iter().next().unwrap()),
2534 _ => Ok(ResultData::Error("#NUM!".to_string())),
2535 },
2536 "DCOUNT" => Ok(ResultData::Float(
2537 matched
2538 .iter()
2539 .filter(|v| Self::range_numeric(v).is_some())
2540 .count() as f64,
2541 )),
2542 "DCOUNTA" => Ok(ResultData::Float(
2543 matched.iter().map(|v| self.counta_helper(v)).sum::<usize>() as f64,
2544 )),
2545 _ => {
2546 let nums: Vec<f64> = matched.iter().filter_map(Self::range_numeric).collect();
2547 match func_name {
2548 "DSUM" => Ok(ResultData::Float(nums.iter().sum())),
2549 "DPRODUCT" => Ok(ResultData::Float(if nums.is_empty() {
2550 0.0
2551 } else {
2552 nums.iter().product()
2553 })),
2554 "DMAX" => {
2555 let m = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
2556 Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2557 }
2558 "DMIN" => {
2559 let m = nums.iter().cloned().fold(f64::INFINITY, f64::min);
2560 Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2561 }
2562 "DAVERAGE" => {
2563 if nums.is_empty() {
2564 Ok(ResultData::Error("#DIV/0!".to_string()))
2565 } else {
2566 Ok(ResultData::Float(
2567 nums.iter().sum::<f64>() / nums.len() as f64,
2568 ))
2569 }
2570 }
2571 "DSTDEV" => match crate::core::stats::stdev_s(&nums) {
2572 Ok(v) => Ok(ResultData::Float(v)),
2573 Err(e) => Ok(ResultData::Error(e)),
2574 },
2575 "DSTDEVP" => match crate::core::stats::stdev_p(&nums) {
2576 Ok(v) => Ok(ResultData::Float(v)),
2577 Err(e) => Ok(ResultData::Error(e)),
2578 },
2579 "DVAR" => match crate::core::stats::var_s(&nums) {
2580 Ok(v) => Ok(ResultData::Float(v)),
2581 Err(e) => Ok(ResultData::Error(e)),
2582 },
2583 "DVARP" => match crate::core::stats::var_p(&nums) {
2584 Ok(v) => Ok(ResultData::Float(v)),
2585 Err(e) => Ok(ResultData::Error(e)),
2586 },
2587 _ => unreachable!(),
2588 }
2589 }
2590 }
2591 }
2592
2593 fn proper(&self, s: &str) -> String {
2594 let mut c_chars = Vec::new();
2600 let mut capitalize_next = true;
2601 for c in s.chars() {
2602 if c.is_alphabetic() {
2603 if capitalize_next {
2604 c_chars.extend(c.to_uppercase());
2605 } else {
2606 c_chars.extend(c.to_lowercase());
2607 }
2608 capitalize_next = false;
2609 } else {
2610 c_chars.push(c);
2611 capitalize_next = true;
2612 }
2613 }
2614 c_chars.into_iter().collect()
2615 }
2616
2617 fn get_ymd_hms(&self) -> ((i32, u32, u32), (u32, u32, u32)) {
2618 let now = web_time::SystemTime::now()
2619 .duration_since(web_time::SystemTime::UNIX_EPOCH)
2620 .unwrap_or_default()
2621 .as_secs();
2622 let secs_in_day = 86400;
2623 let days_since_epoch = (now / secs_in_day) as i32;
2624 let seconds_of_day = (now % secs_in_day) as u32;
2625
2626 let hour = seconds_of_day / 3600;
2627 let minute = (seconds_of_day % 3600) / 60;
2628 let second = seconds_of_day % 60;
2629
2630 let era = (if days_since_epoch >= -719468 {
2631 days_since_epoch + 719468
2632 } else {
2633 days_since_epoch + 719468 - 146096
2634 }) / 146097;
2635 let doe = (days_since_epoch + 719468 - era * 146097) as u32;
2636 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
2637 let y = (yoe as i32) + era * 400;
2638 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2639 let mp = (5 * doy + 2) / 153;
2640 let d = doy - (153 * mp + 2) / 5 + 1;
2641 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2642 let year = if m <= 2 { y + 1 } else { y };
2643
2644 ((year, m, d), (hour, minute, second))
2645 }
2646
2647 fn evaluate_let(
2654 &self,
2655 args: &[crate::core::parser::Expr],
2656 context: Option<&Context>,
2657 row: Option<usize>,
2658 col: Option<usize>,
2659 deps: &mut Vec<Dependency>,
2660 scope: &LetScope<'_>,
2661 ) -> Result<ResultData, EngineError> {
2662 use crate::core::parser::Expr;
2663
2664 if args.is_empty() || args.len().is_multiple_of(2) {
2665 return Ok(ResultData::Error("#VALUE!".to_string()));
2668 }
2669 if args.len() == 1 {
2670 return self.evaluate_ast(&args[0], context, row, col, deps, scope);
2671 }
2672
2673 let name = match &args[0] {
2674 Expr::Identifier(n) => n.as_str(),
2675 _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2676 };
2677 let remaining_pairs = args.len() / 2 - 1;
2680 let is_duplicate = args[2..]
2681 .iter()
2682 .step_by(2)
2683 .take(remaining_pairs)
2684 .any(|a| matches!(a, Expr::Identifier(n2) if n2.eq_ignore_ascii_case(name)));
2685 if is_duplicate {
2686 return Ok(ResultData::Error("#VALUE!".to_string()));
2687 }
2688
2689 let value = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
2690 let inner_scope = LetScope::Bound {
2691 name,
2692 value: &value,
2693 parent: scope,
2694 };
2695 self.evaluate_let(&args[2..], context, row, col, deps, &inner_scope)
2696 }
2697
2698 fn extract_lambda(
2707 expr: &crate::core::parser::Expr,
2708 ) -> Option<(Vec<&str>, &crate::core::parser::Expr)> {
2709 use crate::core::parser::Expr;
2710 let Expr::FunctionCall { name, args } = expr else {
2711 return None;
2712 };
2713 if !name.eq_ignore_ascii_case("LAMBDA") || args.is_empty() {
2714 return None;
2715 }
2716 let (body, params) = args.split_last().unwrap();
2717 let param_names: Vec<&str> = params
2718 .iter()
2719 .filter_map(|p| match p {
2720 Expr::Identifier(n) => Some(n.as_str()),
2721 _ => None,
2722 })
2723 .collect();
2724 if param_names.len() != params.len() {
2725 return None;
2726 }
2727 Some((param_names, body))
2728 }
2729
2730 #[allow(clippy::too_many_arguments)]
2736 fn invoke_lambda<'v>(
2737 &self,
2738 params: &[&str],
2739 values: &'v [ResultData],
2740 body: &crate::core::parser::Expr,
2741 context: Option<&Context>,
2742 row: Option<usize>,
2743 col: Option<usize>,
2744 deps: &mut Vec<Dependency>,
2745 scope: &LetScope<'v>,
2746 ) -> Result<ResultData, EngineError> {
2747 match (params.split_first(), values.split_first()) {
2748 (Some((&pname, prest)), Some((vfirst, vrest))) => {
2749 let inner_scope = LetScope::Bound {
2750 name: pname,
2751 value: vfirst,
2752 parent: scope,
2753 };
2754 self.invoke_lambda(prest, vrest, body, context, row, col, deps, &inner_scope)
2755 }
2756 _ => self.evaluate_ast(body, context, row, col, deps, scope),
2757 }
2758 }
2759
2760 fn eval_as_array(
2764 &self,
2765 expr: &crate::core::parser::Expr,
2766 context: Option<&Context>,
2767 row: Option<usize>,
2768 col: Option<usize>,
2769 deps: &mut Vec<Dependency>,
2770 scope: &LetScope<'_>,
2771 ) -> Result<Vec<ResultData>, EngineError> {
2772 Ok(
2773 match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2774 ResultData::List(items) => Self::flatten_row_major(items).0,
2775 other => vec![other],
2776 },
2777 )
2778 }
2779
2780 fn flatten_row_major(items: Vec<ResultData>) -> (Vec<ResultData>, Option<usize>) {
2790 if !items.is_empty() && items.iter().all(|v| matches!(v, ResultData::List(_))) {
2791 let cols = match &items[0] {
2792 ResultData::List(inner) => inner.len().max(1),
2793 _ => 1,
2794 };
2795 let flat = items
2796 .into_iter()
2797 .flat_map(|v| match v {
2798 ResultData::List(inner) => inner,
2799 other => vec![other],
2800 })
2801 .collect();
2802 (flat, Some(cols))
2803 } else {
2804 (items, None)
2805 }
2806 }
2807
2808 fn array_shape(
2816 &self,
2817 expr: &crate::core::parser::Expr,
2818 context: Option<&Context>,
2819 row: Option<usize>,
2820 col: Option<usize>,
2821 deps: &mut Vec<Dependency>,
2822 scope: &LetScope<'_>,
2823 ) -> Result<(Vec<ResultData>, usize), EngineError> {
2824 use crate::core::parser::Expr;
2825 let items = match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2826 ResultData::List(items) => items,
2827 other => vec![other],
2828 };
2829 let (flat, nested_cols) = Self::flatten_row_major(items);
2830 if let Some(cols) = nested_cols {
2831 return Ok((flat, cols));
2832 }
2833 let num_cols = match expr {
2834 Expr::RangeRef {
2835 start_col, end_col, ..
2836 } => (end_col - start_col + 1).max(1),
2837 Expr::CellRef { .. } => 1,
2838 Expr::FunctionCall { name, args } => self
2839 .function_call_cols(name, args, context, row, col, deps, scope)
2840 .unwrap_or_else(|| flat.len().max(1)),
2841 _ => flat.len().max(1),
2842 };
2843 Ok((flat, num_cols))
2844 }
2845
2846 #[allow(clippy::too_many_arguments)]
2855 fn function_call_cols(
2856 &self,
2857 name: &str,
2858 args: &[crate::core::parser::Expr],
2859 context: Option<&Context>,
2860 row: Option<usize>,
2861 col: Option<usize>,
2862 deps: &mut Vec<Dependency>,
2863 scope: &LetScope<'_>,
2864 ) -> Option<usize> {
2865 let mut upper = name.to_ascii_uppercase();
2866 if let Some(rest) = upper.strip_prefix("_XLFN.") {
2867 upper = rest.to_string();
2868 }
2869 if let Some(rest) = upper.strip_prefix("_XLWS.") {
2870 upper = rest.to_string();
2871 }
2872 match upper.as_str() {
2873 "TRANSPOSE" => {
2874 let (flat, cols) = self
2875 .array_shape(args.first()?, context, row, col, deps, scope)
2876 .ok()?;
2877 Some((flat.len().checked_div(cols).unwrap_or(0)).max(1))
2878 }
2879 "HSTACK" => {
2880 let mut total = 0usize;
2881 for a in args {
2882 total += self.array_shape(a, context, row, col, deps, scope).ok()?.1;
2883 }
2884 Some(total)
2885 }
2886 "VSTACK" => {
2887 let mut max_cols = 0usize;
2888 for a in args {
2889 max_cols =
2890 max_cols.max(self.array_shape(a, context, row, col, deps, scope).ok()?.1);
2891 }
2892 Some(max_cols)
2893 }
2894 "CHOOSEROWS" => Some(
2895 self.array_shape(args.first()?, context, row, col, deps, scope)
2896 .ok()?
2897 .1,
2898 ),
2899 "CHOOSECOLS" => Some(args.len().saturating_sub(1).max(1)),
2900 "DROP" | "TAKE" => {
2901 let (_, cols) = self
2902 .array_shape(args.first()?, context, row, col, deps, scope)
2903 .ok()?;
2904 let is_take = upper == "TAKE";
2905 match args.get(2) {
2906 Some(e) => {
2907 let n = self
2908 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2909 .unwrap_or(0.0) as isize;
2910 let (s, e2) = Self::drop_take_bounds(cols as isize, n, is_take);
2911 Some((e2 - s).max(0) as usize)
2912 }
2913 None => Some(if is_take { cols } else { 0 }),
2914 }
2915 }
2916 "EXPAND" => {
2917 let (_, cols) = self
2918 .array_shape(args.first()?, context, row, col, deps, scope)
2919 .ok()?;
2920 match args.get(2) {
2921 Some(e) => Some(
2922 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2923 .unwrap_or(cols as f64) as usize,
2924 ),
2925 None => Some(cols),
2926 }
2927 }
2928 "TOCOL" => Some(1),
2929 "WRAPROWS" => {
2930 let n = self
2931 .to_f64(
2932 &self
2933 .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2934 .ok()?,
2935 )
2936 .unwrap_or(1.0)
2937 .max(1.0) as usize;
2938 Some(n)
2939 }
2940 "WRAPCOLS" => {
2941 let (flat, _) = self
2942 .array_shape(args.first()?, context, row, col, deps, scope)
2943 .ok()?;
2944 let wrap = self
2945 .to_f64(
2946 &self
2947 .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2948 .ok()?,
2949 )
2950 .unwrap_or(1.0)
2951 .max(1.0) as usize;
2952 Some(flat.len().div_ceil(wrap).max(1))
2953 }
2954 "UNIQUE" | "SORT" | "SORTBY" | "FILTER" | "TRIMRANGE" => Some(
2955 self.array_shape(args.first()?, context, row, col, deps, scope)
2956 .ok()?
2957 .1,
2958 ),
2959 "SEQUENCE" => match args.get(1) {
2960 Some(e) => Some(
2961 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2962 .unwrap_or(1.0)
2963 .max(1.0) as usize,
2964 ),
2965 None => Some(1),
2966 },
2967 "MUNIT" => {
2968 let n = self
2969 .to_f64(
2970 &self
2971 .evaluate_ast(args.first()?, context, row, col, deps, scope)
2972 .ok()?,
2973 )
2974 .unwrap_or(1.0)
2975 .max(1.0) as usize;
2976 Some(n)
2977 }
2978 "MAKEARRAY" => match args.get(1) {
2979 Some(e) => Some(
2980 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2981 .unwrap_or(1.0)
2982 .max(1.0) as usize,
2983 ),
2984 None => Some(1),
2985 },
2986 _ => None,
2987 }
2988 }
2989
2990 fn drop_take_bounds(total: isize, n: isize, is_take: bool) -> (isize, isize) {
2994 let n = n.clamp(-total, total);
2995 if is_take {
2996 if n >= 0 { (0, n) } else { (total + n, total) }
2997 } else if n >= 0 {
2998 (n, total)
2999 } else {
3000 (0, total + n)
3001 }
3002 }
3003
3004 #[allow(clippy::too_many_arguments)]
3013 fn evaluate_lambda_function(
3014 &self,
3015 func_name: &str,
3016 args: &[crate::core::parser::Expr],
3017 context: Option<&Context>,
3018 row: Option<usize>,
3019 col: Option<usize>,
3020 deps: &mut Vec<Dependency>,
3021 scope: &LetScope<'_>,
3022 ) -> Result<ResultData, EngineError> {
3023 use crate::core::parser::Expr;
3024
3025 match func_name {
3026 "MAP" => {
3027 if args.len() < 2 {
3028 return Ok(ResultData::Error("#VALUE!".to_string()));
3029 }
3030 let (lambda_expr, array_exprs) = args.split_last().unwrap();
3031 let Some((params, body)) = Self::extract_lambda(lambda_expr) else {
3032 return Ok(ResultData::Error("#VALUE!".to_string()));
3033 };
3034 if params.len() != array_exprs.len() {
3035 return Ok(ResultData::Error("#VALUE!".to_string()));
3036 }
3037 let arrays: Vec<Vec<ResultData>> = array_exprs
3038 .iter()
3039 .map(|e| self.eval_as_array(e, context, row, col, deps, scope))
3040 .collect::<Result<_, _>>()?;
3041 let len = arrays.iter().map(|a| a.len()).max().unwrap_or(0);
3042 let mut results = Vec::with_capacity(len);
3043 for i in 0..len {
3044 let values: Vec<ResultData> = arrays
3045 .iter()
3046 .map(|a| a.get(i).cloned().unwrap_or(ResultData::None))
3047 .collect();
3048 results.push(
3049 self.invoke_lambda(¶ms, &values, body, context, row, col, deps, scope)?,
3050 );
3051 }
3052 Ok(ResultData::List(results))
3053 }
3054 "BYROW" | "BYCOL" => {
3055 if args.len() != 2 {
3056 return Ok(ResultData::Error("#VALUE!".to_string()));
3057 }
3058 let Some((params, body)) = Self::extract_lambda(&args[1]) else {
3059 return Ok(ResultData::Error("#VALUE!".to_string()));
3060 };
3061 if params.len() != 1 {
3062 return Ok(ResultData::Error("#VALUE!".to_string()));
3063 }
3064 let num_cols = match &args[0] {
3068 Expr::RangeRef {
3069 start_col, end_col, ..
3070 } => (end_col - start_col + 1).max(1),
3071 _ => 1,
3072 };
3073 let flat = self.eval_as_array(&args[0], context, row, col, deps, scope)?;
3074 let num_rows = if num_cols == 0 {
3075 0
3076 } else {
3077 flat.len().div_ceil(num_cols)
3078 };
3079 let mut results = Vec::new();
3080 if func_name == "BYROW" {
3081 for r in 0..num_rows {
3082 let row_vals: Vec<ResultData> = (0..num_cols)
3083 .filter_map(|c| flat.get(r * num_cols + c).cloned())
3084 .collect();
3085 let arg = vec![ResultData::List(row_vals)];
3086 results.push(
3087 self.invoke_lambda(
3088 ¶ms, &arg, body, context, row, col, deps, scope,
3089 )?,
3090 );
3091 }
3092 } else {
3093 for c in 0..num_cols {
3094 let col_vals: Vec<ResultData> = (0..num_rows)
3095 .filter_map(|r| flat.get(r * num_cols + c).cloned())
3096 .collect();
3097 let arg = vec![ResultData::List(col_vals)];
3098 results.push(
3099 self.invoke_lambda(
3100 ¶ms, &arg, body, context, row, col, deps, scope,
3101 )?,
3102 );
3103 }
3104 }
3105 Ok(ResultData::List(results))
3106 }
3107 "REDUCE" | "SCAN" => {
3108 if args.len() != 2 && args.len() != 3 {
3117 return Ok(ResultData::Error("#VALUE!".to_string()));
3118 }
3119 let lambda_idx = args.len() - 1;
3120 let array_idx = args.len() - 2;
3121 let Some((params, body)) = Self::extract_lambda(&args[lambda_idx]) else {
3122 return Ok(ResultData::Error("#VALUE!".to_string()));
3123 };
3124 if params.len() != 2 {
3125 return Ok(ResultData::Error("#VALUE!".to_string()));
3126 }
3127 let array = self.eval_as_array(&args[array_idx], context, row, col, deps, scope)?;
3128 let (mut acc, rest, mut history): (ResultData, &[ResultData], Vec<ResultData>) =
3134 if args.len() == 3 {
3135 let init = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
3136 (init, &array[..], Vec::new())
3137 } else {
3138 match array.split_first() {
3139 Some((first, rest)) => (first.clone(), rest, vec![first.clone()]),
3140 None => return Ok(ResultData::Error("#VALUE!".to_string())),
3141 }
3142 };
3143 for item in rest {
3144 let call_args = [acc.clone(), item.clone()];
3145 acc = self
3146 .invoke_lambda(¶ms, &call_args, body, context, row, col, deps, scope)?;
3147 history.push(acc.clone());
3148 }
3149 if func_name == "REDUCE" {
3150 Ok(acc)
3151 } else {
3152 Ok(ResultData::List(history))
3153 }
3154 }
3155 "MAKEARRAY" => {
3156 if args.len() != 3 {
3157 return Ok(ResultData::Error("#VALUE!".to_string()));
3158 }
3159 let Some((params, body)) = Self::extract_lambda(&args[2]) else {
3160 return Ok(ResultData::Error("#VALUE!".to_string()));
3161 };
3162 if params.len() != 2 {
3163 return Ok(ResultData::Error("#VALUE!".to_string()));
3164 }
3165 let rows_val = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
3166 let cols_val = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
3167 let num_rows = self.to_f64(&rows_val).unwrap_or(0.0).max(0.0) as usize;
3168 let num_cols = self.to_f64(&cols_val).unwrap_or(0.0).max(0.0) as usize;
3169 let mut results = Vec::with_capacity(num_rows * num_cols);
3170 for r in 1..=num_rows {
3171 for c in 1..=num_cols {
3172 let call_args = [ResultData::Float(r as f64), ResultData::Float(c as f64)];
3173 results.push(self.invoke_lambda(
3174 ¶ms, &call_args, body, context, row, col, deps, scope,
3175 )?);
3176 }
3177 }
3178 Ok(ResultData::List(results))
3179 }
3180 _ => unreachable!(),
3181 }
3182 }
3183
3184 fn parse_a1_reference(text: &str) -> Option<(Option<String>, usize, usize, usize, usize)> {
3193 let text = text.trim();
3194 let (sheet_part, ref_part) = match text.rfind('!') {
3195 Some(idx) => (Some(&text[..idx]), &text[idx + 1..]),
3196 None => (None, text),
3197 };
3198 let sheet = sheet_part.map(|s| s.trim().trim_matches('\'').to_string());
3199
3200 fn parse_cell(s: &str) -> Option<(usize, usize)> {
3201 let s = s.replace('$', "");
3202 let col_end = s.find(|c: char| c.is_ascii_digit())?;
3203 let (col_str, row_str) = s.split_at(col_end);
3204 if col_str.is_empty() || row_str.is_empty() {
3205 return None;
3206 }
3207 let mut col = 0usize;
3208 for ch in col_str.chars() {
3209 if !ch.is_ascii_alphabetic() {
3210 return None;
3211 }
3212 col = col * 26 + (ch.to_ascii_uppercase() as usize - 'A' as usize + 1);
3213 }
3214 let row: usize = row_str.parse().ok()?;
3215 if row == 0 || col == 0 {
3216 return None;
3217 }
3218 Some((row - 1, col - 1))
3219 }
3220
3221 if let Some((start, end)) = ref_part.split_once(':') {
3222 let (r1, c1) = parse_cell(start)?;
3223 let (r2, c2) = parse_cell(end)?;
3224 Some((sheet, r1.min(r2), c1.min(c2), r1.max(r2), c1.max(c2)))
3225 } else {
3226 let (r, c) = parse_cell(ref_part)?;
3227 Some((sheet, r, c, r, c))
3228 }
3229 }
3230
3231 fn read_cell_with_deps(
3238 &self,
3239 sheet_opt: &Option<String>,
3240 r: usize,
3241 c: usize,
3242 context: Option<&Context>,
3243 deps: &mut Vec<Dependency>,
3244 ) -> ResultData {
3245 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3246 if is_self {
3247 deps.push(Dependency::Local(CellRef::new(r, c)));
3248 self.get_result_data(&CellRef::new(r, c))
3249 } else if let Some(ctx) = context {
3250 let name = sheet_opt.clone().unwrap();
3251 deps.push(Dependency::Remote {
3252 sheet: name.clone(),
3253 cell: CellRef::new(r, c),
3254 });
3255 ctx.sheets
3256 .get(&name)
3257 .map(|s| s.get_result_data(&CellRef::new(r, c)))
3258 .unwrap_or(ResultData::None)
3259 } else {
3260 ResultData::None
3261 }
3262 }
3263
3264 #[allow(clippy::too_many_arguments)]
3273 fn evaluate_range_info_function(
3274 &self,
3275 func_name: &str,
3276 args: &[crate::core::parser::Expr],
3277 context: Option<&Context>,
3278 row: Option<usize>,
3279 col: Option<usize>,
3280 deps: &mut Vec<Dependency>,
3281 scope: &LetScope<'_>,
3282 ) -> Result<ResultData, EngineError> {
3283 use crate::core::parser::Expr;
3284
3285 match func_name {
3286 "ROW" => match args.first() {
3287 Some(arg) => match Self::range_bounds(arg) {
3292 Some((_, start_row, _, end_row, _)) if end_row > start_row => {
3293 Ok(ResultData::List(
3294 (start_row..=end_row)
3295 .map(|r| ResultData::Float((r + 1) as f64))
3296 .collect(),
3297 ))
3298 }
3299 Some((_, start_row, _, _, _)) => Ok(ResultData::Float((start_row + 1) as f64)),
3300 None => Ok(ResultData::Error("#VALUE!".to_string())),
3301 },
3302 None => match row {
3303 Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3304 None => Ok(ResultData::Error("#VALUE!".to_string())),
3305 },
3306 },
3307 "COLUMN" => match args.first() {
3308 Some(arg) => match Self::range_bounds(arg) {
3311 Some((_, _, start_col, _, end_col)) if end_col > start_col => {
3312 Ok(ResultData::List(
3313 (start_col..=end_col)
3314 .map(|c| ResultData::Float((c + 1) as f64))
3315 .collect(),
3316 ))
3317 }
3318 Some((_, _, start_col, _, _)) => Ok(ResultData::Float((start_col + 1) as f64)),
3319 None => Ok(ResultData::Error("#VALUE!".to_string())),
3320 },
3321 None => match col {
3322 Some(c) => Ok(ResultData::Float((c + 1) as f64)),
3323 None => Ok(ResultData::Error("#VALUE!".to_string())),
3324 },
3325 },
3326 "ROWS" => {
3327 let Some(arg) = args.first() else {
3328 return Ok(ResultData::Error("#VALUE!".to_string()));
3329 };
3330 let Some((sheet_opt, start_row, _, end_row, _)) = Self::range_bounds(arg) else {
3331 return Ok(ResultData::Error("#VALUE!".to_string()));
3332 };
3333 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3334 let actual_end_row = if end_row == usize::MAX {
3335 if is_self {
3336 self.row_count().saturating_sub(1)
3337 } else {
3338 context
3339 .and_then(|ctx| sheet_opt.as_ref().and_then(|n| ctx.sheets.get(n)))
3340 .map(|s| s.row_count().saturating_sub(1))
3341 .unwrap_or(0)
3342 }
3343 } else {
3344 end_row
3345 };
3346 Ok(ResultData::Float(
3347 (actual_end_row.saturating_sub(start_row) + 1) as f64,
3348 ))
3349 }
3350 "COLUMNS" => {
3351 let Some(arg) = args.first() else {
3352 return Ok(ResultData::Error("#VALUE!".to_string()));
3353 };
3354 match Self::range_bounds(arg) {
3355 Some((_, _, start_col, _, end_col)) => Ok(ResultData::Float(
3356 (end_col.saturating_sub(start_col) + 1) as f64,
3357 )),
3358 None => Ok(ResultData::Error("#VALUE!".to_string())),
3359 }
3360 }
3361 "AREAS" => {
3362 if args.is_empty() {
3366 Ok(ResultData::Error("#VALUE!".to_string()))
3367 } else {
3368 Ok(ResultData::Float(1.0))
3369 }
3370 }
3371 "ISREF" => Ok(ResultData::Boolean(matches!(
3372 args.first(),
3373 Some(Expr::CellRef { .. } | Expr::RangeRef { .. } | Expr::StructuredRef { .. })
3374 ))),
3375 "FORMULATEXT" | "ISFORMULA" => {
3376 let Some(arg) = args.first() else {
3377 return Ok(ResultData::Error("#VALUE!".to_string()));
3378 };
3379 let Some((sheet_opt, r, c, _, _)) = Self::range_bounds(arg) else {
3380 return Ok(ResultData::Error("#VALUE!".to_string()));
3381 };
3382 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3383 let src = if is_self {
3384 deps.push(Dependency::Local(CellRef::new(r, c)));
3385 self.get_src_str(&CellRef::new(r, c))
3386 } else if let Some(ctx) = context {
3387 let name = sheet_opt.unwrap();
3388 deps.push(Dependency::Remote {
3389 sheet: name.clone(),
3390 cell: CellRef::new(r, c),
3391 });
3392 ctx.sheets
3393 .get(&name)
3394 .map(|s| s.get_src_str(&CellRef::new(r, c)))
3395 .unwrap_or_default()
3396 } else {
3397 String::new()
3398 };
3399 let is_formula = src.starts_with('=');
3400 if func_name == "ISFORMULA" {
3401 Ok(ResultData::Boolean(is_formula))
3402 } else if is_formula {
3403 Ok(ResultData::String(src))
3404 } else {
3405 Ok(ResultData::Error("#N/A".to_string()))
3406 }
3407 }
3408 "SHEETS" => Ok(ResultData::Float(
3409 context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3410 )),
3411 "SHEET" => {
3412 let sheet_name = match args.first() {
3418 None => Some(self.name.clone()),
3419 Some(arg) => match Self::range_bounds(arg) {
3420 Some((sheet_opt, ..)) => {
3421 Some(sheet_opt.unwrap_or_else(|| self.name.clone()))
3422 }
3423 None => self
3424 .evaluate_ast(arg, context, row, col, deps, scope)
3425 .ok()
3426 .map(|v| v.to_string()),
3427 },
3428 };
3429
3430 match sheet_name {
3431 Some(name) => {
3432 let ordinal = context
3433 .and_then(|c| {
3434 c.sheet_order
3435 .iter()
3436 .position(|n| n.eq_ignore_ascii_case(&name))
3437 })
3438 .map(|i| i + 1)
3439 .unwrap_or(1);
3445 Ok(ResultData::Float(ordinal as f64))
3446 }
3447 None => Ok(ResultData::Error("#N/A".to_string())),
3448 }
3449 }
3450 "CELL" => {
3451 if args.is_empty() {
3452 return Ok(ResultData::Error("#VALUE!".to_string()));
3453 }
3454 let info_type = self
3455 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3456 .to_string()
3457 .to_lowercase();
3458 let bounds = args.get(1).and_then(Self::range_bounds);
3459 match info_type.as_str() {
3460 "row" => match bounds.map(|b| b.1).or(row) {
3461 Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3462 None => Ok(ResultData::Error("#VALUE!".to_string())),
3463 },
3464 "col" => match bounds {
3465 Some((_, _, c, _, _)) => Ok(ResultData::Float((c + 1) as f64)),
3466 None => Ok(ResultData::Error("#VALUE!".to_string())),
3467 },
3468 "address" => match bounds {
3469 Some((_, r, c, _, _)) => Ok(ResultData::String(format!(
3470 "${}${}",
3471 crate::core::parser::col_idx_to_letters(c),
3472 r + 1
3473 ))),
3474 None => Ok(ResultData::Error("#VALUE!".to_string())),
3475 },
3476 "contents" => match bounds {
3477 Some((sheet_opt, r, c, _, _)) => {
3478 Ok(self.read_cell_with_deps(&sheet_opt, r, c, context, deps))
3479 }
3480 None => Ok(ResultData::Error("#VALUE!".to_string())),
3481 },
3482 _ => Ok(ResultData::Error("#VALUE!".to_string())),
3483 }
3484 }
3485 "INFO" => {
3486 if args.is_empty() {
3487 return Ok(ResultData::Error("#VALUE!".to_string()));
3488 }
3489 let info_type = self
3490 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3491 .to_string()
3492 .to_lowercase();
3493 match info_type.as_str() {
3494 "numfile" => Ok(ResultData::Float(
3495 context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3496 )),
3497 "release" => Ok(ResultData::String("16.0".to_string())),
3498 "system" => Ok(ResultData::String(
3499 if cfg!(target_os = "macos") {
3500 "mac"
3501 } else {
3502 "pcdos"
3503 }
3504 .to_string(),
3505 )),
3506 _ => Ok(ResultData::Error("#VALUE!".to_string())),
3507 }
3508 }
3509 "INDIRECT" => {
3510 if args.is_empty() {
3511 return Ok(ResultData::Error("#VALUE!".to_string()));
3512 }
3513 let text = self
3514 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3515 .to_string();
3516 let a1_style = match args.get(1) {
3517 Some(a) => self.to_bool(&self.evaluate_ast(a, context, row, col, deps, scope)?),
3518 None => true,
3519 };
3520 if !a1_style {
3521 return Ok(ResultData::Error("#VALUE!".to_string()));
3523 }
3524 match Self::parse_a1_reference(&text) {
3525 Some((sheet_opt, start_row, start_col, end_row, end_col)) => {
3526 if start_row == end_row && start_col == end_col {
3527 Ok(self.read_cell_with_deps(
3528 &sheet_opt, start_row, start_col, context, deps,
3529 ))
3530 } else {
3531 match self.materialize_range(
3532 &sheet_opt, start_row, start_col, end_row, end_col, context,
3533 ) {
3534 Some(grid) => {
3535 Ok(ResultData::List(grid.into_iter().flatten().collect()))
3536 }
3537 None => Ok(ResultData::Error("#REF!".to_string())),
3538 }
3539 }
3540 }
3541 None => Ok(ResultData::Error("#REF!".to_string())),
3542 }
3543 }
3544 "OFFSET" => {
3545 if args.len() < 3 {
3546 return Ok(ResultData::Error("#VALUE!".to_string()));
3547 }
3548 let Some((sheet_opt, base_row, base_col, base_end_row, base_end_col)) =
3549 Self::range_bounds(&args[0])
3550 else {
3551 return Ok(ResultData::Error("#VALUE!".to_string()));
3552 };
3553 let row_offset = self
3554 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3555 .unwrap_or(0.0) as isize;
3556 let col_offset = self
3557 .to_f64(&self.evaluate_ast(&args[2], context, row, col, deps, scope)?)
3558 .unwrap_or(0.0) as isize;
3559 let base_height = (base_end_row.saturating_sub(base_row) + 1) as isize;
3560 let base_width = (base_end_col.saturating_sub(base_col) + 1) as isize;
3561 let height = match args.get(3) {
3562 Some(a) => self
3563 .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3564 .unwrap_or(base_height as f64) as isize,
3565 None => base_height,
3566 };
3567 let width = match args.get(4) {
3568 Some(a) => self
3569 .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3570 .unwrap_or(base_width as f64) as isize,
3571 None => base_width,
3572 };
3573 let new_row = base_row as isize + row_offset;
3574 let new_col = base_col as isize + col_offset;
3575 if new_row < 0 || new_col < 0 || height <= 0 || width <= 0 {
3576 return Ok(ResultData::Error("#REF!".to_string()));
3577 }
3578 let (start_row, start_col) = (new_row as usize, new_col as usize);
3579 let (end_row, end_col) = (
3580 start_row + (height - 1) as usize,
3581 start_col + (width - 1) as usize,
3582 );
3583 if start_row == end_row && start_col == end_col {
3584 Ok(self.read_cell_with_deps(&sheet_opt, start_row, start_col, context, deps))
3585 } else {
3586 match self.materialize_range(
3587 &sheet_opt, start_row, start_col, end_row, end_col, context,
3588 ) {
3589 Some(grid) => Ok(ResultData::List(grid.into_iter().flatten().collect())),
3590 None => Ok(ResultData::Error("#REF!".to_string())),
3591 }
3592 }
3593 }
3594 _ => unreachable!(),
3595 }
3596 }
3597
3598 fn evaluate_getpivotdata(
3605 &self,
3606 args: &[crate::core::parser::Expr],
3607 context: Option<&Context>,
3608 row: Option<usize>,
3609 col: Option<usize>,
3610 deps: &mut Vec<Dependency>,
3611 scope: &LetScope<'_>,
3612 ) -> Result<ResultData, EngineError> {
3613 if args.len() < 2 || !(args.len() - 2).is_multiple_of(2) {
3614 return Ok(ResultData::Error("#VALUE!".to_string()));
3615 }
3616
3617 let data_field = self
3618 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3619 .to_string();
3620
3621 let (sheet_opt, target_row, target_col, _, _) = match Self::range_bounds(&args[1]) {
3622 Some(bounds) => bounds,
3623 None => return Ok(ResultData::Error("#REF!".to_string())),
3624 };
3625 self.read_cell_with_deps(&sheet_opt, target_row, target_col, context, deps);
3628
3629 let sheet_id = match &sheet_opt {
3630 None => self.id,
3631 Some(name) if name == &self.name => self.id,
3632 Some(name) => match context.and_then(|c| c.sheets.get(name)) {
3633 Some(s) => s.id,
3634 None => return Ok(ResultData::Error("#REF!".to_string())),
3635 },
3636 };
3637
3638 let pivot_tables = context.map(|c| c.pivot_tables).unwrap_or(&[]);
3639 let pivot = match pivot_tables.iter().find(|p| {
3640 p.dest_sheet_id == sheet_id
3641 && p.last_output_end_row
3642 .is_some_and(|end| target_row >= p.dest_row && target_row <= end)
3643 && p.last_output_end_col
3644 .is_some_and(|end| target_col >= p.dest_col && target_col <= end)
3645 }) {
3646 Some(p) => p,
3647 None => return Ok(ResultData::Error("#REF!".to_string())),
3648 };
3649
3650 let mut criteria: Vec<(String, String)> = Vec::new();
3651 let mut i = 2;
3652 while i < args.len() {
3653 let field = self
3654 .evaluate_ast(&args[i], context, row, col, deps, scope)?
3655 .to_string();
3656 let item = self
3657 .evaluate_ast(&args[i + 1], context, row, col, deps, scope)?
3658 .to_string();
3659 criteria.push((field, item));
3660 i += 2;
3661 }
3662
3663 let mut sheet_refs: Vec<&Sheet> = context
3664 .map(|c| c.sheets.values().copied().collect())
3665 .unwrap_or_default();
3666 sheet_refs.push(self);
3667
3668 match crate::core::pivot::getpivotdata(&sheet_refs, pivot, &data_field, &criteria) {
3669 Ok(v) => Ok(v),
3670 Err(e) => Ok(ResultData::Error(e)),
3671 }
3672 }
3673
3674 #[allow(clippy::too_many_arguments)]
3689 fn evaluate_array_reshape_function(
3690 &self,
3691 func_name: &str,
3692 args: &[crate::core::parser::Expr],
3693 context: Option<&Context>,
3694 row: Option<usize>,
3695 col: Option<usize>,
3696 deps: &mut Vec<Dependency>,
3697 scope: &LetScope<'_>,
3698 ) -> Result<ResultData, EngineError> {
3699 match func_name {
3700 "TRANSPOSE" => {
3701 let Some(arg) = args.first() else {
3702 return Ok(ResultData::Error("#VALUE!".to_string()));
3703 };
3704 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3705 let rows = flat.len().checked_div(cols).unwrap_or(0);
3706 let mut result = Vec::with_capacity(flat.len());
3707 for c in 0..cols {
3708 for r in 0..rows {
3709 result.push(flat[r * cols + c].clone());
3710 }
3711 }
3712 Ok(ResultData::List(result))
3713 }
3714 "HSTACK" | "VSTACK" => {
3715 if args.is_empty() {
3716 return Ok(ResultData::Error("#VALUE!".to_string()));
3717 }
3718 let mut shapes = Vec::with_capacity(args.len());
3719 for a in args {
3720 shapes.push(self.array_shape(a, context, row, col, deps, scope)?);
3721 }
3722 let mut result = Vec::new();
3723 if func_name == "HSTACK" {
3724 let max_rows = shapes
3725 .iter()
3726 .map(|(f, c)| if *c == 0 { 0 } else { f.len() / c })
3727 .max()
3728 .unwrap_or(0);
3729 for r in 0..max_rows {
3730 for (flat, cols) in &shapes {
3731 let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3732 for c in 0..*cols {
3733 result.push(if r < rows {
3734 flat[r * cols + c].clone()
3735 } else {
3736 ResultData::Error("#N/A".to_string())
3737 });
3738 }
3739 }
3740 }
3741 } else {
3742 let max_cols = shapes.iter().map(|(_, c)| *c).max().unwrap_or(0);
3743 for (flat, cols) in &shapes {
3744 let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3745 for r in 0..rows {
3746 for c in 0..max_cols {
3747 result.push(if c < *cols {
3748 flat[r * cols + c].clone()
3749 } else {
3750 ResultData::Error("#N/A".to_string())
3751 });
3752 }
3753 }
3754 }
3755 }
3756 Ok(ResultData::List(result))
3757 }
3758 "CHOOSEROWS" | "CHOOSECOLS" => {
3759 if args.len() < 2 {
3760 return Ok(ResultData::Error("#VALUE!".to_string()));
3761 }
3762 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3763 let rows = flat.len().checked_div(cols).unwrap_or(0);
3764 let total = if func_name == "CHOOSEROWS" {
3765 rows
3766 } else {
3767 cols
3768 } as isize;
3769 let mut indices = Vec::with_capacity(args.len() - 1);
3770 for idx_expr in &args[1..] {
3771 let n = self
3772 .to_f64(&self.evaluate_ast(idx_expr, context, row, col, deps, scope)?)
3773 .unwrap_or(0.0) as isize;
3774 let real_idx = if n < 0 { total + n } else { n - 1 };
3775 if real_idx < 0 || real_idx >= total {
3776 return Ok(ResultData::Error("#VALUE!".to_string()));
3777 }
3778 indices.push(real_idx as usize);
3779 }
3780 let mut result = Vec::new();
3781 if func_name == "CHOOSEROWS" {
3782 for r in indices {
3783 for c in 0..cols {
3784 result.push(flat[r * cols + c].clone());
3785 }
3786 }
3787 } else {
3788 for r in 0..rows {
3789 for &c in &indices {
3790 result.push(flat[r * cols + c].clone());
3791 }
3792 }
3793 }
3794 Ok(ResultData::List(result))
3795 }
3796 "DROP" | "TAKE" => {
3797 if args.len() < 2 {
3798 return Ok(ResultData::Error("#VALUE!".to_string()));
3799 }
3800 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3801 let num_rows = flat.len().checked_div(cols).unwrap_or(0) as isize;
3802 let is_take = func_name == "TAKE";
3803 let rows_n = self
3804 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3805 .unwrap_or(0.0) as isize;
3806 let cols_n = match args.get(2) {
3807 Some(e) => self
3808 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3809 .unwrap_or(0.0) as isize,
3810 None => {
3811 if is_take {
3812 cols as isize
3813 } else {
3814 0
3815 }
3816 }
3817 };
3818 let (row_start, row_end) = Self::drop_take_bounds(num_rows, rows_n, is_take);
3819 let (col_start, col_end) = Self::drop_take_bounds(cols as isize, cols_n, is_take);
3820 if row_start >= row_end || col_start >= col_end {
3821 return Ok(ResultData::Error("#CALC!".to_string()));
3822 }
3823 let mut result = Vec::new();
3824 for r in row_start..row_end {
3825 for c in col_start..col_end {
3826 result.push(flat[(r as usize) * cols + (c as usize)].clone());
3827 }
3828 }
3829 Ok(ResultData::List(result))
3830 }
3831 "EXPAND" => {
3832 if args.len() < 2 {
3833 return Ok(ResultData::Error("#VALUE!".to_string()));
3834 }
3835 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3836 let orig_rows = flat.len().checked_div(cols).unwrap_or(0);
3837 let new_rows = self
3838 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3839 .unwrap_or(orig_rows as f64) as usize;
3840 let new_cols = match args.get(2) {
3841 Some(e) => self
3842 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3843 .unwrap_or(cols as f64) as usize,
3844 None => cols,
3845 };
3846 let pad = match args.get(3) {
3847 Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3848 None => ResultData::Error("#N/A".to_string()),
3849 };
3850 if new_rows < orig_rows || new_cols < cols {
3851 return Ok(ResultData::Error("#VALUE!".to_string()));
3852 }
3853 let mut result = Vec::with_capacity(new_rows * new_cols);
3854 for r in 0..new_rows {
3855 for c in 0..new_cols {
3856 result.push(if r < orig_rows && c < cols {
3857 flat[r * cols + c].clone()
3858 } else {
3859 pad.clone()
3860 });
3861 }
3862 }
3863 Ok(ResultData::List(result))
3864 }
3865 "TOCOL" | "TOROW" => {
3866 let Some(arg) = args.first() else {
3867 return Ok(ResultData::Error("#VALUE!".to_string()));
3868 };
3869 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3870 let rows = flat.len().checked_div(cols).unwrap_or(0);
3871 let ignore = match args.get(1) {
3872 Some(e) => self
3873 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3874 .unwrap_or(0.0) as i64,
3875 None => 0,
3876 };
3877 let scan_by_col = match args.get(2) {
3878 Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3879 None => false,
3880 };
3881 let ordered: Vec<ResultData> = if scan_by_col {
3882 let mut v = Vec::with_capacity(flat.len());
3883 for c in 0..cols {
3884 for r in 0..rows {
3885 v.push(flat[r * cols + c].clone());
3886 }
3887 }
3888 v
3889 } else {
3890 flat
3891 };
3892 let filtered: Vec<ResultData> = ordered
3893 .into_iter()
3894 .filter(|v| match ignore {
3895 1 => !matches!(v, ResultData::None),
3896 2 => !matches!(v, ResultData::Error(_)),
3897 3 => !matches!(v, ResultData::None | ResultData::Error(_)),
3898 _ => true,
3899 })
3900 .collect();
3901 Ok(ResultData::List(filtered))
3902 }
3903 "WRAPROWS" | "WRAPCOLS" => {
3904 if args.len() < 2 {
3905 return Ok(ResultData::Error("#VALUE!".to_string()));
3906 }
3907 let (flat, _cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3908 let wrap = self
3909 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3910 .unwrap_or(1.0)
3911 .max(1.0) as usize;
3912 let pad = match args.get(2) {
3913 Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3914 None => ResultData::Error("#N/A".to_string()),
3915 };
3916 if func_name == "WRAPROWS" {
3917 let mut result = flat;
3920 let rem = result.len() % wrap;
3921 if rem != 0 {
3922 result.extend(std::iter::repeat_n(pad, wrap - rem));
3923 }
3924 Ok(ResultData::List(result))
3925 } else {
3926 let num_result_cols = flat.len().div_ceil(wrap).max(1);
3927 let total = wrap * num_result_cols;
3928 let mut result = Vec::with_capacity(total);
3929 for i in 0..total {
3930 let col = i / wrap;
3931 let r = i % wrap;
3932 let target = r * num_result_cols + col;
3933 while result.len() <= target {
3934 result.push(pad.clone());
3935 }
3936 if i < flat.len() {
3937 result[target] = flat[i].clone();
3938 }
3939 }
3940 Ok(ResultData::List(result))
3941 }
3942 }
3943 "UNIQUE" => {
3944 let Some(arg) = args.first() else {
3945 return Ok(ResultData::Error("#VALUE!".to_string()));
3946 };
3947 let (flat, _cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3948 let exactly_once = match args.get(2) {
3949 Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3950 None => false,
3951 };
3952 let mut seen: Vec<(String, ResultData, usize)> = Vec::new();
3953 for v in &flat {
3954 let key = match v {
3958 ResultData::None => "blank:".to_string(),
3959 ResultData::Boolean(b) => format!("bool:{b}"),
3960 ResultData::Integer(i) => format!("num:{}", *i as f64),
3961 ResultData::Float(f) => format!("num:{f}"),
3962 ResultData::String(s) => format!("str:{s}"),
3963 ResultData::Error(e) => format!("err:{e}"),
3964 ResultData::List(_) | ResultData::Dict(_) => format!("other:{v}"),
3965 };
3966 match seen.iter_mut().find(|(k, ..)| k == &key) {
3967 Some(entry) => entry.2 += 1,
3968 None => seen.push((key, v.clone(), 1)),
3969 }
3970 }
3971 let result: Vec<ResultData> = seen
3972 .into_iter()
3973 .filter(|(_, _, count)| !exactly_once || *count == 1)
3974 .map(|(_, v, _)| v)
3975 .collect();
3976 Ok(ResultData::List(result))
3977 }
3978 "SORT" => {
3979 let Some(arg) = args.first() else {
3980 return Ok(ResultData::Error("#VALUE!".to_string()));
3981 };
3982 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3983 let rows = flat.len().checked_div(cols).unwrap_or(0);
3984 let sort_index = match args.get(1) {
3985 Some(e) => self
3986 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3987 .unwrap_or(1.0) as usize,
3988 None => 1,
3989 };
3990 let sort_order = match args.get(2) {
3991 Some(e) => self
3992 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3993 .unwrap_or(1.0),
3994 None => 1.0,
3995 };
3996 let col_idx = sort_index.saturating_sub(1).min(cols.saturating_sub(1));
3997 let mut row_indices: Vec<usize> = (0..rows).collect();
3998 row_indices.sort_by(|&a, &b| {
3999 Self::sort_compare_blanks_last(
4000 &flat[a * cols + col_idx],
4001 &flat[b * cols + col_idx],
4002 sort_order,
4003 )
4004 });
4005 let mut result = Vec::with_capacity(flat.len());
4006 for r in row_indices {
4007 for c in 0..cols {
4008 result.push(flat[r * cols + c].clone());
4009 }
4010 }
4011 Ok(ResultData::List(result))
4012 }
4013 "SORTBY" => {
4014 if args.len() < 2 {
4015 return Ok(ResultData::Error("#VALUE!".to_string()));
4016 }
4017 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
4018 let rows = flat.len().checked_div(cols).unwrap_or(0);
4019 let by = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
4020 let order = match args.get(2) {
4021 Some(e) => self
4022 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
4023 .unwrap_or(1.0),
4024 None => 1.0,
4025 };
4026 let mut row_indices: Vec<usize> = (0..rows).collect();
4027 row_indices.sort_by(|&a, &b| {
4028 let va = by.get(a).cloned().unwrap_or(ResultData::None);
4029 let vb = by.get(b).cloned().unwrap_or(ResultData::None);
4030 Self::sort_compare_blanks_last(&va, &vb, order)
4031 });
4032 let mut result = Vec::with_capacity(flat.len());
4033 for r in row_indices {
4034 for c in 0..cols {
4035 result.push(flat[r * cols + c].clone());
4036 }
4037 }
4038 Ok(ResultData::List(result))
4039 }
4040 "FILTER" => {
4041 if args.len() < 2 {
4042 return Ok(ResultData::Error("#VALUE!".to_string()));
4043 }
4044 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
4045 let rows = flat.len().checked_div(cols).unwrap_or(0);
4046 let include = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
4047 let mut result = Vec::new();
4048 for r in 0..rows {
4049 let keep = include.get(r).map(|v| self.to_bool(v)).unwrap_or(false);
4050 if keep {
4051 for c in 0..cols {
4052 result.push(flat[r * cols + c].clone());
4053 }
4054 }
4055 }
4056 if result.is_empty() {
4057 match args.get(2) {
4058 Some(e) => Ok(self.evaluate_ast(e, context, row, col, deps, scope)?),
4059 None => Ok(ResultData::Error("#CALC!".to_string())),
4060 }
4061 } else {
4062 Ok(ResultData::List(result))
4063 }
4064 }
4065 "TRIMRANGE" => {
4066 let Some(arg) = args.first() else {
4067 return Ok(ResultData::Error("#VALUE!".to_string()));
4068 };
4069 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
4070 let rows = flat.len().checked_div(cols).unwrap_or(0);
4071 let is_blank = |v: &ResultData| {
4072 matches!(v, ResultData::None)
4073 || matches!(v, ResultData::String(s) if s.is_empty())
4074 };
4075 let row_blank = |r: usize| (0..cols).all(|c| is_blank(&flat[r * cols + c]));
4076 let col_blank = |c: usize| (0..rows).all(|r| is_blank(&flat[r * cols + c]));
4077 let mut r_start = 0;
4078 while r_start < rows && row_blank(r_start) {
4079 r_start += 1;
4080 }
4081 let mut r_end = rows;
4082 while r_end > r_start && row_blank(r_end - 1) {
4083 r_end -= 1;
4084 }
4085 let mut c_start = 0;
4086 while c_start < cols && col_blank(c_start) {
4087 c_start += 1;
4088 }
4089 let mut c_end = cols;
4090 while c_end > c_start && col_blank(c_end - 1) {
4091 c_end -= 1;
4092 }
4093 let mut result = Vec::new();
4094 for r in r_start..r_end {
4095 for c in c_start..c_end {
4096 result.push(flat[r * cols + c].clone());
4097 }
4098 }
4099 Ok(ResultData::List(result))
4100 }
4101 _ => unreachable!(),
4102 }
4103 }
4104
4105 pub fn get_src(&self, cell: &CellRef) -> Option<&String> {
4112 let col = self.columns.get(cell.col);
4113 if let Some(col) = col {
4114 col.src.get(cell.row)
4115 } else {
4116 None
4117 }
4118 }
4119
4120 pub fn get_src_str(&self, cell: &CellRef) -> String {
4123 let col = self.columns.get(cell.col);
4124 if let Some(col) = col {
4125 col.src.get(cell.row).cloned().unwrap_or("".to_string())
4126 } else {
4127 "".to_string()
4128 }
4129 }
4130
4131 pub fn get_src_str_ref(&self, cell: &CellRef) -> Option<&str> {
4133 let col = self.columns.get(cell.col)?;
4134 col.src.get(cell.row).map(|s| s.as_str())
4135 }
4136
4137 pub fn get_word_boundaries(&self, cell: &CellRef, char_offset: usize) -> (usize, usize) {
4141 let text = self.get_src_str(cell);
4142 get_word_boundaries_from_str(&text, char_offset)
4143 }
4144}