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