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