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::{
14 CellRef, CellType, Dependency, EngineError, EvalError, TextCellRef, generate_unique_id,
15};
16use super::column::DataColumn;
17use super::result_data::ResultData;
18#[derive(Default)]
20pub struct Context<'a> {
21 pub sheets: HashMap<String, &'a Sheet>,
23 pub pivot_tables: &'a [crate::core::pivot::PivotTable],
29 pub sheet_order: Vec<String>,
35}
36
37impl<'a> Context<'a> {
38 pub fn new() -> Self {
40 Self {
41 sheets: HashMap::new(),
42 pivot_tables: &[],
43 sheet_order: Vec::new(),
44 }
45 }
46
47 pub fn add_table(&mut self, name: String, sheet: &'a Sheet) {
49 self.sheets.insert(name, sheet);
50 }
51}
52
53enum LetScope<'a> {
61 Empty,
62 Bound {
63 name: &'a str,
64 value: &'a ResultData,
65 parent: &'a LetScope<'a>,
66 },
67}
68
69impl<'a> LetScope<'a> {
70 fn get(&self, name: &str) -> Option<&ResultData> {
71 match self {
72 LetScope::Empty => None,
73 LetScope::Bound {
74 name: n,
75 value,
76 parent,
77 } => {
78 if n.eq_ignore_ascii_case(name) {
79 Some(value)
80 } else {
81 parent.get(name)
82 }
83 }
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq)]
90pub enum Direction {
91 None,
93 Up,
95 Down,
97 Left,
99 Right,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct Sheet {
139 #[serde(default = "generate_unique_id")]
142 pub id: u64,
143 pub name: String,
145 pub(crate) columns: Vec<DataColumn>,
152 #[serde(default)]
154 pub tables: Vec<crate::core::table::ExcelTable>,
155 #[serde(skip, default)]
158 pub dependencies: HashMap<Dependency, HashSet<CellRef>>,
159 #[serde(skip, default)]
162 pub dependencies_rev: HashMap<CellRef, HashSet<Dependency>>,
163 #[serde(skip)]
166 pub uncommitted_actions: Vec<crate::core::SheetAction>,
167 #[serde(default)]
169 pub locale: crate::core::locale::Locale,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct SheetInit {
176 #[serde(default)]
178 pub id: Option<u64>,
179 pub name: Option<String>,
181 pub rows: usize,
183 pub cols: usize,
185}
186
187impl Default for SheetInit {
188 fn default() -> Self {
189 Self {
190 id: None,
191 name: None,
192 rows: 10,
193 cols: 5,
194 }
195 }
196}
197
198#[derive(Clone, Copy, PartialEq, Eq)]
200enum BlankPolicy {
201 Zero,
203 Skip,
205 Reject,
207}
208
209impl Sheet {
210 pub fn new(args: SheetInit) -> Sheet {
213 let SheetInit {
214 id,
215 name,
216 rows,
217 cols,
218 } = args;
219 let sheet_id = id.unwrap_or_else(generate_unique_id);
220 let sheet_name = name.unwrap_or_else(|| "table_1".to_string());
221
222 let mut columns = Vec::with_capacity(cols);
223 for _ in 0..cols {
224 columns.push(DataColumn::new(rows));
225 }
226
227 let mut uncommitted_actions = Vec::new();
228 for c in 0..cols {
229 for r in 0..rows {
230 uncommitted_actions.push(crate::core::SheetAction::SetCellSrc {
231 sheet_name: sheet_name.clone(),
232 col: c,
233 row: r,
234 src: String::new(),
235 });
236 }
237 }
238
239 Self {
240 id: sheet_id,
241 name: sheet_name,
242 columns,
243 tables: Vec::new(),
244 dependencies: HashMap::new(),
245 dependencies_rev: HashMap::new(),
246 uncommitted_actions,
247 locale: crate::core::locale::Locale::default(),
248 }
249 }
250
251 pub fn setup_after_deserialization(&mut self) {
258 for col in &mut self.columns {
259 col.rebuild_after_load();
260 }
261 self.mark_all_dirty();
262 }
263
264 pub(crate) fn get_all_tables_for_compilation(&self, context: Option<&Context>) -> Vec<Sheet> {
272 let mut list = vec![self.clone()];
273 let mut seen = std::collections::HashSet::new();
274 seen.insert(self.id);
275 if let Some(ctx) = context {
276 for t in ctx.sheets.values() {
277 if !seen.contains(&t.id) {
278 seen.insert(t.id);
279 list.push((*t).clone());
280 }
281 }
282 }
283 list
284 }
285
286 pub fn mark_all_dirty(&mut self) {
291 for col in &mut self.columns {
292 col.dirty_indices.clear();
293 col.dirty_indices.extend(0..col.src.len());
294 }
295 }
296
297 pub fn commit(&mut self, context: Option<&Context>) -> Result<HashSet<CellRef>, EngineError> {
299 let mut queue: VecDeque<CellRef> = VecDeque::new();
300 let mut queue_set: HashSet<CellRef> = HashSet::new();
301 let mut updated_cells: HashSet<CellRef> = HashSet::new();
302
303 for (col_idx, col_data) in self.columns.iter_mut().enumerate() {
305 for row_idx in &col_data.dirty_indices {
306 let cell = CellRef::new(*row_idx, col_idx);
307 queue.push_back(cell);
308 queue_set.insert(cell);
309 updated_cells.insert(cell);
310 }
311 col_data.dirty_indices.clear();
312 }
313
314 let initial_queue_len = queue.len();
315 if initial_queue_len == 0 {
316 return Ok(updated_cells);
317 }
318
319 let start_commit = Instant::now();
320 log::info!(
321 "Sheet '{}' commit starting for {} dirty cells",
322 self.name,
323 initial_queue_len
324 );
325 let max_ops = 10000.max(initial_queue_len * 3);
326 let mut ops = 0;
327
328 let mut tables_for_compilation = self.get_all_tables_for_compilation(context);
329 let mut last_log_time = Instant::now();
330
331 while let Some(cell_ref) = queue.pop_front() {
332 queue_set.remove(&cell_ref);
333 ops += 1;
334 if ops > max_ops {
335 println!("Circular dependency or too many updates detected");
336 break;
337 }
338
339 if ops % 50000 == 0 {
340 log::info!(
341 "Sheet '{}' commit progress: {}/{} cells processed ({:.2?})",
342 self.name,
343 ops,
344 initial_queue_len,
345 last_log_time.elapsed()
346 );
347 last_log_time = Instant::now();
348 }
349
350 let cell_type_hint = self
351 .columns
352 .get(cell_ref.col)
353 .and_then(|c| c.cell_types.get(cell_ref.row).copied())
354 .unwrap_or(CellType::Auto);
355
356 let mut detected_num_format: Option<String> = None;
359 let (result, new_deps, compiled_to_cache, final_cell_type) = {
360 let src = self.get_src_str_ref(&cell_ref).unwrap_or("");
361 if cell_type_hint == CellType::String {
362 let val = if src.starts_with('"') && src.ends_with('"') && src.len() >= 2 {
363 src[1..src.len() - 1].to_string()
364 } else {
365 src.to_string()
366 };
367 (ResultData::String(val), vec![], None, CellType::String)
368 } else if !src.starts_with('=') && cell_type_hint != CellType::Formula {
369 let (res, c_type) = if let Some(stripped) = src.strip_prefix('\'') {
375 (ResultData::String(stripped.to_string()), CellType::String)
376 } else if src.is_empty() {
377 (ResultData::None, CellType::Empty)
378 } else if src.starts_with('"') && src.ends_with('"') && src.len() >= 2 {
379 (
380 ResultData::String(src[1..src.len() - 1].to_string()),
381 CellType::String,
382 )
383 } else if let Ok(i) = src.trim().parse::<i64>() {
384 (ResultData::Integer(i), CellType::Number)
385 } else if let Ok(f) = src.trim().parse::<f64>()
386 && f.is_finite()
394 {
395 (ResultData::Float(f), CellType::Number)
396 } else if crate::core::engine::result_data::is_excel_error_code(src) {
397 (ResultData::Error(src.to_uppercase()), CellType::Error)
400 } else if src.eq_ignore_ascii_case("true") {
401 (ResultData::Boolean(true), CellType::Boolean)
402 } else if src.eq_ignore_ascii_case("false") {
403 (ResultData::Boolean(false), CellType::Boolean)
404 } else if let Some((date, format)) = crate::core::date::parse_date_with_locale(
405 src.trim_matches(' '),
406 &self.locale,
407 ) {
408 detected_num_format = Some(format.to_format_code());
412 (
413 ResultData::Float(crate::core::date::date_to_excel_serial(date)),
414 CellType::Number,
415 )
416 } else if let Some(f) = crate::core::date_fn::parse_time_fraction(src) {
417 (ResultData::Float(f), CellType::Number)
421 } else {
422 (ResultData::String(src.to_string()), CellType::String)
423 };
424 (res, vec![], None, c_type)
425 } else {
426 let compiled =
427 crate::core::parser::compile_formula(src, &tables_for_compilation);
428 let eval_src =
429 crate::core::parser::serialize_formula(&compiled, &tables_for_compilation);
430 let (res, deps) = match self.eval_with_row(
431 &eval_src,
432 context,
433 Some(cell_ref.row),
434 Some(cell_ref.col),
435 ) {
436 Ok(r) => r,
437 Err(e) => (ResultData::Error(e.to_string()), vec![]),
438 };
439 let final_res = if let ResultData::None = res {
440 ResultData::Float(0.0)
441 } else {
442 res
443 };
444 (final_res, deps, Some(compiled), CellType::Formula)
445 }
446 };
447
448 if let Some(src_str) = self.get_src_str_ref(&cell_ref)
450 && let Some(stripped) = src_str.strip_prefix('\'')
451 {
452 let stripped_str = stripped.to_string();
453 if let Some(col) = self.columns.get_mut(cell_ref.col)
454 && cell_ref.row < col.src.len()
455 {
456 col.src[cell_ref.row] = stripped_str;
457 }
458 }
459
460 if let Some(col) = self.columns.get_mut(cell_ref.col)
462 && cell_ref.row < col.compiled_src.len()
463 {
464 col.compiled_src[cell_ref.row] = compiled_to_cache.unwrap_or_default();
465 }
466
467 if let Some(old_deps) = self.dependencies_rev.remove(&cell_ref) {
470 for provider in old_deps {
471 if let Some(dependents) = self.dependencies.get_mut(&provider) {
472 dependents.remove(&cell_ref);
473 }
474 }
475 }
476
477 if !new_deps.is_empty() {
479 let mut new_deps_set = HashSet::new();
480 for provider in new_deps {
481 new_deps_set.insert(provider.clone());
482 self.dependencies
483 .entry(provider)
484 .or_default()
485 .insert(cell_ref);
486 }
487 self.dependencies_rev.insert(cell_ref, new_deps_set);
488 }
489
490 let inherited = if detected_num_format.is_some()
495 || !matches!(result, ResultData::Float(_) | ResultData::Integer(_))
496 {
497 None
498 } else {
499 self.get_src_str_ref(&cell_ref)
500 .and_then(|src| src.strip_prefix('='))
501 .and_then(|body| crate::core::parser::parse_excel_formula(body).ok())
502 .and_then(|ast| self.inherited_date_format(&ast))
503 };
504 if let Some(code) = detected_num_format.or(inherited) {
507 let existing = self
508 .get_cell_style(cell_ref.row, cell_ref.col)
509 .and_then(|s| s.num_format.clone());
510 if existing.is_none() {
511 self.update_cell_style(cell_ref.row, cell_ref.col, |style| {
512 style.num_format = Some(code);
513 });
514 }
515 }
516
517 if let Some(col) = self.columns.get_mut(cell_ref.col)
519 && cell_ref.row < col.data.len()
520 {
521 col.cell_types[cell_ref.row] = final_cell_type;
522 col.data.set(cell_ref.row, result.clone());
523 updated_cells.insert(cell_ref);
524 }
525 if let Some(comp_sheet) = tables_for_compilation
526 .iter_mut()
527 .find(|s| s.name == self.name)
528 && let Some(col) = comp_sheet.columns.get_mut(cell_ref.col)
529 && cell_ref.row < col.data.len()
530 {
531 col.cell_types[cell_ref.row] = final_cell_type;
532 col.data.set(cell_ref.row, result);
533 }
534
535 let local_dep_key = Dependency::Local(cell_ref);
539 if let Some(dependents) = self.dependencies.get(&local_dep_key) {
540 for dependent in dependents {
541 if !queue_set.contains(dependent) {
542 queue.push_back(*dependent);
543 queue_set.insert(*dependent);
544 }
545 }
546 }
547
548 let local_col_dep_key = Dependency::LocalColumn(cell_ref.col);
550 if let Some(dependents) = self.dependencies.get(&local_col_dep_key) {
551 for dependent in dependents {
552 if !queue_set.contains(dependent) {
553 queue.push_back(*dependent);
554 queue_set.insert(*dependent);
555 }
556 }
557 }
558 }
559 if initial_queue_len > 0 {
560 log::info!(
561 "Sheet '{}' commit finished. Processed {} cell updates. Total time: {:.2?}",
562 self.name,
563 ops,
564 start_commit.elapsed()
565 );
566 }
567 Ok(updated_cells)
568 }
569
570 pub fn eval_with_row(
583 &self,
584 input: &str,
585 context: Option<&Context>,
586 row: Option<usize>,
587 col: Option<usize>,
588 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
589 if input.is_empty() {
590 return Ok((ResultData::None, vec![]));
591 }
592 if let Some(formula) = input.strip_prefix('=') {
593 self.eval_excel(formula, context, row, col)
594 } else {
595 if let Ok(i) = input.parse::<i64>() {
596 Ok((ResultData::Integer(i), vec![]))
597 } else if let Ok(f) = input.parse::<f64>() {
598 Ok((ResultData::Float(f), vec![]))
599 } else if let Ok(b) = input.parse::<bool>() {
600 Ok((ResultData::Boolean(b), vec![]))
601 } else {
602 Ok((ResultData::String(input.to_string()), vec![]))
603 }
604 }
605 }
606
607 pub fn eval(
620 &self,
621 input: &str,
622 context: Option<&Context>,
623 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
624 self.eval_with_row(input, context, None, None)
625 }
626
627 fn eval_excel(
628 &self,
629 code: &str,
630 context: Option<&Context>,
631 row: Option<usize>,
632 col: Option<usize>,
633 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
634 let ast = crate::core::parser::parse_excel_formula(code)
635 .map_err(|e| EngineError::EvalError(EvalError::UnknownFunction(e)))?;
636
637 let mut deps = Vec::new();
638 let result = match self.evaluate_ast(&ast, context, row, col, &mut deps, &LetScope::Empty) {
639 Ok(r) => r,
640 Err(EngineError::EvalError(EvalError::UnknownFunction(err_str)))
641 if err_str.starts_with('#') =>
642 {
643 ResultData::Error(err_str)
644 }
645 Err(e) => return Err(e),
646 };
647 Ok((result, deps))
648 }
649
650 fn evaluate_ast(
651 &self,
652 ast: &crate::core::parser::Expr,
653 context: Option<&Context>,
654 row: Option<usize>,
655 col: Option<usize>,
656 deps: &mut Vec<Dependency>,
657 scope: &LetScope<'_>,
658 ) -> Result<ResultData, EngineError> {
659 use crate::core::SheetSection;
660 use crate::core::parser::Expr;
661 use crate::core::parser::Op;
662
663 match ast {
664 Expr::Number(n) => Ok(ResultData::Float(*n)),
665 Expr::String(s) => Ok(ResultData::String(s.clone())),
666 Expr::Boolean(b) => Ok(ResultData::Boolean(*b)),
667 Expr::Error(code) => Ok(ResultData::Error(code.to_string())),
668 Expr::Identifier(name) => match scope.get(name) {
669 Some(val) => Ok(val.clone()),
670 None => Ok(ResultData::Error("#NAME?".to_string())),
671 },
672 Expr::StructuredRef {
673 sheet,
674 column,
675 is_this_row,
676 section,
677 } => {
678 let ref_name = match sheet {
679 Some(name) => name.clone(),
680 None => self.name.clone(),
681 };
682
683 let mut found: Option<(&Sheet, &crate::core::table::ExcelTable)> =
691 self.find_table(&ref_name).map(|t| (self, t));
692 if found.is_none()
693 && let Some(ctx) = context
694 {
695 for s in ctx.sheets.values() {
696 if let Some(t) = s.find_table(&ref_name) {
697 found = Some((s, t));
698 break;
699 }
700 }
701 }
702
703 if let Some((table_sheet, excel_table)) = found {
704 let is_self = table_sheet.name == self.name;
705 let sheet_name = table_sheet.name.clone();
706
707 let col_indices: Vec<(usize, usize)> = if let Some(col_name) = column {
709 let local = excel_table.local_column_index(col_name).ok_or_else(|| {
710 EngineError::EvalError(EvalError::UnknownFunction(format!(
711 "Column not found: {}",
712 col_name
713 )))
714 })?;
715 vec![(local, excel_table.start_col + local)]
716 } else {
717 (0..excel_table.columns.len())
718 .map(|local| (local, excel_table.start_col + local))
719 .collect()
720 };
721 let is_whole_table = column.is_none();
722
723 match section {
724 SheetSection::Headers => {
725 let names: Vec<ResultData> = col_indices
726 .iter()
727 .map(|&(local, _)| {
728 ResultData::String(
729 excel_table.columns.get(local).cloned().unwrap_or_default(),
730 )
731 })
732 .collect();
733 if is_whole_table {
734 Ok(ResultData::List(names))
735 } else {
736 Ok(names.into_iter().next().unwrap_or(ResultData::None))
737 }
738 }
739 SheetSection::Totals => {
740 if let Some(totals_row) = excel_table.totals_row() {
741 let mut results = Vec::new();
742 for &(_, col_idx) in &col_indices {
743 let cell_ref = CellRef::new(totals_row, col_idx);
744 if is_self {
745 deps.push(Dependency::Local(cell_ref));
746 } else {
747 deps.push(Dependency::Remote {
748 sheet: sheet_name.clone(),
749 cell: cell_ref,
750 });
751 }
752 results.push(table_sheet.get_result_data(&cell_ref));
753 }
754 if is_whole_table {
755 Ok(ResultData::List(results))
756 } else {
757 Ok(results.into_iter().next().unwrap_or(ResultData::None))
758 }
759 } else {
760 Ok(ResultData::None)
761 }
762 }
763 SheetSection::Data | SheetSection::All => {
764 if *is_this_row {
765 let r = row.ok_or_else(|| {
766 EngineError::EvalError(EvalError::UnknownFunction(
767 "This row reference cannot be evaluated without row context"
768 .to_string(),
769 ))
770 })?;
771 let mut results = Vec::new();
772 for &(_, col_idx) in &col_indices {
773 let cell_ref = CellRef::new(r, col_idx);
774 if is_self {
775 deps.push(Dependency::Local(cell_ref));
776 } else {
777 deps.push(Dependency::Remote {
778 sheet: sheet_name.clone(),
779 cell: cell_ref,
780 });
781 }
782 results.push(table_sheet.get_result_data(&cell_ref));
783 }
784 if is_whole_table {
785 Ok(ResultData::List(results))
786 } else {
787 Ok(results.into_iter().next().unwrap_or(ResultData::None))
788 }
789 } else {
790 let mut results = Vec::new();
804 for &(_, col_idx) in &col_indices {
805 for r in
806 excel_table.data_start_row()..=excel_table.data_end_row()
807 {
808 let cell_ref = CellRef::new(r, col_idx);
809 if is_self {
810 deps.push(Dependency::Local(cell_ref));
811 } else {
812 deps.push(Dependency::Remote {
813 sheet: sheet_name.clone(),
814 cell: cell_ref,
815 });
816 }
817 results.push(table_sheet.get_result_data(&cell_ref));
818 }
819 }
820 Ok(ResultData::List(results))
821 }
822 }
823 }
824 } else {
825 let sheet_name = ref_name;
829 let is_self = sheet_name == self.name;
830
831 let target_table = if is_self {
832 self
833 } else if let Some(ctx) = context {
834 if let Some(t) = ctx.sheets.get(&sheet_name) {
835 t
836 } else {
837 return Err(EngineError::EvalError(EvalError::UnknownFunction(
838 format!("Sheet not found: {}", sheet_name),
839 )));
840 }
841 } else {
842 return Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
843 "No context to resolve sheet reference: {}",
844 sheet_name
845 ))));
846 };
847
848 let col_indices: Vec<usize> = if let Some(col_name) = column {
852 let pos = target_table
853 .columns
854 .iter()
855 .position(|c| c.name == *col_name)
856 .ok_or_else(|| {
857 EngineError::EvalError(EvalError::UnknownFunction(format!(
858 "Column not found: {}",
859 col_name
860 )))
861 })?;
862 vec![pos]
863 } else {
864 (0..target_table.columns.len()).collect()
865 };
866 let is_whole_table = column.is_none();
867
868 match section {
869 SheetSection::Headers => {
870 let names: Vec<ResultData> = col_indices
871 .iter()
872 .map(|&idx| {
873 ResultData::String(
874 target_table
875 .columns
876 .get(idx)
877 .map(|c| c.name.clone())
878 .unwrap_or_default(),
879 )
880 })
881 .collect();
882 if is_whole_table {
883 Ok(ResultData::List(names))
884 } else {
885 Ok(names.into_iter().next().unwrap_or(ResultData::None))
886 }
887 }
888 SheetSection::Totals => Ok(ResultData::None),
889 SheetSection::Data | SheetSection::All => {
890 if *is_this_row {
891 let r = row.ok_or_else(|| {
892 EngineError::EvalError(EvalError::UnknownFunction(
893 "This row reference cannot be evaluated without row context"
894 .to_string(),
895 ))
896 })?;
897 let mut results = Vec::new();
898 for &col_idx in &col_indices {
899 let cell_ref = CellRef::new(r, col_idx);
900 if is_self {
901 deps.push(Dependency::Local(cell_ref));
902 } else {
903 deps.push(Dependency::Remote {
904 sheet: sheet_name.clone(),
905 cell: cell_ref,
906 });
907 }
908 results.push(target_table.get_result_data(&cell_ref));
909 }
910 if is_whole_table {
911 Ok(ResultData::List(results))
912 } else {
913 Ok(results.into_iter().next().unwrap_or(ResultData::None))
914 }
915 } else {
916 let mut results = Vec::new();
917 for &col_idx in &col_indices {
918 if is_self {
919 deps.push(Dependency::LocalColumn(col_idx));
920 } else {
921 deps.push(Dependency::RemoteColumn {
922 sheet: sheet_name.clone(),
923 col: col_idx,
924 });
925 }
926 for r in 0..target_table.row_count() {
927 let cell_ref = CellRef::new(r, col_idx);
928 results.push(target_table.get_result_data(&cell_ref));
929 }
930 }
931 Ok(ResultData::List(results))
932 }
933 }
934 }
935 }
936 }
937 Expr::CellRef {
938 sheet,
939 row: r_val,
940 col,
941 ..
942 } => {
943 let cell_ref = CellRef::new(*r_val, *col);
944 let is_self = match sheet {
945 Some(name) => name == &self.name,
946 None => true,
947 };
948
949 if is_self {
950 deps.push(Dependency::Local(cell_ref));
951 Ok(self.get_result_data(&cell_ref))
952 } else {
953 let name = sheet.as_ref().unwrap().clone();
954 deps.push(Dependency::Remote {
955 sheet: name.clone(),
956 cell: cell_ref,
957 });
958
959 if let Some(ctx) = context {
960 if let Some(t) = ctx.sheets.get(&name) {
961 Ok(t.get_result_data(&cell_ref))
962 } else {
963 Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
964 "Sheet not found: {}",
965 name
966 ))))
967 }
968 } else {
969 Err(EngineError::EvalError(EvalError::UnknownFunction(
970 "No context to resolve sheet reference".to_string(),
971 )))
972 }
973 }
974 }
975 Expr::RangeRef {
976 sheet,
977 start_row,
978 start_col,
979 end_row,
980 end_col,
981 ..
982 } => {
983 let is_self = match sheet {
984 Some(name) => name == &self.name,
985 None => true,
986 };
987
988 let target_sheet = if is_self {
989 Some(self)
990 } else {
991 context.and_then(|ctx| ctx.sheets.get(sheet.as_ref().unwrap()).copied())
992 };
993
994 let actual_end_row = if *end_row == usize::MAX {
995 target_sheet
996 .map(|t| t.row_count().saturating_sub(1))
997 .unwrap_or(0)
998 } else {
999 *end_row
1000 };
1001 let actual_end_col = if *end_col == usize::MAX {
1002 target_sheet
1003 .map(|t| t.col_count().saturating_sub(1))
1004 .unwrap_or(0)
1005 } else {
1006 *end_col
1007 };
1008
1009 let is_col_range = *end_row == usize::MAX;
1010
1011 let mut seen_col_deps: HashSet<usize> = HashSet::new();
1027
1028 let mut results = Vec::new();
1029 for r in *start_row..=actual_end_row {
1030 for c in *start_col..=actual_end_col {
1031 let cell_ref = CellRef::new(r, c);
1032 if is_self {
1033 if is_col_range {
1034 if seen_col_deps.insert(c) {
1035 let col_dep = Dependency::LocalColumn(c);
1036 if !deps.contains(&col_dep) {
1037 deps.push(col_dep);
1038 }
1039 }
1040 } else {
1041 deps.push(Dependency::Local(cell_ref));
1042 }
1043 if row == Some(r) && col == Some(c) {
1060 results.push(ResultData::None);
1061 } else {
1062 results.push(self.get_result_data(&cell_ref));
1063 }
1064 } else {
1065 let name = sheet.as_ref().unwrap().clone();
1066 if is_col_range {
1067 if seen_col_deps.insert(c) {
1068 let col_dep = Dependency::RemoteColumn {
1069 sheet: name.clone(),
1070 col: c,
1071 };
1072 if !deps.contains(&col_dep) {
1073 deps.push(col_dep);
1074 }
1075 }
1076 } else {
1077 deps.push(Dependency::Remote {
1078 sheet: name.clone(),
1079 cell: cell_ref,
1080 });
1081 }
1082 if let Some(ctx) = context {
1083 if let Some(t) = ctx.sheets.get(&name) {
1084 results.push(t.get_result_data(&cell_ref));
1085 } else {
1086 return Err(EngineError::EvalError(
1087 EvalError::UnknownFunction(format!(
1088 "Sheet not found: {}",
1089 name
1090 )),
1091 ));
1092 }
1093 } else {
1094 return Err(EngineError::EvalError(EvalError::UnknownFunction(
1095 "No context to resolve sheet reference".to_string(),
1096 )));
1097 }
1098 }
1099 }
1100 }
1101 Ok(ResultData::List(results))
1102 }
1103 Expr::List(list) => {
1104 let mut results = Vec::new();
1105 for item in list {
1106 results.push(self.evaluate_ast(item, context, row, col, deps, scope)?);
1107 }
1108 Ok(ResultData::List(results))
1109 }
1110 Expr::UnaryOp { op, expr } => {
1111 let val = self.evaluate_ast(expr, context, row, col, deps, scope)?;
1112 match op {
1113 Op::Sub => match val {
1114 ResultData::Float(f) => Ok(ResultData::Float(-f)),
1115 ResultData::Integer(i) => Ok(ResultData::Integer(-i)),
1116 _ => Err(EngineError::EvalError(EvalError::UnknownFunction(
1117 "Unary minus expects number".to_string(),
1118 ))),
1119 },
1120 _ => Ok(val),
1121 }
1122 }
1123 Expr::BinaryOp { op, left, right } => {
1124 let l_val = self.evaluate_ast(left, context, row, col, deps, scope)?;
1125
1126 match op {
1127 Op::Eq | Op::Ne | Op::Lt | Op::Gt | Op::Le | Op::Ge => {
1128 if let ResultData::Error(_) = &l_val {
1129 return Ok(l_val);
1130 }
1131 let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1132 if let ResultData::Error(_) = &r_val {
1133 return Ok(r_val);
1134 }
1135 let ord = Self::compare_excel_values(&l_val, &r_val);
1136 let b = match op {
1137 Op::Eq => ord.is_eq(),
1138 Op::Ne => !ord.is_eq(),
1139 Op::Lt => ord.is_lt(),
1140 Op::Gt => ord.is_gt(),
1141 Op::Le => ord.is_le(),
1142 Op::Ge => ord.is_ge(),
1143 _ => unreachable!(),
1144 };
1145 Ok(ResultData::Boolean(b))
1146 }
1147 _ => {
1148 if let ResultData::Error(_) = &l_val {
1149 return Ok(l_val);
1150 }
1151 let lf = match self.to_f64(&l_val) {
1152 Some(f) => f,
1153 None => return Ok(ResultData::Error("#VALUE!".to_string())),
1154 };
1155 let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1156 if let ResultData::Error(_) = &r_val {
1157 return Ok(r_val);
1158 }
1159 let rf = match self.to_f64(&r_val) {
1160 Some(f) => f,
1161 None => return Ok(ResultData::Error("#VALUE!".to_string())),
1162 };
1163 match op {
1164 Op::Add => Ok(ResultData::Float(lf + rf)),
1165 Op::Sub => Ok(ResultData::Float(lf - rf)),
1166 Op::Mul => Ok(ResultData::Float(lf * rf)),
1167 Op::Div => {
1168 if rf == 0.0 {
1169 return Ok(ResultData::Error("#DIV/0!".to_string()));
1170 }
1171 Ok(ResultData::Float(lf / rf))
1172 }
1173 Op::Exp => {
1174 if lf == 0.0 && rf == 0.0 {
1175 return Ok(ResultData::Error("#NUM!".to_string()));
1176 }
1177 if lf == 0.0 && rf < 0.0 {
1178 return Ok(ResultData::Error("#DIV/0!".to_string()));
1179 }
1180 if lf < 0.0 {
1181 if rf.fract() != 0.0 || rf.abs() > 1e6 {
1182 return Ok(ResultData::Error("#NUM!".to_string()));
1183 }
1184 let res = lf.powi(rf as i32);
1185 if res.is_nan() || res.is_infinite() {
1186 return Ok(ResultData::Error("#NUM!".to_string()));
1187 }
1188 return Ok(ResultData::Float(res));
1189 }
1190 let res = lf.powf(rf);
1191 if res.is_nan() || res.is_infinite() {
1192 return Ok(ResultData::Error("#NUM!".to_string()));
1193 }
1194 Ok(ResultData::Float(res))
1195 }
1196 _ => unreachable!(),
1197 }
1198 }
1199 }
1200 }
1201 Expr::FunctionCall { name, args } => {
1202 self.evaluate_function(name, args, context, row, col, deps, scope)
1203 }
1204 }
1205 }
1206
1207 fn excel_type_rank(val: &ResultData) -> u8 {
1208 match val {
1209 ResultData::None => 0,
1210 ResultData::Integer(_) | ResultData::Float(_) => 1,
1211 ResultData::String(_) => 2,
1212 ResultData::Boolean(_) => 3,
1213 _ => 4,
1214 }
1215 }
1216
1217 fn compare_excel_values(l: &ResultData, r: &ResultData) -> std::cmp::Ordering {
1218 match (l, r) {
1220 (ResultData::None, ResultData::None) => return std::cmp::Ordering::Equal,
1221 (ResultData::None, ResultData::Integer(b)) => {
1222 return 0.0
1223 .partial_cmp(&(*b as f64))
1224 .unwrap_or(std::cmp::Ordering::Equal);
1225 }
1226 (ResultData::None, ResultData::Float(b)) => {
1227 return 0.0.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal);
1228 }
1229 (ResultData::Integer(a), ResultData::None) => {
1230 return (*a as f64)
1231 .partial_cmp(&0.0)
1232 .unwrap_or(std::cmp::Ordering::Equal);
1233 }
1234 (ResultData::Float(a), ResultData::None) => {
1235 return a.partial_cmp(&0.0).unwrap_or(std::cmp::Ordering::Equal);
1236 }
1237 (ResultData::None, ResultData::String(b)) => {
1238 return "".cmp(b.to_lowercase().as_str());
1239 }
1240 (ResultData::String(a), ResultData::None) => {
1241 return a.to_lowercase().as_str().cmp("");
1242 }
1243 (ResultData::None, ResultData::Boolean(b)) => {
1244 return false.cmp(b);
1245 }
1246 (ResultData::Boolean(a), ResultData::None) => {
1247 return a.cmp(&false);
1248 }
1249 _ => {}
1250 }
1251
1252 let rank_l = Self::excel_type_rank(l);
1253 let rank_r = Self::excel_type_rank(r);
1254 if rank_l != rank_r {
1255 return rank_l.cmp(&rank_r);
1256 }
1257 match (l, r) {
1258 (ResultData::Integer(a), ResultData::Integer(b)) => a.cmp(b),
1259 (ResultData::Float(a), ResultData::Float(b)) => {
1260 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
1261 }
1262 (ResultData::Integer(a), ResultData::Float(b)) => (*a as f64)
1263 .partial_cmp(b)
1264 .unwrap_or(std::cmp::Ordering::Equal),
1265 (ResultData::Float(a), ResultData::Integer(b)) => a
1266 .partial_cmp(&(*b as f64))
1267 .unwrap_or(std::cmp::Ordering::Equal),
1268 (ResultData::Boolean(a), ResultData::Boolean(b)) => a.cmp(b),
1269 (ResultData::String(a), ResultData::String(b)) => Self::compare_excel_strings(a, b),
1270 _ => std::cmp::Ordering::Equal,
1271 }
1272 }
1273
1274 fn sort_compare_blanks_last(
1284 l: &ResultData,
1285 r: &ResultData,
1286 sort_order: f64,
1287 ) -> std::cmp::Ordering {
1288 match (matches!(l, ResultData::None), matches!(r, ResultData::None)) {
1289 (true, true) => std::cmp::Ordering::Equal,
1290 (true, false) => std::cmp::Ordering::Greater,
1291 (false, true) => std::cmp::Ordering::Less,
1292 (false, false) => {
1293 let ord = Self::compare_excel_values(l, r);
1294 if sort_order < 0.0 { ord.reverse() } else { ord }
1295 }
1296 }
1297 }
1298
1299 fn is_excel_number_str(s: &str) -> bool {
1300 let s = s.trim();
1301 if s.is_empty() {
1302 return false;
1303 }
1304 let bytes = s.as_bytes();
1305 let first = bytes[0];
1306 if first == b'e' || first == b'E' {
1307 return false;
1308 }
1309 if (first == b'+' || first == b'-') && bytes.len() > 1 {
1310 let second = bytes[1];
1311 if second == b'e' || second == b'E' {
1312 return false;
1313 }
1314 }
1315 true
1316 }
1317
1318 fn compare_excel_strings(a: &str, b: &str) -> std::cmp::Ordering {
1319 let char_weight = |ch: char| -> u32 {
1320 match ch {
1321 ' ' => 0,
1322 '_' => 1,
1323 '-' => 2,
1324 ',' => 3,
1325 ';' => 4,
1326 ':' => 5,
1327 '!' => 6,
1328 '?' => 7,
1329 '.' => 8,
1330 '\'' => 9,
1331 '"' => 10,
1332 '(' => 11,
1333 ')' => 12,
1334 '[' => 13,
1335 ']' => 14,
1336 '{' => 15,
1337 '}' => 16,
1338 '@' => 17,
1339 '*' => 18,
1340 '/' => 19,
1341 '\\' => 20,
1342 '&' => 21,
1343 '#' => 22,
1344 '%' => 23,
1345 '`' => 24,
1346 '^' => 25,
1347 '+' => 26,
1348 '<' => 27,
1349 '=' => 28,
1350 '>' => 29,
1351 '|' => 30,
1352 '~' => 31,
1353 '$' => 32,
1354 '0'..='9' => 33 + (ch as u32 - '0' as u32),
1355 'A'..='Z' => 43 + (ch as u32 - 'A' as u32),
1356 'a'..='z' => 43 + (ch as u32 - 'a' as u32),
1357 _ => ch
1358 .to_lowercase()
1359 .next()
1360 .map(|c| c as u32 + 200)
1361 .unwrap_or(ch as u32 + 200),
1362 }
1363 };
1364
1365 for (ca, cb) in a.chars().zip(b.chars()) {
1366 let wa = char_weight(ca);
1367 let wb = char_weight(cb);
1368 if wa != wb {
1369 return wa.cmp(&wb);
1370 }
1371 }
1372 a.len().cmp(&b.len())
1373 }
1374
1375 pub(crate) fn clean_float(val: f64) -> f64 {
1382 if val == 0.0 || !val.is_finite() {
1383 return val;
1384 }
1385 let abs_val = val.abs();
1386 let exp = abs_val.log10().floor() as i32;
1387 let factor = 10.0f64.powi(15 - 1 - exp);
1388 if factor.is_finite() && factor != 0.0 {
1389 let rounded = (val * factor).round() / factor;
1390 if (val - rounded).abs() <= 1e-14 * abs_val {
1391 return rounded;
1392 }
1393 }
1394 val
1395 }
1396
1397 pub(crate) fn to_f64(&self, val: &ResultData) -> Option<f64> {
1407 match val {
1408 ResultData::None => Some(0.0),
1409 ResultData::Float(f) => Some(*f),
1410 ResultData::Integer(i) => Some(*i as f64),
1411 ResultData::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
1412 ResultData::String(s) => {
1413 let s_trim = s.trim();
1414 if Self::is_excel_number_str(s_trim) {
1415 if let Ok(f) = s_trim.parse::<f64>() {
1416 return Some(f);
1417 }
1418 if let Some((date, _)) =
1419 crate::core::date::parse_date_with_locale(s_trim, &self.locale)
1420 {
1421 return Some(crate::core::date::date_to_excel_serial(date));
1422 }
1423 None
1424 } else if let Some((date, _)) =
1425 crate::core::date::parse_date_with_locale(s_trim, &self.locale)
1426 {
1427 Some(crate::core::date::date_to_excel_serial(date))
1428 } else {
1429 None
1430 }
1431 }
1432 _ => None,
1433 }
1434 }
1435
1436 fn to_f64_arg(&self, arg_opt: Option<&ResultData>, fn_name: &str) -> Result<f64, EngineError> {
1437 let val = arg_opt.ok_or_else(|| {
1438 EngineError::EvalError(EvalError::UnknownFunction(format!(
1439 "{} requires argument",
1440 fn_name
1441 )))
1442 })?;
1443 if let ResultData::Error(e) = val {
1444 return Err(EngineError::EvalError(EvalError::UnknownFunction(
1445 e.clone(),
1446 )));
1447 }
1448 self.to_f64(val).ok_or_else(|| {
1449 EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
1450 })
1451 }
1452
1453 fn find_error_in_args(args: &[ResultData]) -> Option<ResultData> {
1454 for arg in args {
1455 match arg {
1456 ResultData::Error(_) => return Some(arg.clone()),
1457 ResultData::List(list) => {
1458 if let Some(err) = Self::find_error_in_args(list) {
1459 return Some(err);
1460 }
1461 }
1462 _ => {}
1463 }
1464 }
1465 None
1466 }
1467
1468 fn check_arg_errors(&self, args: &[ResultData], is_direct: &[bool]) -> Option<ResultData> {
1469 for (i, arg) in args.iter().enumerate() {
1470 match arg {
1471 ResultData::Error(_) => return Some(arg.clone()),
1472 ResultData::List(list) => {
1473 if let Some(err) = self.check_arg_errors(list, &[]) {
1474 return Some(err);
1475 }
1476 }
1477 ResultData::String(_)
1478 if is_direct.get(i).copied().unwrap_or(false) && self.to_f64(arg).is_none() =>
1479 {
1480 return Some(ResultData::Error("#VALUE!".to_string()));
1481 }
1482 _ => {}
1483 }
1484 }
1485 None
1486 }
1487
1488 fn sum_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
1489 match arg {
1490 ResultData::Float(f) => *f,
1491 ResultData::Integer(i) => *i as f64,
1492 ResultData::Boolean(b) => {
1493 if is_direct {
1494 if *b { 1.0 } else { 0.0 }
1495 } else {
1496 0.0
1497 }
1498 }
1499 ResultData::String(_) => {
1500 if is_direct {
1501 self.to_f64(arg).unwrap_or(0.0)
1502 } else {
1503 0.0
1504 }
1505 }
1506 ResultData::List(list) => {
1507 let mut sum = 0.0;
1508 for item in list {
1509 sum += self.sum_helper(item, false);
1510 }
1511 sum
1512 }
1513 _ => 0.0,
1514 }
1515 }
1516
1517 fn flatten_finance_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1523 match arg {
1524 ResultData::Float(f) => vec![*f],
1525 ResultData::Integer(i) => vec![*i as f64],
1526 ResultData::Boolean(b) => {
1527 if is_direct {
1528 vec![if *b { 1.0 } else { 0.0 }]
1529 } else {
1530 vec![]
1531 }
1532 }
1533 ResultData::String(_) => {
1534 if is_direct {
1535 self.to_f64(arg).into_iter().collect()
1536 } else {
1537 vec![]
1538 }
1539 }
1540 ResultData::List(list) => list
1541 .iter()
1542 .flat_map(|v| self.flatten_finance_numbers(v, false))
1543 .collect(),
1544 _ => vec![],
1545 }
1546 }
1547
1548 fn flatten_stat_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1549 match arg {
1550 ResultData::Float(f) => vec![*f],
1551 ResultData::Integer(i) => vec![*i as f64],
1552 ResultData::Boolean(b) => {
1553 if is_direct {
1554 vec![if *b { 1.0 } else { 0.0 }]
1555 } else {
1556 vec![]
1557 }
1558 }
1559 ResultData::String(_) => {
1560 if is_direct {
1561 self.to_f64(arg).into_iter().collect()
1562 } else {
1563 vec![]
1564 }
1565 }
1566 ResultData::List(list) => list
1567 .iter()
1568 .flat_map(|v| self.flatten_stat_numbers(v, false))
1569 .collect(),
1570 _ => vec![],
1571 }
1572 }
1573
1574 fn flatten_positional(
1581 &self,
1582 arg: &ResultData,
1583 out: &mut Vec<Option<f64>>,
1584 first_err: &mut Option<String>,
1585 ) {
1586 match arg {
1587 ResultData::List(items) => {
1588 for item in items {
1589 self.flatten_positional(item, out, first_err);
1590 }
1591 }
1592 ResultData::Float(f) => out.push(Some(*f)),
1593 ResultData::Integer(i) => out.push(Some(*i as f64)),
1594 ResultData::Error(e) => {
1595 if first_err.is_none() {
1596 *first_err = Some(e.clone());
1597 }
1598 out.push(None);
1599 }
1600 _ => out.push(None),
1601 }
1602 }
1603
1604 fn positional_numbers(
1605 &self,
1606 arg: Option<&ResultData>,
1607 first_err: &mut Option<String>,
1608 ) -> Vec<Option<f64>> {
1609 let mut out = Vec::new();
1610 if let Some(a) = arg {
1611 self.flatten_positional(a, &mut out, first_err);
1612 }
1613 out
1614 }
1615
1616 fn pair_and_filter(
1639 xs_raw: Vec<Option<f64>>,
1640 ys_raw: Vec<Option<f64>>,
1641 ) -> Result<(Vec<f64>, Vec<f64>), String> {
1642 if xs_raw.len() != ys_raw.len() {
1643 return Err("#N/A".to_string());
1644 }
1645 let mut xs = Vec::with_capacity(xs_raw.len());
1646 let mut ys = Vec::with_capacity(ys_raw.len());
1647 for (x, y) in xs_raw.into_iter().zip(ys_raw) {
1648 if let (Some(x), Some(y)) = (x, y) {
1649 xs.push(x);
1650 ys.push(y);
1651 }
1652 }
1653 Ok((xs, ys))
1654 }
1655
1656 fn paired_args(
1658 &self,
1659 x_arg: Option<&ResultData>,
1660 y_arg: Option<&ResultData>,
1661 ) -> Result<(Vec<f64>, Vec<f64>), String> {
1662 for arg in [x_arg, y_arg].into_iter().flatten() {
1683 let scalar = match arg {
1689 ResultData::List(items) if items.len() == 1 => &items[0],
1690 other => other,
1691 };
1692 if let ResultData::Error(e) = scalar {
1693 return Err(e.clone());
1694 }
1695 if Self::is_empty_scalar_operand(arg) {
1703 return Err("#VALUE!".to_string());
1704 }
1705 }
1706 let mut first_err = None;
1707 let xs_raw = self.positional_numbers(x_arg, &mut first_err);
1708 let ys_raw = self.positional_numbers(y_arg, &mut first_err);
1709 if xs_raw.len() != ys_raw.len() {
1710 return Err("#N/A".to_string());
1711 }
1712 if let Some(e) = first_err {
1713 return Err(e);
1714 }
1715 Self::pair_and_filter(xs_raw, ys_raw)
1716 }
1717
1718 fn flatten_strict_inner(
1749 &self,
1750 arg: &ResultData,
1751 blanks: BlankPolicy,
1752 coerce_text: bool,
1753 out: &mut Vec<f64>,
1754 ) -> Result<(), String> {
1755 match arg {
1756 ResultData::List(items) => {
1757 for item in items {
1758 self.flatten_strict_inner(item, blanks, coerce_text, out)?;
1759 }
1760 Ok(())
1761 }
1762 ResultData::Error(e) => Err(e.clone()),
1763 ResultData::Float(f) => {
1764 out.push(*f);
1765 Ok(())
1766 }
1767 ResultData::Integer(i) => {
1768 out.push(*i as f64);
1769 Ok(())
1770 }
1771 ResultData::None => match blanks {
1772 BlankPolicy::Zero => {
1773 out.push(0.0);
1774 Ok(())
1775 }
1776 BlankPolicy::Skip => Ok(()),
1777 BlankPolicy::Reject => Err("#VALUE!".to_string()),
1778 },
1779 ResultData::String(_) if coerce_text => match self.to_f64(arg) {
1786 Some(f) => {
1787 out.push(f);
1788 Ok(())
1789 }
1790 None => Err("#VALUE!".to_string()),
1791 },
1792 _ => Err("#VALUE!".to_string()),
1793 }
1794 }
1795
1796 fn flatten_strict_numbers(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1797 let mut out = Vec::new();
1798 self.flatten_strict_inner(arg, BlankPolicy::Zero, true, &mut out)?;
1799 Ok(out)
1800 }
1801
1802 fn flatten_skipping_blanks(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1805 let mut out = Vec::new();
1806 if let Some(a) = arg {
1807 self.flatten_strict_inner(a, BlankPolicy::Skip, true, &mut out)?;
1808 }
1809 Ok(out)
1810 }
1811
1812 fn flatten_skipping_blanks_no_text_coercion(
1819 &self,
1820 arg: Option<&ResultData>,
1821 ) -> Result<Vec<f64>, String> {
1822 let mut out = Vec::new();
1823 if let Some(a) = arg {
1824 self.flatten_strict_inner(a, BlankPolicy::Skip, false, &mut out)?;
1825 }
1826 Ok(out)
1827 }
1828
1829 fn flatten_numbers_only(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1832 let mut out = Vec::new();
1833 self.flatten_strict_inner(arg, BlankPolicy::Reject, false, &mut out)?;
1834 Ok(out)
1835 }
1836
1837 fn aggregate_range_number(val: &ResultData) -> Option<f64> {
1846 match val {
1847 ResultData::Float(f) => Some(*f),
1848 ResultData::Integer(i) => Some(*i as f64),
1849 _ => None,
1850 }
1851 }
1852
1853 fn flatten_numbers_only_arg(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1854 match arg {
1855 Some(a) => self.flatten_numbers_only(a),
1856 None => Ok(vec![]),
1857 }
1858 }
1859
1860 fn flatten_args_stat_numbers(
1874 &self,
1875 args: &[ResultData],
1876 is_direct: &[bool],
1877 ) -> Result<Vec<f64>, String> {
1878 let mut out = Vec::new();
1879 for (i, arg) in args.iter().enumerate() {
1880 let direct = is_direct.get(i).copied().unwrap_or(false);
1881 if direct && matches!(arg, ResultData::String(_)) && self.to_f64(arg).is_none() {
1882 return Err("#VALUE!".to_string());
1883 }
1884 out.extend(self.flatten_stat_numbers(arg, direct));
1885 }
1886 Ok(out)
1887 }
1888
1889 fn flatten_stat_numbers_a(
1906 &self,
1907 arg: &ResultData,
1908 is_direct: bool,
1909 ) -> Result<Vec<f64>, String> {
1910 Ok(match arg {
1911 ResultData::Float(f) => vec![*f],
1912 ResultData::Integer(i) => vec![*i as f64],
1913 ResultData::Boolean(b) => vec![if *b { 1.0 } else { 0.0 }],
1914 ResultData::String(_) => {
1915 if is_direct {
1916 match self.to_f64(arg) {
1917 Some(f) => vec![f],
1918 None => return Err("#VALUE!".to_string()),
1919 }
1920 } else {
1921 vec![0.0]
1922 }
1923 }
1924 ResultData::Error(e) => return Err(e.clone()),
1925 ResultData::List(list) => {
1927 let mut out = Vec::new();
1928 for v in list {
1929 out.extend(self.flatten_stat_numbers_a(v, false)?);
1930 }
1931 out
1932 }
1933 ResultData::None => vec![],
1934 _ => vec![0.0],
1935 })
1936 }
1937
1938 fn flatten_args_stat_numbers_a(
1941 &self,
1942 args: &[ResultData],
1943 is_direct: &[bool],
1944 ) -> Result<Vec<f64>, String> {
1945 let mut out = Vec::new();
1946 for (i, arg) in args.iter().enumerate() {
1947 out.extend(
1948 self.flatten_stat_numbers_a(arg, is_direct.get(i).copied().unwrap_or(false))?,
1949 );
1950 }
1951 Ok(out)
1952 }
1953
1954 fn extract_matrix(&self, arg: &ResultData) -> Vec<Vec<f64>> {
1955 match arg {
1956 ResultData::List(list) => {
1957 let mut rows = Vec::new();
1958 for item in list {
1959 match item {
1960 ResultData::List(sub_list) => {
1961 let row: Vec<f64> =
1962 sub_list.iter().flat_map(|v| self.to_f64(v)).collect();
1963 if !row.is_empty() {
1964 rows.push(row);
1965 }
1966 }
1967 _ => {
1968 if let Some(f) = self.to_f64(item) {
1969 rows.push(vec![f]);
1970 }
1971 }
1972 }
1973 }
1974 rows
1975 }
1976 _ => vec![],
1977 }
1978 }
1979
1980 fn matrix_from_arg(
1991 &self,
1992 expr: &crate::core::parser::Expr,
1993 value: &ResultData,
1994 ) -> Vec<Vec<f64>> {
1995 if let ResultData::List(items) = value
1997 && items.iter().any(|i| matches!(i, ResultData::List(_)))
1998 {
1999 return self.extract_matrix(value);
2000 }
2001 fn plain(v: &ResultData) -> Option<f64> {
2006 match v {
2007 ResultData::Float(f) => Some(*f),
2008 ResultData::Integer(i) => Some(*i as f64),
2009 _ => None,
2010 }
2011 }
2012 let items: Vec<&ResultData> = match value {
2013 ResultData::List(items) => items.iter().collect(),
2014 other => vec![other],
2015 };
2016 if items.iter().any(|v| plain(v).is_none()) {
2017 return Vec::new();
2018 }
2019 let flat: Vec<f64> = items.iter().filter_map(|v| plain(v)).collect();
2020 let cols = match Self::range_bounds(expr) {
2021 Some((_, _, start_col, _, end_col)) => end_col.saturating_sub(start_col) + 1,
2022 None => flat.len().max(1),
2023 };
2024 if cols == 0 || !flat.len().is_multiple_of(cols) {
2025 return self.extract_matrix(value);
2026 }
2027 flat.chunks(cols).map(|c| c.to_vec()).collect()
2028 }
2029
2030 fn paired_sum_has_no_numbers(&self, arg: Option<&ResultData>) -> bool {
2048 let mut ignored = None;
2049 let slots = self.positional_numbers(arg, &mut ignored);
2050 slots.iter().all(|v| v.is_none())
2051 }
2052
2053 fn is_empty_scalar_operand(arg: &ResultData) -> bool {
2067 let scalar = match arg {
2068 ResultData::List(items) if items.len() == 1 => &items[0],
2069 other => other,
2070 };
2071 matches!(scalar, ResultData::None)
2072 }
2073
2074 fn first_arg_is_boolean(args: &[ResultData]) -> bool {
2084 matches!(args.first(), Some(ResultData::Boolean(_)))
2085 }
2086
2087 fn opt_f64_arg(&self, args: &[ResultData], i: usize, default: f64) -> Result<f64, EngineError> {
2088 match args.get(i) {
2089 None => Ok(default),
2090 Some(ResultData::None) => Ok(0.0),
2095 Some(ResultData::Error(e)) => Err(EngineError::EvalError(EvalError::UnknownFunction(
2096 e.clone(),
2097 ))),
2098 Some(v) => self.to_f64(v).ok_or_else(|| {
2099 EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
2100 }),
2101 }
2102 }
2103
2104 fn opt_f64(&self, args: &[ResultData], i: usize, default: f64) -> f64 {
2105 args.get(i).and_then(|v| self.to_f64(v)).unwrap_or(default)
2106 }
2107
2108 fn average_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, usize) {
2109 match arg {
2110 ResultData::Float(f) => (*f, 1),
2111 ResultData::Integer(i) => (*i as f64, 1),
2112 ResultData::Boolean(b) => {
2113 if is_direct {
2114 (if *b { 1.0 } else { 0.0 }, 1)
2115 } else {
2116 (0.0, 0)
2117 }
2118 }
2119 ResultData::String(_) => {
2120 if is_direct {
2121 if let Some(f) = self.to_f64(arg) {
2122 (f, 1)
2123 } else {
2124 (0.0, 0)
2125 }
2126 } else {
2127 (0.0, 0)
2128 }
2129 }
2130 ResultData::List(list) => {
2131 let mut sum = 0.0;
2132 let mut count = 0;
2133 for item in list {
2134 let (s, c) = self.average_helper(item, false);
2135 sum += s;
2136 count += c;
2137 }
2138 (sum, count)
2139 }
2140 _ => (0.0, 0),
2141 }
2142 }
2143
2144 fn count_helper(&self, arg: &ResultData) -> usize {
2145 match arg {
2146 ResultData::Float(_) | ResultData::Integer(_) => 1,
2147 ResultData::List(list) => {
2148 let mut count = 0;
2149 for item in list {
2150 count += self.count_helper(item);
2151 }
2152 count
2153 }
2154 _ => 0,
2155 }
2156 }
2157
2158 fn min_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
2159 match arg {
2160 ResultData::Float(f) => *f,
2161 ResultData::Integer(i) => *i as f64,
2162 ResultData::Boolean(b) => {
2163 if is_direct {
2164 if *b { 1.0 } else { 0.0 }
2165 } else {
2166 f64::INFINITY
2167 }
2168 }
2169 ResultData::String(_) => {
2170 if is_direct {
2171 self.to_f64(arg).unwrap_or(f64::INFINITY)
2172 } else {
2173 f64::INFINITY
2174 }
2175 }
2176 ResultData::List(list) => {
2177 let mut min_val = f64::INFINITY;
2178 for item in list {
2179 min_val = min_val.min(self.min_helper(item, false));
2180 }
2181 min_val
2182 }
2183 _ => f64::INFINITY,
2184 }
2185 }
2186
2187 fn max_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
2188 match arg {
2189 ResultData::Float(f) => *f,
2190 ResultData::Integer(i) => *i as f64,
2191 ResultData::Boolean(b) => {
2192 if is_direct {
2193 if *b { 1.0 } else { 0.0 }
2194 } else {
2195 f64::NEG_INFINITY
2196 }
2197 }
2198 ResultData::String(_) => {
2199 if is_direct {
2200 self.to_f64(arg).unwrap_or(f64::NEG_INFINITY)
2201 } else {
2202 f64::NEG_INFINITY
2203 }
2204 }
2205 ResultData::List(list) => {
2206 let mut max_val = f64::NEG_INFINITY;
2207 for item in list {
2208 max_val = max_val.max(self.max_helper(item, false));
2209 }
2210 max_val
2211 }
2212 _ => f64::NEG_INFINITY,
2213 }
2214 }
2215
2216 fn concat_helper(&self, arg: &ResultData, out: &mut String) {
2217 match arg {
2218 ResultData::List(list) => {
2219 for item in list {
2220 self.concat_helper(item, out);
2221 }
2222 }
2223 other => {
2224 out.push_str(&other.to_string());
2225 }
2226 }
2227 }
2228
2229 fn counta_helper(&self, arg: &ResultData) -> usize {
2230 match arg {
2231 ResultData::None => 0,
2232 ResultData::List(list) => {
2236 let mut count = 0;
2237 for item in list {
2238 count += self.counta_helper(item);
2239 }
2240 count
2241 }
2242 _ => 1,
2243 }
2244 }
2245
2246 fn product_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, bool) {
2247 match arg {
2248 ResultData::Float(f) => (*f, true),
2249 ResultData::Integer(i) => (*i as f64, true),
2250 ResultData::Boolean(b) => {
2251 if is_direct {
2252 (if *b { 1.0 } else { 0.0 }, true)
2253 } else {
2254 (1.0, false)
2255 }
2256 }
2257 ResultData::String(_) => {
2258 if is_direct {
2259 if let Some(f) = self.to_f64(arg) {
2260 (f, true)
2261 } else {
2262 (1.0, false)
2263 }
2264 } else {
2265 (1.0, false)
2266 }
2267 }
2268 ResultData::List(list) => {
2269 let mut prod = 1.0;
2270 let mut has_nums = false;
2271 for item in list {
2272 let (p, h) = self.product_helper(item, false);
2273 if h {
2274 prod *= p;
2277 has_nums = true;
2278 }
2279 }
2280 (prod, has_nums)
2281 }
2282 _ => (1.0, false),
2283 }
2284 }
2285
2286 fn to_bool_opt(&self, val: &ResultData) -> Option<bool> {
2287 match val {
2288 ResultData::Boolean(b) => Some(*b),
2289 ResultData::Integer(i) => Some(*i != 0),
2290 ResultData::Float(f) => Some(*f != 0.0),
2291 ResultData::String(s) => {
2292 let s_trim = s.trim();
2293 if s_trim.eq_ignore_ascii_case("true") {
2294 Some(true)
2295 } else if s_trim.eq_ignore_ascii_case("false") {
2296 Some(false)
2297 } else if let Ok(f) = s_trim.parse::<f64>() {
2298 Some(f != 0.0)
2299 } else {
2300 None
2301 }
2302 }
2303 ResultData::None => Some(false),
2304 _ => None,
2305 }
2306 }
2307
2308 fn to_bool(&self, val: &ResultData) -> bool {
2309 self.to_bool_opt(val).unwrap_or(false)
2310 }
2311
2312 fn range_numeric(val: &ResultData) -> Option<f64> {
2322 match val {
2323 ResultData::Integer(i) => Some(*i as f64),
2324 ResultData::Float(f) => Some(*f),
2325 _ => None,
2326 }
2327 }
2328
2329 fn exact_lookup_matches(lookup: &ResultData, candidate: &ResultData) -> bool {
2339 if matches!(candidate, ResultData::None) {
2340 return false;
2341 }
2342 let lookup_key = match lookup {
2343 ResultData::None => "0".to_string(),
2344 other => other.to_string(),
2345 };
2346 candidate.to_string() == lookup_key
2347 }
2348
2349 fn wildcard_criteria_matches(pattern: &str, text: &str) -> bool {
2350 fn rec(pat: &[char], txt: &[char]) -> bool {
2351 if pat.is_empty() {
2352 return txt.is_empty();
2353 }
2354 match pat[0] {
2355 '*' => rec(&pat[1..], txt) || (!txt.is_empty() && rec(pat, &txt[1..])),
2356 '?' => !txt.is_empty() && rec(&pat[1..], &txt[1..]),
2357 '~' if pat.len() > 1 && matches!(pat[1], '*' | '?' | '~') => {
2358 !txt.is_empty() && pat[1] == txt[0] && rec(&pat[2..], &txt[1..])
2359 }
2360 ch => !txt.is_empty() && ch == txt[0] && rec(&pat[1..], &txt[1..]),
2361 }
2362 }
2363
2364 let pat = pattern.to_lowercase().chars().collect::<Vec<_>>();
2365 let txt = text.to_lowercase().chars().collect::<Vec<_>>();
2366 rec(&pat, &txt)
2367 }
2368
2369 fn criteria_text_eq(val: &ResultData, pattern: &str) -> bool {
2370 let text = val.to_string();
2371 if pattern.contains('*') || pattern.contains('?') {
2372 matches!(val, ResultData::String(_)) && Self::wildcard_criteria_matches(pattern, &text)
2375 } else {
2376 text.to_lowercase() == pattern.to_lowercase()
2377 }
2378 }
2379
2380 fn match_criteria(&self, val: &ResultData, criteria: &ResultData) -> bool {
2381 let crit_str = criteria.to_string();
2382 if let Some(rest) = crit_str.strip_prefix(">=") {
2383 let val_f = match Self::range_numeric(val) {
2389 Some(f) => f,
2390 None => return false,
2391 };
2392 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2393 val_f >= crit_f
2394 } else if let Some(rest) = crit_str.strip_prefix('>') {
2395 let val_f = match Self::range_numeric(val) {
2396 Some(f) => f,
2397 None => return false,
2398 };
2399 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2400 val_f > crit_f
2401 } else if let Some(rest) = crit_str.strip_prefix("<>") {
2402 let remainder = rest.trim();
2403 !Self::criteria_text_eq(val, remainder)
2404 } else if let Some(rest) = crit_str.strip_prefix("<=") {
2405 let val_f = match Self::range_numeric(val) {
2406 Some(f) => f,
2407 None => return false,
2408 };
2409 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2410 val_f <= crit_f
2411 } else if let Some(rest) = crit_str.strip_prefix('<') {
2412 let val_f = match Self::range_numeric(val) {
2413 Some(f) => f,
2414 None => return false,
2415 };
2416 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2417 val_f < crit_f
2418 } else if let Some(rest) = crit_str.strip_prefix('=') {
2419 let remainder = rest.trim();
2420 Self::criteria_text_eq(val, remainder)
2421 } else {
2422 Self::criteria_text_eq(val, &crit_str)
2423 }
2424 }
2425
2426 fn range_bounds(
2432 expr: &crate::core::parser::Expr,
2433 ) -> Option<(Option<String>, usize, usize, usize, usize)> {
2434 use crate::core::parser::Expr;
2435 match expr {
2436 Expr::RangeRef {
2437 sheet,
2438 start_row,
2439 start_col,
2440 end_row,
2441 end_col,
2442 ..
2443 } => Some((sheet.clone(), *start_row, *start_col, *end_row, *end_col)),
2444 Expr::CellRef {
2445 sheet, row, col, ..
2446 } => Some((sheet.clone(), *row, *col, *row, *col)),
2447 _ => None,
2448 }
2449 }
2450
2451 fn materialize_range(
2458 &self,
2459 sheet_opt: &Option<String>,
2460 start_row: usize,
2461 start_col: usize,
2462 end_row: usize,
2463 end_col: usize,
2464 context: Option<&Context>,
2465 ) -> Option<Vec<Vec<ResultData>>> {
2466 let is_self = match sheet_opt {
2467 Some(name) => name == &self.name,
2468 None => true,
2469 };
2470 let source: &Sheet = if is_self {
2471 self
2472 } else {
2473 context?.sheets.get(sheet_opt.as_ref()?)?
2474 };
2475 let actual_end_row = if end_row == usize::MAX {
2476 source.row_count().saturating_sub(1)
2477 } else {
2478 end_row
2479 };
2480 let actual_end_col = if end_col == usize::MAX {
2481 source.col_count().saturating_sub(1)
2482 } else {
2483 end_col
2484 };
2485 if actual_end_row < start_row || actual_end_col < start_col {
2486 return Some(Vec::new());
2487 }
2488 let mut grid = Vec::with_capacity(actual_end_row - start_row + 1);
2489 for r in start_row..=actual_end_row {
2490 let mut row = Vec::with_capacity(actual_end_col - start_col + 1);
2491 for c in start_col..=actual_end_col {
2492 row.push(source.get_result_data(&CellRef::new(r, c)));
2493 }
2494 grid.push(row);
2495 }
2496 Some(grid)
2497 }
2498
2499 fn evaluate_database_function(
2513 &self,
2514 func_name: &str,
2515 args: &[crate::core::parser::Expr],
2516 evaluated_args: &[ResultData],
2517 context: Option<&Context>,
2518 ) -> Result<ResultData, EngineError> {
2519 if args.len() < 3 || evaluated_args.len() < 3 {
2520 return Ok(ResultData::Error("#VALUE!".to_string()));
2521 }
2522 let (db_sheet, db_sr, db_sc, db_er, db_ec) = match Self::range_bounds(&args[0]) {
2523 Some(v) => v,
2524 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2525 };
2526 let (crit_sheet, crit_sr, crit_sc, crit_er, crit_ec) = match Self::range_bounds(&args[2]) {
2527 Some(v) => v,
2528 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2529 };
2530 let db = match self.materialize_range(&db_sheet, db_sr, db_sc, db_er, db_ec, context) {
2531 Some(g) => g,
2532 None => return Ok(ResultData::Error("#REF!".to_string())),
2533 };
2534 let crit = match self.materialize_range(
2535 &crit_sheet,
2536 crit_sr,
2537 crit_sc,
2538 crit_er,
2539 crit_ec,
2540 context,
2541 ) {
2542 Some(g) => g,
2543 None => return Ok(ResultData::Error("#REF!".to_string())),
2544 };
2545 if db.len() < 2 || crit.len() < 2 {
2546 return Ok(ResultData::Error("#VALUE!".to_string()));
2547 }
2548
2549 let db_headers: Vec<String> = db[0].iter().map(|v| v.to_string()).collect();
2550 let field_idx: usize = match &evaluated_args[1] {
2551 ResultData::String(s) => {
2552 match db_headers.iter().position(|h| h.eq_ignore_ascii_case(s)) {
2553 Some(idx) => idx,
2554 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2555 }
2556 }
2557 other => match self.to_f64(other) {
2558 Some(n) if n >= 1.0 && (n as usize) <= db_headers.len() => n as usize - 1,
2559 _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2560 },
2561 };
2562
2563 let crit_headers: Vec<String> = crit[0].iter().map(|v| v.to_string()).collect();
2564 let crit_to_db: Vec<Option<usize>> = crit_headers
2565 .iter()
2566 .map(|h| db_headers.iter().position(|dh| dh.eq_ignore_ascii_case(h)))
2567 .collect();
2568
2569 let mut matched: Vec<ResultData> = Vec::new();
2570 for row in db.iter().skip(1) {
2571 let row_matches_any_criteria_row = crit.iter().skip(1).any(|crit_row| {
2572 crit_row.iter().enumerate().all(|(ci, cell)| {
2573 if matches!(cell, ResultData::None) {
2574 return true;
2575 }
2576 match crit_to_db.get(ci).copied().flatten() {
2577 Some(db_col) => self.match_criteria(&row[db_col], cell),
2578 None => false,
2579 }
2580 })
2581 });
2582 if row_matches_any_criteria_row {
2583 matched.push(row[field_idx].clone());
2584 }
2585 }
2586
2587 match func_name {
2588 "DGET" => match matched.len() {
2589 0 => Ok(ResultData::Error("#VALUE!".to_string())),
2590 1 => Ok(matched.into_iter().next().unwrap()),
2591 _ => Ok(ResultData::Error("#NUM!".to_string())),
2592 },
2593 "DCOUNT" => Ok(ResultData::Float(
2594 matched
2595 .iter()
2596 .filter(|v| Self::range_numeric(v).is_some())
2597 .count() as f64,
2598 )),
2599 "DCOUNTA" => Ok(ResultData::Float(
2600 matched.iter().map(|v| self.counta_helper(v)).sum::<usize>() as f64,
2601 )),
2602 _ => {
2603 let nums: Vec<f64> = matched.iter().filter_map(Self::range_numeric).collect();
2604 match func_name {
2605 "DSUM" => Ok(ResultData::Float(nums.iter().sum())),
2606 "DPRODUCT" => Ok(ResultData::Float(if nums.is_empty() {
2607 0.0
2608 } else {
2609 nums.iter().product()
2610 })),
2611 "DMAX" => {
2612 let m = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
2613 Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2614 }
2615 "DMIN" => {
2616 let m = nums.iter().cloned().fold(f64::INFINITY, f64::min);
2617 Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2618 }
2619 "DAVERAGE" => {
2620 if nums.is_empty() {
2621 Ok(ResultData::Error("#DIV/0!".to_string()))
2622 } else {
2623 Ok(ResultData::Float(
2624 nums.iter().sum::<f64>() / nums.len() as f64,
2625 ))
2626 }
2627 }
2628 "DSTDEV" => match crate::core::stats::stdev_s(&nums) {
2629 Ok(v) => Ok(ResultData::Float(v)),
2630 Err(e) => Ok(ResultData::Error(e)),
2631 },
2632 "DSTDEVP" => match crate::core::stats::stdev_p(&nums) {
2633 Ok(v) => Ok(ResultData::Float(v)),
2634 Err(e) => Ok(ResultData::Error(e)),
2635 },
2636 "DVAR" => match crate::core::stats::var_s(&nums) {
2637 Ok(v) => Ok(ResultData::Float(v)),
2638 Err(e) => Ok(ResultData::Error(e)),
2639 },
2640 "DVARP" => match crate::core::stats::var_p(&nums) {
2641 Ok(v) => Ok(ResultData::Float(v)),
2642 Err(e) => Ok(ResultData::Error(e)),
2643 },
2644 _ => unreachable!(),
2645 }
2646 }
2647 }
2648 }
2649
2650 fn proper(&self, s: &str) -> String {
2651 let mut c_chars = Vec::new();
2657 let mut capitalize_next = true;
2658 for c in s.chars() {
2659 if c.is_alphabetic() {
2660 if capitalize_next {
2661 c_chars.extend(c.to_uppercase());
2662 } else {
2663 c_chars.extend(c.to_lowercase());
2664 }
2665 capitalize_next = false;
2666 } else {
2667 c_chars.push(c);
2668 capitalize_next = true;
2669 }
2670 }
2671 c_chars.into_iter().collect()
2672 }
2673
2674 fn get_ymd_hms(&self) -> ((i32, u32, u32), (u32, u32, u32)) {
2675 let now = web_time::SystemTime::now()
2676 .duration_since(web_time::SystemTime::UNIX_EPOCH)
2677 .unwrap_or_default()
2678 .as_secs();
2679 let secs_in_day = 86400;
2680 let days_since_epoch = (now / secs_in_day) as i32;
2681 let seconds_of_day = (now % secs_in_day) as u32;
2682
2683 let hour = seconds_of_day / 3600;
2684 let minute = (seconds_of_day % 3600) / 60;
2685 let second = seconds_of_day % 60;
2686
2687 let era = (if days_since_epoch >= -719468 {
2688 days_since_epoch + 719468
2689 } else {
2690 days_since_epoch + 719468 - 146096
2691 }) / 146097;
2692 let doe = (days_since_epoch + 719468 - era * 146097) as u32;
2693 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
2694 let y = (yoe as i32) + era * 400;
2695 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2696 let mp = (5 * doy + 2) / 153;
2697 let d = doy - (153 * mp + 2) / 5 + 1;
2698 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2699 let year = if m <= 2 { y + 1 } else { y };
2700
2701 ((year, m, d), (hour, minute, second))
2702 }
2703
2704 fn evaluate_let(
2711 &self,
2712 args: &[crate::core::parser::Expr],
2713 context: Option<&Context>,
2714 row: Option<usize>,
2715 col: Option<usize>,
2716 deps: &mut Vec<Dependency>,
2717 scope: &LetScope<'_>,
2718 ) -> Result<ResultData, EngineError> {
2719 use crate::core::parser::Expr;
2720
2721 if args.is_empty() || args.len().is_multiple_of(2) {
2722 return Ok(ResultData::Error("#VALUE!".to_string()));
2725 }
2726 if args.len() == 1 {
2727 return self.evaluate_ast(&args[0], context, row, col, deps, scope);
2728 }
2729
2730 let name = match &args[0] {
2731 Expr::Identifier(n) => n.as_str(),
2732 _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2733 };
2734 let remaining_pairs = args.len() / 2 - 1;
2737 let is_duplicate = args[2..]
2738 .iter()
2739 .step_by(2)
2740 .take(remaining_pairs)
2741 .any(|a| matches!(a, Expr::Identifier(n2) if n2.eq_ignore_ascii_case(name)));
2742 if is_duplicate {
2743 return Ok(ResultData::Error("#VALUE!".to_string()));
2744 }
2745
2746 let value = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
2747 let inner_scope = LetScope::Bound {
2748 name,
2749 value: &value,
2750 parent: scope,
2751 };
2752 self.evaluate_let(&args[2..], context, row, col, deps, &inner_scope)
2753 }
2754
2755 fn extract_lambda(
2764 expr: &crate::core::parser::Expr,
2765 ) -> Option<(Vec<&str>, &crate::core::parser::Expr)> {
2766 use crate::core::parser::Expr;
2767 let Expr::FunctionCall { name, args } = expr else {
2768 return None;
2769 };
2770 if !name.eq_ignore_ascii_case("LAMBDA") || args.is_empty() {
2771 return None;
2772 }
2773 let (body, params) = args.split_last().unwrap();
2774 let param_names: Vec<&str> = params
2775 .iter()
2776 .filter_map(|p| match p {
2777 Expr::Identifier(n) => Some(n.as_str()),
2778 _ => None,
2779 })
2780 .collect();
2781 if param_names.len() != params.len() {
2782 return None;
2783 }
2784 Some((param_names, body))
2785 }
2786
2787 #[allow(clippy::too_many_arguments)]
2793 fn invoke_lambda<'v>(
2794 &self,
2795 params: &[&str],
2796 values: &'v [ResultData],
2797 body: &crate::core::parser::Expr,
2798 context: Option<&Context>,
2799 row: Option<usize>,
2800 col: Option<usize>,
2801 deps: &mut Vec<Dependency>,
2802 scope: &LetScope<'v>,
2803 ) -> Result<ResultData, EngineError> {
2804 match (params.split_first(), values.split_first()) {
2805 (Some((&pname, prest)), Some((vfirst, vrest))) => {
2806 let inner_scope = LetScope::Bound {
2807 name: pname,
2808 value: vfirst,
2809 parent: scope,
2810 };
2811 self.invoke_lambda(prest, vrest, body, context, row, col, deps, &inner_scope)
2812 }
2813 _ => self.evaluate_ast(body, context, row, col, deps, scope),
2814 }
2815 }
2816
2817 fn eval_as_array(
2821 &self,
2822 expr: &crate::core::parser::Expr,
2823 context: Option<&Context>,
2824 row: Option<usize>,
2825 col: Option<usize>,
2826 deps: &mut Vec<Dependency>,
2827 scope: &LetScope<'_>,
2828 ) -> Result<Vec<ResultData>, EngineError> {
2829 Ok(
2830 match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2831 ResultData::List(items) => Self::flatten_row_major(items).0,
2832 other => vec![other],
2833 },
2834 )
2835 }
2836
2837 fn flatten_row_major(items: Vec<ResultData>) -> (Vec<ResultData>, Option<usize>) {
2847 if !items.is_empty() && items.iter().all(|v| matches!(v, ResultData::List(_))) {
2848 let cols = match &items[0] {
2849 ResultData::List(inner) => inner.len().max(1),
2850 _ => 1,
2851 };
2852 let flat = items
2853 .into_iter()
2854 .flat_map(|v| match v {
2855 ResultData::List(inner) => inner,
2856 other => vec![other],
2857 })
2858 .collect();
2859 (flat, Some(cols))
2860 } else {
2861 (items, None)
2862 }
2863 }
2864
2865 fn array_shape(
2873 &self,
2874 expr: &crate::core::parser::Expr,
2875 context: Option<&Context>,
2876 row: Option<usize>,
2877 col: Option<usize>,
2878 deps: &mut Vec<Dependency>,
2879 scope: &LetScope<'_>,
2880 ) -> Result<(Vec<ResultData>, usize), EngineError> {
2881 use crate::core::parser::Expr;
2882 let items = match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2883 ResultData::List(items) => items,
2884 other => vec![other],
2885 };
2886 let (flat, nested_cols) = Self::flatten_row_major(items);
2887 if let Some(cols) = nested_cols {
2888 return Ok((flat, cols));
2889 }
2890 let num_cols = match expr {
2891 Expr::RangeRef {
2892 start_col, end_col, ..
2893 } => (end_col - start_col + 1).max(1),
2894 Expr::CellRef { .. } => 1,
2895 Expr::FunctionCall { name, args } => self
2896 .function_call_cols(name, args, context, row, col, deps, scope)
2897 .unwrap_or_else(|| flat.len().max(1)),
2898 _ => flat.len().max(1),
2899 };
2900 Ok((flat, num_cols))
2901 }
2902
2903 #[allow(clippy::too_many_arguments)]
2912 fn function_call_cols(
2913 &self,
2914 name: &str,
2915 args: &[crate::core::parser::Expr],
2916 context: Option<&Context>,
2917 row: Option<usize>,
2918 col: Option<usize>,
2919 deps: &mut Vec<Dependency>,
2920 scope: &LetScope<'_>,
2921 ) -> Option<usize> {
2922 let mut upper = name.to_ascii_uppercase();
2923 if let Some(rest) = upper.strip_prefix("_XLFN.") {
2924 upper = rest.to_string();
2925 }
2926 if let Some(rest) = upper.strip_prefix("_XLWS.") {
2927 upper = rest.to_string();
2928 }
2929 match upper.as_str() {
2930 "TRANSPOSE" => {
2931 let (flat, cols) = self
2932 .array_shape(args.first()?, context, row, col, deps, scope)
2933 .ok()?;
2934 Some((flat.len().checked_div(cols).unwrap_or(0)).max(1))
2935 }
2936 "HSTACK" => {
2937 let mut total = 0usize;
2938 for a in args {
2939 total += self.array_shape(a, context, row, col, deps, scope).ok()?.1;
2940 }
2941 Some(total)
2942 }
2943 "VSTACK" => {
2944 let mut max_cols = 0usize;
2945 for a in args {
2946 max_cols =
2947 max_cols.max(self.array_shape(a, context, row, col, deps, scope).ok()?.1);
2948 }
2949 Some(max_cols)
2950 }
2951 "CHOOSEROWS" => Some(
2952 self.array_shape(args.first()?, context, row, col, deps, scope)
2953 .ok()?
2954 .1,
2955 ),
2956 "CHOOSECOLS" => Some(args.len().saturating_sub(1).max(1)),
2957 "DROP" | "TAKE" => {
2958 let (_, cols) = self
2959 .array_shape(args.first()?, context, row, col, deps, scope)
2960 .ok()?;
2961 let is_take = upper == "TAKE";
2962 match args.get(2) {
2963 Some(e) => {
2964 let n = self
2965 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2966 .unwrap_or(0.0) as isize;
2967 let (s, e2) = Self::drop_take_bounds(cols as isize, n, is_take);
2968 Some((e2 - s).max(0) as usize)
2969 }
2970 None => Some(if is_take { cols } else { 0 }),
2971 }
2972 }
2973 "EXPAND" => {
2974 let (_, cols) = self
2975 .array_shape(args.first()?, context, row, col, deps, scope)
2976 .ok()?;
2977 match args.get(2) {
2978 Some(e) => Some(
2979 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2980 .unwrap_or(cols as f64) as usize,
2981 ),
2982 None => Some(cols),
2983 }
2984 }
2985 "TOCOL" => Some(1),
2986 "WRAPROWS" => {
2987 let n = self
2988 .to_f64(
2989 &self
2990 .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2991 .ok()?,
2992 )
2993 .unwrap_or(1.0)
2994 .max(1.0) as usize;
2995 Some(n)
2996 }
2997 "WRAPCOLS" => {
2998 let (flat, _) = self
2999 .array_shape(args.first()?, context, row, col, deps, scope)
3000 .ok()?;
3001 let wrap = self
3002 .to_f64(
3003 &self
3004 .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
3005 .ok()?,
3006 )
3007 .unwrap_or(1.0)
3008 .max(1.0) as usize;
3009 Some(flat.len().div_ceil(wrap).max(1))
3010 }
3011 "UNIQUE" | "SORT" | "SORTBY" | "FILTER" | "TRIMRANGE" => Some(
3012 self.array_shape(args.first()?, context, row, col, deps, scope)
3013 .ok()?
3014 .1,
3015 ),
3016 "SEQUENCE" => match args.get(1) {
3017 Some(e) => Some(
3018 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
3019 .unwrap_or(1.0)
3020 .max(1.0) as usize,
3021 ),
3022 None => Some(1),
3023 },
3024 "MUNIT" => {
3025 let n = self
3026 .to_f64(
3027 &self
3028 .evaluate_ast(args.first()?, context, row, col, deps, scope)
3029 .ok()?,
3030 )
3031 .unwrap_or(1.0)
3032 .max(1.0) as usize;
3033 Some(n)
3034 }
3035 "MAKEARRAY" => match args.get(1) {
3036 Some(e) => Some(
3037 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
3038 .unwrap_or(1.0)
3039 .max(1.0) as usize,
3040 ),
3041 None => Some(1),
3042 },
3043 _ => None,
3044 }
3045 }
3046
3047 fn drop_take_bounds(total: isize, n: isize, is_take: bool) -> (isize, isize) {
3051 let n = n.clamp(-total, total);
3052 if is_take {
3053 if n >= 0 { (0, n) } else { (total + n, total) }
3054 } else if n >= 0 {
3055 (n, total)
3056 } else {
3057 (0, total + n)
3058 }
3059 }
3060
3061 #[allow(clippy::too_many_arguments)]
3070 fn evaluate_lambda_function(
3071 &self,
3072 func_name: &str,
3073 args: &[crate::core::parser::Expr],
3074 context: Option<&Context>,
3075 row: Option<usize>,
3076 col: Option<usize>,
3077 deps: &mut Vec<Dependency>,
3078 scope: &LetScope<'_>,
3079 ) -> Result<ResultData, EngineError> {
3080 use crate::core::parser::Expr;
3081
3082 match func_name {
3083 "MAP" => {
3084 if args.len() < 2 {
3085 return Ok(ResultData::Error("#VALUE!".to_string()));
3086 }
3087 let (lambda_expr, array_exprs) = args.split_last().unwrap();
3088 let Some((params, body)) = Self::extract_lambda(lambda_expr) else {
3089 return Ok(ResultData::Error("#VALUE!".to_string()));
3090 };
3091 if params.len() != array_exprs.len() {
3092 return Ok(ResultData::Error("#VALUE!".to_string()));
3093 }
3094 let arrays: Vec<Vec<ResultData>> = array_exprs
3095 .iter()
3096 .map(|e| self.eval_as_array(e, context, row, col, deps, scope))
3097 .collect::<Result<_, _>>()?;
3098 let len = arrays.iter().map(|a| a.len()).max().unwrap_or(0);
3099 let mut results = Vec::with_capacity(len);
3100 for i in 0..len {
3101 let values: Vec<ResultData> = arrays
3102 .iter()
3103 .map(|a| a.get(i).cloned().unwrap_or(ResultData::None))
3104 .collect();
3105 results.push(
3106 self.invoke_lambda(¶ms, &values, body, context, row, col, deps, scope)?,
3107 );
3108 }
3109 Ok(ResultData::List(results))
3110 }
3111 "BYROW" | "BYCOL" => {
3112 if args.len() != 2 {
3113 return Ok(ResultData::Error("#VALUE!".to_string()));
3114 }
3115 let Some((params, body)) = Self::extract_lambda(&args[1]) else {
3116 return Ok(ResultData::Error("#VALUE!".to_string()));
3117 };
3118 if params.len() != 1 {
3119 return Ok(ResultData::Error("#VALUE!".to_string()));
3120 }
3121 let num_cols = match &args[0] {
3125 Expr::RangeRef {
3126 start_col, end_col, ..
3127 } => (end_col - start_col + 1).max(1),
3128 _ => 1,
3129 };
3130 let flat = self.eval_as_array(&args[0], context, row, col, deps, scope)?;
3131 let num_rows = if num_cols == 0 {
3132 0
3133 } else {
3134 flat.len().div_ceil(num_cols)
3135 };
3136 let mut results = Vec::new();
3137 if func_name == "BYROW" {
3138 for r in 0..num_rows {
3139 let row_vals: Vec<ResultData> = (0..num_cols)
3140 .filter_map(|c| flat.get(r * num_cols + c).cloned())
3141 .collect();
3142 let arg = vec![ResultData::List(row_vals)];
3143 results.push(
3144 self.invoke_lambda(
3145 ¶ms, &arg, body, context, row, col, deps, scope,
3146 )?,
3147 );
3148 }
3149 } else {
3150 for c in 0..num_cols {
3151 let col_vals: Vec<ResultData> = (0..num_rows)
3152 .filter_map(|r| flat.get(r * num_cols + c).cloned())
3153 .collect();
3154 let arg = vec![ResultData::List(col_vals)];
3155 results.push(
3156 self.invoke_lambda(
3157 ¶ms, &arg, body, context, row, col, deps, scope,
3158 )?,
3159 );
3160 }
3161 }
3162 Ok(ResultData::List(results))
3163 }
3164 "REDUCE" | "SCAN" => {
3165 if args.len() != 2 && args.len() != 3 {
3174 return Ok(ResultData::Error("#VALUE!".to_string()));
3175 }
3176 let lambda_idx = args.len() - 1;
3177 let array_idx = args.len() - 2;
3178 let Some((params, body)) = Self::extract_lambda(&args[lambda_idx]) else {
3179 return Ok(ResultData::Error("#VALUE!".to_string()));
3180 };
3181 if params.len() != 2 {
3182 return Ok(ResultData::Error("#VALUE!".to_string()));
3183 }
3184 let array = self.eval_as_array(&args[array_idx], context, row, col, deps, scope)?;
3185 let (mut acc, rest, mut history): (ResultData, &[ResultData], Vec<ResultData>) =
3191 if args.len() == 3 {
3192 let init = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
3193 (init, &array[..], Vec::new())
3194 } else {
3195 match array.split_first() {
3196 Some((first, rest)) => (first.clone(), rest, vec![first.clone()]),
3197 None => return Ok(ResultData::Error("#VALUE!".to_string())),
3198 }
3199 };
3200 for item in rest {
3201 let call_args = [acc.clone(), item.clone()];
3202 acc = self
3203 .invoke_lambda(¶ms, &call_args, body, context, row, col, deps, scope)?;
3204 history.push(acc.clone());
3205 }
3206 if func_name == "REDUCE" {
3207 Ok(acc)
3208 } else {
3209 Ok(ResultData::List(history))
3210 }
3211 }
3212 "MAKEARRAY" => {
3213 if args.len() != 3 {
3214 return Ok(ResultData::Error("#VALUE!".to_string()));
3215 }
3216 let Some((params, body)) = Self::extract_lambda(&args[2]) else {
3217 return Ok(ResultData::Error("#VALUE!".to_string()));
3218 };
3219 if params.len() != 2 {
3220 return Ok(ResultData::Error("#VALUE!".to_string()));
3221 }
3222 let rows_val = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
3223 let cols_val = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
3224 let num_rows = self.to_f64(&rows_val).unwrap_or(0.0).max(0.0) as usize;
3225 let num_cols = self.to_f64(&cols_val).unwrap_or(0.0).max(0.0) as usize;
3226 let mut results = Vec::with_capacity(num_rows * num_cols);
3227 for r in 1..=num_rows {
3228 for c in 1..=num_cols {
3229 let call_args = [ResultData::Float(r as f64), ResultData::Float(c as f64)];
3230 results.push(self.invoke_lambda(
3231 ¶ms, &call_args, body, context, row, col, deps, scope,
3232 )?);
3233 }
3234 }
3235 Ok(ResultData::List(results))
3236 }
3237 _ => unreachable!(),
3238 }
3239 }
3240
3241 fn parse_a1_reference(text: &str) -> Option<(Option<String>, usize, usize, usize, usize)> {
3250 let text = text.trim();
3251 let (sheet_part, ref_part) = match text.rfind('!') {
3252 Some(idx) => (Some(&text[..idx]), &text[idx + 1..]),
3253 None => (None, text),
3254 };
3255 let sheet = sheet_part.map(|s| s.trim().trim_matches('\'').to_string());
3256
3257 fn parse_cell(s: &str) -> Option<(usize, usize)> {
3258 let s = s.replace('$', "");
3259 let col_end = s.find(|c: char| c.is_ascii_digit())?;
3260 let (col_str, row_str) = s.split_at(col_end);
3261 if col_str.is_empty() || row_str.is_empty() {
3262 return None;
3263 }
3264 let mut col = 0usize;
3265 for ch in col_str.chars() {
3266 if !ch.is_ascii_alphabetic() {
3267 return None;
3268 }
3269 col = col * 26 + (ch.to_ascii_uppercase() as usize - 'A' as usize + 1);
3270 }
3271 let row: usize = row_str.parse().ok()?;
3272 if row == 0 || col == 0 {
3273 return None;
3274 }
3275 Some((row - 1, col - 1))
3276 }
3277
3278 if let Some((start, end)) = ref_part.split_once(':') {
3279 let (r1, c1) = parse_cell(start)?;
3280 let (r2, c2) = parse_cell(end)?;
3281 Some((sheet, r1.min(r2), c1.min(c2), r1.max(r2), c1.max(c2)))
3282 } else {
3283 let (r, c) = parse_cell(ref_part)?;
3284 Some((sheet, r, c, r, c))
3285 }
3286 }
3287
3288 fn read_cell_with_deps(
3295 &self,
3296 sheet_opt: &Option<String>,
3297 r: usize,
3298 c: usize,
3299 context: Option<&Context>,
3300 deps: &mut Vec<Dependency>,
3301 ) -> ResultData {
3302 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3303 if is_self {
3304 deps.push(Dependency::Local(CellRef::new(r, c)));
3305 self.get_result_data(&CellRef::new(r, c))
3306 } else if let Some(ctx) = context {
3307 let name = sheet_opt.clone().unwrap();
3308 deps.push(Dependency::Remote {
3309 sheet: name.clone(),
3310 cell: CellRef::new(r, c),
3311 });
3312 ctx.sheets
3313 .get(&name)
3314 .map(|s| s.get_result_data(&CellRef::new(r, c)))
3315 .unwrap_or(ResultData::None)
3316 } else {
3317 ResultData::None
3318 }
3319 }
3320
3321 #[allow(clippy::too_many_arguments)]
3330 fn evaluate_range_info_function(
3331 &self,
3332 func_name: &str,
3333 args: &[crate::core::parser::Expr],
3334 context: Option<&Context>,
3335 row: Option<usize>,
3336 col: Option<usize>,
3337 deps: &mut Vec<Dependency>,
3338 scope: &LetScope<'_>,
3339 ) -> Result<ResultData, EngineError> {
3340 use crate::core::parser::Expr;
3341
3342 match func_name {
3343 "ROW" => match args.first() {
3344 Some(arg) => match Self::range_bounds(arg) {
3349 Some((_, start_row, _, end_row, _)) if end_row > start_row => {
3350 Ok(ResultData::List(
3351 (start_row..=end_row)
3352 .map(|r| ResultData::Float((r + 1) as f64))
3353 .collect(),
3354 ))
3355 }
3356 Some((_, start_row, _, _, _)) => Ok(ResultData::Float((start_row + 1) as f64)),
3357 None => Ok(ResultData::Error("#VALUE!".to_string())),
3358 },
3359 None => match row {
3360 Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3361 None => Ok(ResultData::Error("#VALUE!".to_string())),
3362 },
3363 },
3364 "COLUMN" => match args.first() {
3365 Some(arg) => match Self::range_bounds(arg) {
3368 Some((_, _, start_col, _, end_col)) if end_col > start_col => {
3369 Ok(ResultData::List(
3370 (start_col..=end_col)
3371 .map(|c| ResultData::Float((c + 1) as f64))
3372 .collect(),
3373 ))
3374 }
3375 Some((_, _, start_col, _, _)) => Ok(ResultData::Float((start_col + 1) as f64)),
3376 None => Ok(ResultData::Error("#VALUE!".to_string())),
3377 },
3378 None => match col {
3379 Some(c) => Ok(ResultData::Float((c + 1) as f64)),
3380 None => Ok(ResultData::Error("#VALUE!".to_string())),
3381 },
3382 },
3383 "ROWS" => {
3384 let Some(arg) = args.first() else {
3385 return Ok(ResultData::Error("#VALUE!".to_string()));
3386 };
3387 let Some((sheet_opt, start_row, _, end_row, _)) = Self::range_bounds(arg) else {
3388 return Ok(ResultData::Error("#VALUE!".to_string()));
3389 };
3390 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3391 let actual_end_row = if end_row == usize::MAX {
3392 if is_self {
3393 self.row_count().saturating_sub(1)
3394 } else {
3395 context
3396 .and_then(|ctx| sheet_opt.as_ref().and_then(|n| ctx.sheets.get(n)))
3397 .map(|s| s.row_count().saturating_sub(1))
3398 .unwrap_or(0)
3399 }
3400 } else {
3401 end_row
3402 };
3403 Ok(ResultData::Float(
3404 (actual_end_row.saturating_sub(start_row) + 1) as f64,
3405 ))
3406 }
3407 "COLUMNS" => {
3408 let Some(arg) = args.first() else {
3409 return Ok(ResultData::Error("#VALUE!".to_string()));
3410 };
3411 match Self::range_bounds(arg) {
3412 Some((_, _, start_col, _, end_col)) => Ok(ResultData::Float(
3413 (end_col.saturating_sub(start_col) + 1) as f64,
3414 )),
3415 None => Ok(ResultData::Error("#VALUE!".to_string())),
3416 }
3417 }
3418 "AREAS" => {
3419 if args.is_empty() {
3423 Ok(ResultData::Error("#VALUE!".to_string()))
3424 } else {
3425 Ok(ResultData::Float(1.0))
3426 }
3427 }
3428 "ISREF" => Ok(ResultData::Boolean(matches!(
3429 args.first(),
3430 Some(Expr::CellRef { .. } | Expr::RangeRef { .. } | Expr::StructuredRef { .. })
3431 ))),
3432 "FORMULATEXT" | "ISFORMULA" => {
3433 let Some(arg) = args.first() else {
3434 return Ok(ResultData::Error("#VALUE!".to_string()));
3435 };
3436 let Some((sheet_opt, r, c, _, _)) = Self::range_bounds(arg) else {
3437 return Ok(ResultData::Error("#VALUE!".to_string()));
3438 };
3439 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3440 let src = if is_self {
3441 deps.push(Dependency::Local(CellRef::new(r, c)));
3442 self.get_src_str(&CellRef::new(r, c))
3443 } else if let Some(ctx) = context {
3444 let name = sheet_opt.unwrap();
3445 deps.push(Dependency::Remote {
3446 sheet: name.clone(),
3447 cell: CellRef::new(r, c),
3448 });
3449 ctx.sheets
3450 .get(&name)
3451 .map(|s| s.get_src_str(&CellRef::new(r, c)))
3452 .unwrap_or_default()
3453 } else {
3454 String::new()
3455 };
3456 let is_formula = src.starts_with('=');
3457 if func_name == "ISFORMULA" {
3458 Ok(ResultData::Boolean(is_formula))
3459 } else if is_formula {
3460 Ok(ResultData::String(src))
3461 } else {
3462 Ok(ResultData::Error("#N/A".to_string()))
3463 }
3464 }
3465 "SHEETS" => Ok(ResultData::Float(
3466 context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3467 )),
3468 "SHEET" => {
3469 let sheet_name = match args.first() {
3475 None => Some(self.name.clone()),
3476 Some(arg) => match Self::range_bounds(arg) {
3477 Some((sheet_opt, ..)) => {
3478 Some(sheet_opt.unwrap_or_else(|| self.name.clone()))
3479 }
3480 None => self
3481 .evaluate_ast(arg, context, row, col, deps, scope)
3482 .ok()
3483 .map(|v| v.to_string()),
3484 },
3485 };
3486
3487 match sheet_name {
3488 Some(name) => {
3489 let ordinal = context
3490 .and_then(|c| {
3491 c.sheet_order
3492 .iter()
3493 .position(|n| n.eq_ignore_ascii_case(&name))
3494 })
3495 .map(|i| i + 1)
3496 .unwrap_or(1);
3502 Ok(ResultData::Float(ordinal as f64))
3503 }
3504 None => Ok(ResultData::Error("#N/A".to_string())),
3505 }
3506 }
3507 "CELL" => {
3508 if args.is_empty() {
3509 return Ok(ResultData::Error("#VALUE!".to_string()));
3510 }
3511 let info_type = self
3512 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3513 .to_string()
3514 .to_lowercase();
3515 let bounds = args.get(1).and_then(Self::range_bounds);
3516 match info_type.as_str() {
3517 "row" => match bounds.map(|b| b.1).or(row) {
3518 Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3519 None => Ok(ResultData::Error("#VALUE!".to_string())),
3520 },
3521 "col" => match bounds {
3522 Some((_, _, c, _, _)) => Ok(ResultData::Float((c + 1) as f64)),
3523 None => Ok(ResultData::Error("#VALUE!".to_string())),
3524 },
3525 "address" => match bounds {
3526 Some((_, r, c, _, _)) => Ok(ResultData::String(format!(
3527 "${}${}",
3528 crate::core::parser::col_idx_to_letters(c),
3529 r + 1
3530 ))),
3531 None => Ok(ResultData::Error("#VALUE!".to_string())),
3532 },
3533 "contents" => match bounds {
3534 Some((sheet_opt, r, c, _, _)) => {
3535 Ok(self.read_cell_with_deps(&sheet_opt, r, c, context, deps))
3536 }
3537 None => Ok(ResultData::Error("#VALUE!".to_string())),
3538 },
3539 _ => Ok(ResultData::Error("#VALUE!".to_string())),
3540 }
3541 }
3542 "INFO" => {
3543 if args.is_empty() {
3544 return Ok(ResultData::Error("#VALUE!".to_string()));
3545 }
3546 let info_type = self
3547 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3548 .to_string()
3549 .to_lowercase();
3550 match info_type.as_str() {
3551 "numfile" => Ok(ResultData::Float(
3552 context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3553 )),
3554 "release" => Ok(ResultData::String("16.0".to_string())),
3555 "system" => Ok(ResultData::String(
3556 if cfg!(target_os = "macos") {
3557 "mac"
3558 } else {
3559 "pcdos"
3560 }
3561 .to_string(),
3562 )),
3563 _ => Ok(ResultData::Error("#VALUE!".to_string())),
3564 }
3565 }
3566 "INDIRECT" => {
3567 if args.is_empty() {
3568 return Ok(ResultData::Error("#VALUE!".to_string()));
3569 }
3570 let text = self
3571 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3572 .to_string();
3573 let a1_style = match args.get(1) {
3574 Some(a) => self.to_bool(&self.evaluate_ast(a, context, row, col, deps, scope)?),
3575 None => true,
3576 };
3577 if !a1_style {
3578 return Ok(ResultData::Error("#VALUE!".to_string()));
3580 }
3581 match Self::parse_a1_reference(&text) {
3582 Some((sheet_opt, start_row, start_col, end_row, end_col)) => {
3583 if start_row == end_row && start_col == end_col {
3584 Ok(self.read_cell_with_deps(
3585 &sheet_opt, start_row, start_col, context, deps,
3586 ))
3587 } else {
3588 match self.materialize_range(
3589 &sheet_opt, start_row, start_col, end_row, end_col, context,
3590 ) {
3591 Some(grid) => {
3592 Ok(ResultData::List(grid.into_iter().flatten().collect()))
3593 }
3594 None => Ok(ResultData::Error("#REF!".to_string())),
3595 }
3596 }
3597 }
3598 None => Ok(ResultData::Error("#REF!".to_string())),
3599 }
3600 }
3601 "OFFSET" => {
3602 if args.len() < 3 {
3603 return Ok(ResultData::Error("#VALUE!".to_string()));
3604 }
3605 let Some((sheet_opt, base_row, base_col, base_end_row, base_end_col)) =
3606 Self::range_bounds(&args[0])
3607 else {
3608 return Ok(ResultData::Error("#VALUE!".to_string()));
3609 };
3610 let row_offset = self
3611 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3612 .unwrap_or(0.0) as isize;
3613 let col_offset = self
3614 .to_f64(&self.evaluate_ast(&args[2], context, row, col, deps, scope)?)
3615 .unwrap_or(0.0) as isize;
3616 let base_height = (base_end_row.saturating_sub(base_row) + 1) as isize;
3617 let base_width = (base_end_col.saturating_sub(base_col) + 1) as isize;
3618 let height = match args.get(3) {
3619 Some(a) => self
3620 .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3621 .unwrap_or(base_height as f64) as isize,
3622 None => base_height,
3623 };
3624 let width = match args.get(4) {
3625 Some(a) => self
3626 .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3627 .unwrap_or(base_width as f64) as isize,
3628 None => base_width,
3629 };
3630 let new_row = base_row as isize + row_offset;
3631 let new_col = base_col as isize + col_offset;
3632 if new_row < 0 || new_col < 0 || height <= 0 || width <= 0 {
3633 return Ok(ResultData::Error("#REF!".to_string()));
3634 }
3635 let (start_row, start_col) = (new_row as usize, new_col as usize);
3636 let (end_row, end_col) = (
3637 start_row + (height - 1) as usize,
3638 start_col + (width - 1) as usize,
3639 );
3640 if start_row == end_row && start_col == end_col {
3641 Ok(self.read_cell_with_deps(&sheet_opt, start_row, start_col, context, deps))
3642 } else {
3643 match self.materialize_range(
3644 &sheet_opt, start_row, start_col, end_row, end_col, context,
3645 ) {
3646 Some(grid) => Ok(ResultData::List(grid.into_iter().flatten().collect())),
3647 None => Ok(ResultData::Error("#REF!".to_string())),
3648 }
3649 }
3650 }
3651 _ => unreachable!(),
3652 }
3653 }
3654
3655 fn evaluate_getpivotdata(
3662 &self,
3663 args: &[crate::core::parser::Expr],
3664 context: Option<&Context>,
3665 row: Option<usize>,
3666 col: Option<usize>,
3667 deps: &mut Vec<Dependency>,
3668 scope: &LetScope<'_>,
3669 ) -> Result<ResultData, EngineError> {
3670 if args.len() < 2 || !(args.len() - 2).is_multiple_of(2) {
3671 return Ok(ResultData::Error("#VALUE!".to_string()));
3672 }
3673
3674 let data_field = self
3675 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3676 .to_string();
3677
3678 let (sheet_opt, target_row, target_col, _, _) = match Self::range_bounds(&args[1]) {
3679 Some(bounds) => bounds,
3680 None => return Ok(ResultData::Error("#REF!".to_string())),
3681 };
3682 self.read_cell_with_deps(&sheet_opt, target_row, target_col, context, deps);
3685
3686 let sheet_id = match &sheet_opt {
3687 None => self.id,
3688 Some(name) if name == &self.name => self.id,
3689 Some(name) => match context.and_then(|c| c.sheets.get(name)) {
3690 Some(s) => s.id,
3691 None => return Ok(ResultData::Error("#REF!".to_string())),
3692 },
3693 };
3694
3695 let pivot_tables = context.map(|c| c.pivot_tables).unwrap_or(&[]);
3696 let pivot = match pivot_tables.iter().find(|p| {
3697 p.dest_sheet_id == sheet_id
3698 && p.last_output_end_row
3699 .is_some_and(|end| target_row >= p.dest_row && target_row <= end)
3700 && p.last_output_end_col
3701 .is_some_and(|end| target_col >= p.dest_col && target_col <= end)
3702 }) {
3703 Some(p) => p,
3704 None => return Ok(ResultData::Error("#REF!".to_string())),
3705 };
3706
3707 let mut criteria: Vec<(String, String)> = Vec::new();
3708 let mut i = 2;
3709 while i < args.len() {
3710 let field = self
3711 .evaluate_ast(&args[i], context, row, col, deps, scope)?
3712 .to_string();
3713 let item = self
3714 .evaluate_ast(&args[i + 1], context, row, col, deps, scope)?
3715 .to_string();
3716 criteria.push((field, item));
3717 i += 2;
3718 }
3719
3720 let mut sheet_refs: Vec<&Sheet> = context
3721 .map(|c| c.sheets.values().copied().collect())
3722 .unwrap_or_default();
3723 sheet_refs.push(self);
3724
3725 match crate::core::pivot::getpivotdata(&sheet_refs, pivot, &data_field, &criteria) {
3726 Ok(v) => Ok(v),
3727 Err(e) => Ok(ResultData::Error(e)),
3728 }
3729 }
3730
3731 #[allow(clippy::too_many_arguments)]
3746 fn evaluate_array_reshape_function(
3747 &self,
3748 func_name: &str,
3749 args: &[crate::core::parser::Expr],
3750 context: Option<&Context>,
3751 row: Option<usize>,
3752 col: Option<usize>,
3753 deps: &mut Vec<Dependency>,
3754 scope: &LetScope<'_>,
3755 ) -> Result<ResultData, EngineError> {
3756 match func_name {
3757 "TRANSPOSE" => {
3758 let Some(arg) = args.first() else {
3759 return Ok(ResultData::Error("#VALUE!".to_string()));
3760 };
3761 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3762 let rows = flat.len().checked_div(cols).unwrap_or(0);
3763 let mut result = Vec::with_capacity(flat.len());
3764 for c in 0..cols {
3765 for r in 0..rows {
3766 result.push(flat[r * cols + c].clone());
3767 }
3768 }
3769 Ok(ResultData::List(result))
3770 }
3771 "HSTACK" | "VSTACK" => {
3772 if args.is_empty() {
3773 return Ok(ResultData::Error("#VALUE!".to_string()));
3774 }
3775 let mut shapes = Vec::with_capacity(args.len());
3776 for a in args {
3777 shapes.push(self.array_shape(a, context, row, col, deps, scope)?);
3778 }
3779 let mut result = Vec::new();
3780 if func_name == "HSTACK" {
3781 let max_rows = shapes
3782 .iter()
3783 .map(|(f, c)| if *c == 0 { 0 } else { f.len() / c })
3784 .max()
3785 .unwrap_or(0);
3786 for r in 0..max_rows {
3787 for (flat, cols) in &shapes {
3788 let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3789 for c in 0..*cols {
3790 result.push(if r < rows {
3791 flat[r * cols + c].clone()
3792 } else {
3793 ResultData::Error("#N/A".to_string())
3794 });
3795 }
3796 }
3797 }
3798 } else {
3799 let max_cols = shapes.iter().map(|(_, c)| *c).max().unwrap_or(0);
3800 for (flat, cols) in &shapes {
3801 let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3802 for r in 0..rows {
3803 for c in 0..max_cols {
3804 result.push(if c < *cols {
3805 flat[r * cols + c].clone()
3806 } else {
3807 ResultData::Error("#N/A".to_string())
3808 });
3809 }
3810 }
3811 }
3812 }
3813 Ok(ResultData::List(result))
3814 }
3815 "CHOOSEROWS" | "CHOOSECOLS" => {
3816 if args.len() < 2 {
3817 return Ok(ResultData::Error("#VALUE!".to_string()));
3818 }
3819 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3820 let rows = flat.len().checked_div(cols).unwrap_or(0);
3821 let total = if func_name == "CHOOSEROWS" {
3822 rows
3823 } else {
3824 cols
3825 } as isize;
3826 let mut indices = Vec::with_capacity(args.len() - 1);
3827 for idx_expr in &args[1..] {
3828 let n = self
3829 .to_f64(&self.evaluate_ast(idx_expr, context, row, col, deps, scope)?)
3830 .unwrap_or(0.0) as isize;
3831 let real_idx = if n < 0 { total + n } else { n - 1 };
3832 if real_idx < 0 || real_idx >= total {
3833 return Ok(ResultData::Error("#VALUE!".to_string()));
3834 }
3835 indices.push(real_idx as usize);
3836 }
3837 let mut result = Vec::new();
3838 if func_name == "CHOOSEROWS" {
3839 for r in indices {
3840 for c in 0..cols {
3841 result.push(flat[r * cols + c].clone());
3842 }
3843 }
3844 } else {
3845 for r in 0..rows {
3846 for &c in &indices {
3847 result.push(flat[r * cols + c].clone());
3848 }
3849 }
3850 }
3851 Ok(ResultData::List(result))
3852 }
3853 "DROP" | "TAKE" => {
3854 if args.len() < 2 {
3855 return Ok(ResultData::Error("#VALUE!".to_string()));
3856 }
3857 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3858 let num_rows = flat.len().checked_div(cols).unwrap_or(0) as isize;
3859 let is_take = func_name == "TAKE";
3860 let rows_n = self
3861 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3862 .unwrap_or(0.0) as isize;
3863 let cols_n = match args.get(2) {
3864 Some(e) => self
3865 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3866 .unwrap_or(0.0) as isize,
3867 None => {
3868 if is_take {
3869 cols as isize
3870 } else {
3871 0
3872 }
3873 }
3874 };
3875 let (row_start, row_end) = Self::drop_take_bounds(num_rows, rows_n, is_take);
3876 let (col_start, col_end) = Self::drop_take_bounds(cols as isize, cols_n, is_take);
3877 if row_start >= row_end || col_start >= col_end {
3878 return Ok(ResultData::Error("#CALC!".to_string()));
3879 }
3880 let mut result = Vec::new();
3881 for r in row_start..row_end {
3882 for c in col_start..col_end {
3883 result.push(flat[(r as usize) * cols + (c as usize)].clone());
3884 }
3885 }
3886 Ok(ResultData::List(result))
3887 }
3888 "EXPAND" => {
3889 if args.len() < 2 {
3890 return Ok(ResultData::Error("#VALUE!".to_string()));
3891 }
3892 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3893 let orig_rows = flat.len().checked_div(cols).unwrap_or(0);
3894 let new_rows = self
3895 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3896 .unwrap_or(orig_rows as f64) as usize;
3897 let new_cols = match args.get(2) {
3898 Some(e) => self
3899 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3900 .unwrap_or(cols as f64) as usize,
3901 None => cols,
3902 };
3903 let pad = match args.get(3) {
3904 Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3905 None => ResultData::Error("#N/A".to_string()),
3906 };
3907 if new_rows < orig_rows || new_cols < cols {
3908 return Ok(ResultData::Error("#VALUE!".to_string()));
3909 }
3910 let mut result = Vec::with_capacity(new_rows * new_cols);
3911 for r in 0..new_rows {
3912 for c in 0..new_cols {
3913 result.push(if r < orig_rows && c < cols {
3914 flat[r * cols + c].clone()
3915 } else {
3916 pad.clone()
3917 });
3918 }
3919 }
3920 Ok(ResultData::List(result))
3921 }
3922 "TOCOL" | "TOROW" => {
3923 let Some(arg) = args.first() else {
3924 return Ok(ResultData::Error("#VALUE!".to_string()));
3925 };
3926 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3927 let rows = flat.len().checked_div(cols).unwrap_or(0);
3928 let ignore = match args.get(1) {
3929 Some(e) => self
3930 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3931 .unwrap_or(0.0) as i64,
3932 None => 0,
3933 };
3934 let scan_by_col = match args.get(2) {
3935 Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3936 None => false,
3937 };
3938 let ordered: Vec<ResultData> = if scan_by_col {
3939 let mut v = Vec::with_capacity(flat.len());
3940 for c in 0..cols {
3941 for r in 0..rows {
3942 v.push(flat[r * cols + c].clone());
3943 }
3944 }
3945 v
3946 } else {
3947 flat
3948 };
3949 let filtered: Vec<ResultData> = ordered
3950 .into_iter()
3951 .filter(|v| match ignore {
3952 1 => !matches!(v, ResultData::None),
3953 2 => !matches!(v, ResultData::Error(_)),
3954 3 => !matches!(v, ResultData::None | ResultData::Error(_)),
3955 _ => true,
3956 })
3957 .collect();
3958 Ok(ResultData::List(filtered))
3959 }
3960 "WRAPROWS" | "WRAPCOLS" => {
3961 if args.len() < 2 {
3962 return Ok(ResultData::Error("#VALUE!".to_string()));
3963 }
3964 let (flat, _cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3965 let wrap = self
3966 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3967 .unwrap_or(1.0)
3968 .max(1.0) as usize;
3969 let pad = match args.get(2) {
3970 Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3971 None => ResultData::Error("#N/A".to_string()),
3972 };
3973 if func_name == "WRAPROWS" {
3974 let mut result = flat;
3977 let rem = result.len() % wrap;
3978 if rem != 0 {
3979 result.extend(std::iter::repeat_n(pad, wrap - rem));
3980 }
3981 Ok(ResultData::List(result))
3982 } else {
3983 let num_result_cols = flat.len().div_ceil(wrap).max(1);
3984 let total = wrap * num_result_cols;
3985 let mut result = Vec::with_capacity(total);
3986 for i in 0..total {
3987 let col = i / wrap;
3988 let r = i % wrap;
3989 let target = r * num_result_cols + col;
3990 while result.len() <= target {
3991 result.push(pad.clone());
3992 }
3993 if i < flat.len() {
3994 result[target] = flat[i].clone();
3995 }
3996 }
3997 Ok(ResultData::List(result))
3998 }
3999 }
4000 "UNIQUE" => {
4001 let Some(arg) = args.first() else {
4002 return Ok(ResultData::Error("#VALUE!".to_string()));
4003 };
4004 let (flat, _cols) = self.array_shape(arg, context, row, col, deps, scope)?;
4005 let exactly_once = match args.get(2) {
4006 Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
4007 None => false,
4008 };
4009 let mut seen: Vec<(String, ResultData, usize)> = Vec::new();
4010 for v in &flat {
4011 let key = match v {
4015 ResultData::None => "blank:".to_string(),
4016 ResultData::Boolean(b) => format!("bool:{b}"),
4017 ResultData::Integer(i) => format!("num:{}", *i as f64),
4018 ResultData::Float(f) => format!("num:{f}"),
4019 ResultData::String(s) => format!("str:{s}"),
4020 ResultData::Error(e) => format!("err:{e}"),
4021 ResultData::List(_) | ResultData::Dict(_) => format!("other:{v}"),
4022 };
4023 match seen.iter_mut().find(|(k, ..)| k == &key) {
4024 Some(entry) => entry.2 += 1,
4025 None => seen.push((key, v.clone(), 1)),
4026 }
4027 }
4028 let result: Vec<ResultData> = seen
4029 .into_iter()
4030 .filter(|(_, _, count)| !exactly_once || *count == 1)
4031 .map(|(_, v, _)| v)
4032 .collect();
4033 Ok(ResultData::List(result))
4034 }
4035 "SORT" => {
4036 let Some(arg) = args.first() else {
4037 return Ok(ResultData::Error("#VALUE!".to_string()));
4038 };
4039 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
4040 let rows = flat.len().checked_div(cols).unwrap_or(0);
4041 let sort_index = match args.get(1) {
4042 Some(e) => self
4043 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
4044 .unwrap_or(1.0) as usize,
4045 None => 1,
4046 };
4047 let sort_order = match args.get(2) {
4048 Some(e) => self
4049 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
4050 .unwrap_or(1.0),
4051 None => 1.0,
4052 };
4053 let col_idx = sort_index.saturating_sub(1).min(cols.saturating_sub(1));
4054 let mut row_indices: Vec<usize> = (0..rows).collect();
4055 row_indices.sort_by(|&a, &b| {
4056 Self::sort_compare_blanks_last(
4057 &flat[a * cols + col_idx],
4058 &flat[b * cols + col_idx],
4059 sort_order,
4060 )
4061 });
4062 let mut result = Vec::with_capacity(flat.len());
4063 for r in row_indices {
4064 for c in 0..cols {
4065 result.push(flat[r * cols + c].clone());
4066 }
4067 }
4068 Ok(ResultData::List(result))
4069 }
4070 "SORTBY" => {
4071 if args.len() < 2 {
4072 return Ok(ResultData::Error("#VALUE!".to_string()));
4073 }
4074 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
4075 let rows = flat.len().checked_div(cols).unwrap_or(0);
4076 let by = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
4077 let order = match args.get(2) {
4078 Some(e) => self
4079 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
4080 .unwrap_or(1.0),
4081 None => 1.0,
4082 };
4083 let mut row_indices: Vec<usize> = (0..rows).collect();
4084 row_indices.sort_by(|&a, &b| {
4085 let va = by.get(a).cloned().unwrap_or(ResultData::None);
4086 let vb = by.get(b).cloned().unwrap_or(ResultData::None);
4087 Self::sort_compare_blanks_last(&va, &vb, order)
4088 });
4089 let mut result = Vec::with_capacity(flat.len());
4090 for r in row_indices {
4091 for c in 0..cols {
4092 result.push(flat[r * cols + c].clone());
4093 }
4094 }
4095 Ok(ResultData::List(result))
4096 }
4097 "FILTER" => {
4098 if args.len() < 2 {
4099 return Ok(ResultData::Error("#VALUE!".to_string()));
4100 }
4101 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
4102 let rows = flat.len().checked_div(cols).unwrap_or(0);
4103 let include = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
4104 let mut result = Vec::new();
4105 for r in 0..rows {
4106 let keep = include.get(r).map(|v| self.to_bool(v)).unwrap_or(false);
4107 if keep {
4108 for c in 0..cols {
4109 result.push(flat[r * cols + c].clone());
4110 }
4111 }
4112 }
4113 if result.is_empty() {
4114 match args.get(2) {
4115 Some(e) => Ok(self.evaluate_ast(e, context, row, col, deps, scope)?),
4116 None => Ok(ResultData::Error("#CALC!".to_string())),
4117 }
4118 } else {
4119 Ok(ResultData::List(result))
4120 }
4121 }
4122 "TRIMRANGE" => {
4123 let Some(arg) = args.first() else {
4124 return Ok(ResultData::Error("#VALUE!".to_string()));
4125 };
4126 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
4127 let rows = flat.len().checked_div(cols).unwrap_or(0);
4128 let is_blank = |v: &ResultData| {
4129 matches!(v, ResultData::None)
4130 || matches!(v, ResultData::String(s) if s.is_empty())
4131 };
4132 let row_blank = |r: usize| (0..cols).all(|c| is_blank(&flat[r * cols + c]));
4133 let col_blank = |c: usize| (0..rows).all(|r| is_blank(&flat[r * cols + c]));
4134 let mut r_start = 0;
4135 while r_start < rows && row_blank(r_start) {
4136 r_start += 1;
4137 }
4138 let mut r_end = rows;
4139 while r_end > r_start && row_blank(r_end - 1) {
4140 r_end -= 1;
4141 }
4142 let mut c_start = 0;
4143 while c_start < cols && col_blank(c_start) {
4144 c_start += 1;
4145 }
4146 let mut c_end = cols;
4147 while c_end > c_start && col_blank(c_end - 1) {
4148 c_end -= 1;
4149 }
4150 let mut result = Vec::new();
4151 for r in r_start..r_end {
4152 for c in c_start..c_end {
4153 result.push(flat[r * cols + c].clone());
4154 }
4155 }
4156 Ok(ResultData::List(result))
4157 }
4158 _ => unreachable!(),
4159 }
4160 }
4161
4162 pub fn get_src(&self, cell: &CellRef) -> Option<&String> {
4169 let col = self.columns.get(cell.col);
4170 if let Some(col) = col {
4171 col.src.get(cell.row)
4172 } else {
4173 None
4174 }
4175 }
4176
4177 pub fn get_src_str(&self, cell: &CellRef) -> String {
4180 let col = self.columns.get(cell.col);
4181 if let Some(col) = col {
4182 col.src.get(cell.row).cloned().unwrap_or("".to_string())
4183 } else {
4184 "".to_string()
4185 }
4186 }
4187
4188 pub fn get_src_str_ref(&self, cell: &CellRef) -> Option<&str> {
4190 let col = self.columns.get(cell.col)?;
4191 col.src.get(cell.row).map(|s| s.as_str())
4192 }
4193
4194 pub fn get_word_boundaries(&self, cell: &CellRef, char_offset: usize) -> (usize, usize) {
4198 let text = self.get_src_str(cell);
4199 get_word_boundaries_from_str(&text, char_offset)
4200 }
4201}