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