1use crate::structured_ref;
2use crate::tokenizer::{
3 Associativity, Token, TokenSpan, TokenStream, TokenSubType, TokenType, TokenizerError,
4};
5use crate::types::{FormulaDialect, ParsingError};
6use crate::{ExcelError, LiteralValue};
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::hasher::FormulaHasher;
12use formualizer_common::coord::{
13 col_index_from_letters_1based, col_letters_from_1based, parse_a1_1based,
14};
15use formualizer_common::{
16 AxisBound, RelativeCoord, SheetCellRef, SheetLocator, SheetRangeRef, SheetRef,
17};
18use once_cell::sync::Lazy;
19use smallvec::SmallVec;
20use std::error::Error;
21use std::fmt::{self, Display};
22use std::hash::{Hash, Hasher};
23use std::str::FromStr;
24use std::sync::Arc;
25
26type VolatilityFn = dyn Fn(&str) -> bool + Send + Sync + 'static;
27type VolatilityClassifierBox = Box<VolatilityFn>;
28type VolatilityClassifierArc = Arc<VolatilityFn>;
29
30#[derive(Debug)]
32pub struct ParserError {
33 pub message: String,
34 pub position: Option<usize>,
35}
36
37impl Display for ParserError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 if let Some(pos) = self.position {
40 write!(f, "ParserError at position {}: {}", pos, self.message)
41 } else {
42 write!(f, "ParserError: {}", self.message)
43 }
44 }
45}
46
47impl Error for ParserError {}
48
49static COLUMN_LOOKUP: Lazy<Vec<String>> = Lazy::new(|| {
51 let mut cols = Vec::with_capacity(702);
52 for c in b'A'..=b'Z' {
54 cols.push(String::from(c as char));
55 }
56 for c1 in b'A'..=b'Z' {
58 for c2 in b'A'..=b'Z' {
59 cols.push(format!("{}{}", c1 as char, c2 as char));
60 }
61 }
62 cols
63});
64
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
67#[derive(Debug, Clone, PartialEq, Hash)]
68pub enum TableSpecifier {
69 All,
71 Data,
73 Headers,
75 Totals,
77 Row(TableRowSpecifier),
79 Column(String),
81 ColumnRange(String, String),
83 SpecialItem(SpecialItem),
85 Combination(Vec<Box<TableSpecifier>>),
87}
88
89#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
91#[derive(Debug, Clone, PartialEq, Hash)]
92pub enum TableRowSpecifier {
93 Current,
95 All,
97 Data,
99 Headers,
101 Totals,
103 Index(u32),
105}
106
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
109#[derive(Debug, Clone, PartialEq, Hash)]
110pub enum SpecialItem {
111 Headers,
113 Data,
115 Totals,
117 All,
119 ThisRow,
121}
122
123#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
125#[derive(Debug, Clone, PartialEq, Hash)]
126pub struct TableReference {
127 pub name: String,
129 pub specifier: Option<TableSpecifier>,
131}
132
133#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
134#[derive(Debug, Clone, PartialEq, Hash)]
135pub enum ExternalBookRef {
136 Token(String),
137}
138
139impl ExternalBookRef {
140 pub fn token(&self) -> &str {
141 match self {
142 ExternalBookRef::Token(s) => s,
143 }
144 }
145}
146
147#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
149pub enum ExternalRefKind {
150 Cell {
151 row: u32,
152 col: u32,
153 row_abs: bool,
154 col_abs: bool,
155 },
156 Range {
157 start_row: Option<u32>,
158 start_col: Option<u32>,
159 end_row: Option<u32>,
160 end_col: Option<u32>,
161 start_row_abs: bool,
162 start_col_abs: bool,
163 end_row_abs: bool,
164 end_col_abs: bool,
165 },
166}
167
168impl ExternalRefKind {
169 pub fn cell(row: u32, col: u32) -> Self {
170 Self::Cell {
171 row,
172 col,
173 row_abs: false,
174 col_abs: false,
175 }
176 }
177
178 pub fn cell_with_abs(row: u32, col: u32, row_abs: bool, col_abs: bool) -> Self {
179 Self::Cell {
180 row,
181 col,
182 row_abs,
183 col_abs,
184 }
185 }
186
187 pub fn range(
188 start_row: Option<u32>,
189 start_col: Option<u32>,
190 end_row: Option<u32>,
191 end_col: Option<u32>,
192 ) -> Self {
193 Self::Range {
194 start_row,
195 start_col,
196 end_row,
197 end_col,
198 start_row_abs: false,
199 start_col_abs: false,
200 end_row_abs: false,
201 end_col_abs: false,
202 }
203 }
204
205 #[allow(clippy::too_many_arguments)]
208 pub fn range_with_abs(
209 start_row: Option<u32>,
210 start_col: Option<u32>,
211 end_row: Option<u32>,
212 end_col: Option<u32>,
213 start_row_abs: bool,
214 start_col_abs: bool,
215 end_row_abs: bool,
216 end_col_abs: bool,
217 ) -> Self {
218 Self::Range {
219 start_row,
220 start_col,
221 end_row,
222 end_col,
223 start_row_abs,
224 start_col_abs,
225 end_row_abs,
226 end_col_abs,
227 }
228 }
229}
230
231#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
232#[derive(Debug, Clone, PartialEq, Hash)]
233pub struct ExternalReference {
234 pub raw: String,
235 pub book: ExternalBookRef,
236 pub sheet: String,
237 pub kind: ExternalRefKind,
238}
239
240#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
242#[derive(Debug, Clone, PartialEq, Hash)]
243pub enum ReferenceType {
244 Cell {
245 sheet: Option<String>,
246 row: u32,
247 col: u32,
248 row_abs: bool,
249 col_abs: bool,
250 },
251 Range {
252 sheet: Option<String>,
253 start_row: Option<u32>,
254 start_col: Option<u32>,
255 end_row: Option<u32>,
256 end_col: Option<u32>,
257 start_row_abs: bool,
258 start_col_abs: bool,
259 end_row_abs: bool,
260 end_col_abs: bool,
261 },
262 Cell3D {
267 sheet_first: String,
268 sheet_last: String,
269 row: u32,
270 col: u32,
271 row_abs: bool,
272 col_abs: bool,
273 },
274 Range3D {
276 sheet_first: String,
277 sheet_last: String,
278 start_row: Option<u32>,
279 start_col: Option<u32>,
280 end_row: Option<u32>,
281 end_col: Option<u32>,
282 start_row_abs: bool,
283 start_col_abs: bool,
284 end_row_abs: bool,
285 end_col_abs: bool,
286 },
287 External(ExternalReference),
288 Table(TableReference),
289 NamedRange(String),
290}
291
292impl Display for TableSpecifier {
293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 match self {
295 TableSpecifier::All => write!(f, "#All"),
296 TableSpecifier::Data => write!(f, "#Data"),
297 TableSpecifier::Headers => write!(f, "#Headers"),
298 TableSpecifier::Totals => write!(f, "#Totals"),
299 TableSpecifier::Row(row) => write!(f, "{row}"),
300 TableSpecifier::Column(column) => write!(f, "{column}"),
301 TableSpecifier::ColumnRange(start, end) => write!(f, "{start}:{end}"),
302 TableSpecifier::SpecialItem(item) => write!(f, "{item}"),
303 TableSpecifier::Combination(specs) => {
304 let mut first = true;
310 for spec in specs {
311 if !first {
312 write!(f, ",")?;
313 }
314 first = false;
315 match spec.as_ref() {
316 TableSpecifier::ColumnRange(start, end) => {
317 write!(f, "[{start}]:[{end}]")?;
318 }
319 other => write!(f, "[{other}]")?,
320 }
321 }
322 Ok(())
323 }
324 }
325 }
326}
327
328impl Display for TableRowSpecifier {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 match self {
331 TableRowSpecifier::Current => write!(f, "@"),
332 TableRowSpecifier::All => write!(f, "#All"),
333 TableRowSpecifier::Data => write!(f, "#Data"),
334 TableRowSpecifier::Headers => write!(f, "#Headers"),
335 TableRowSpecifier::Totals => write!(f, "#Totals"),
336 TableRowSpecifier::Index(idx) => write!(f, "{idx}"),
337 }
338 }
339}
340
341impl Display for SpecialItem {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 match self {
344 SpecialItem::Headers => write!(f, "#Headers"),
345 SpecialItem::Data => write!(f, "#Data"),
346 SpecialItem::Totals => write!(f, "#Totals"),
347 SpecialItem::All => write!(f, "#All"),
348 SpecialItem::ThisRow => write!(f, "@"),
349 }
350 }
351}
352
353fn sheet_name_needs_quoting(name: &str) -> bool {
354 formualizer_common::a1_sheet_name_needs_quoting(name)
355}
356
357#[derive(Debug, Clone)]
358struct OpenFormulaRefPart {
359 sheet: Option<String>,
360 coord: String,
361}
362
363type AxisPartWithAbs = Option<(u32, bool)>;
364type RangePartWithAbs = (AxisPartWithAbs, AxisPartWithAbs);
365
366#[derive(Debug, Clone)]
368enum SheetSpec {
369 None,
371 Single(String),
373 Range { first: String, last: String },
375}
376
377impl ReferenceType {
378 pub fn cell(sheet: Option<String>, row: u32, col: u32) -> Self {
380 Self::Cell {
381 sheet,
382 row,
383 col,
384 row_abs: false,
385 col_abs: false,
386 }
387 }
388
389 pub fn cell_with_abs(
391 sheet: Option<String>,
392 row: u32,
393 col: u32,
394 row_abs: bool,
395 col_abs: bool,
396 ) -> Self {
397 Self::Cell {
398 sheet,
399 row,
400 col,
401 row_abs,
402 col_abs,
403 }
404 }
405
406 pub fn range(
408 sheet: Option<String>,
409 start_row: Option<u32>,
410 start_col: Option<u32>,
411 end_row: Option<u32>,
412 end_col: Option<u32>,
413 ) -> Self {
414 Self::Range {
415 sheet,
416 start_row,
417 start_col,
418 end_row,
419 end_col,
420 start_row_abs: false,
421 start_col_abs: false,
422 end_row_abs: false,
423 end_col_abs: false,
424 }
425 }
426
427 #[allow(clippy::too_many_arguments)]
431 pub fn range_with_abs(
432 sheet: Option<String>,
433 start_row: Option<u32>,
434 start_col: Option<u32>,
435 end_row: Option<u32>,
436 end_col: Option<u32>,
437 start_row_abs: bool,
438 start_col_abs: bool,
439 end_row_abs: bool,
440 end_col_abs: bool,
441 ) -> Self {
442 Self::Range {
443 sheet,
444 start_row,
445 start_col,
446 end_row,
447 end_col,
448 start_row_abs,
449 start_col_abs,
450 end_row_abs,
451 end_col_abs,
452 }
453 }
454
455 pub fn from_string(reference: &str) -> Result<Self, ParsingError> {
457 Self::parse_excel_reference(reference)
458 }
459
460 pub fn from_string_with_dialect(
462 reference: &str,
463 dialect: FormulaDialect,
464 ) -> Result<Self, ParsingError> {
465 match dialect {
466 FormulaDialect::Excel => Self::parse_excel_reference(reference),
467 FormulaDialect::OpenFormula => Self::parse_openformula_reference(reference)
468 .or_else(|_| Self::parse_excel_reference(reference)),
469 }
470 }
471
472 pub fn parse_sheet_ref(reference: &str) -> Result<SheetRef<'static>, ParsingError> {
476 Self::parse_sheet_ref_with_dialect(reference, FormulaDialect::Excel)
477 }
478
479 pub fn parse_sheet_ref_with_dialect(
481 reference: &str,
482 dialect: FormulaDialect,
483 ) -> Result<SheetRef<'static>, ParsingError> {
484 match dialect {
485 FormulaDialect::Excel => Self::parse_excel_sheet_ref(reference),
486 FormulaDialect::OpenFormula => Self::parse_openformula_sheet_ref(reference)
487 .or_else(|_| Self::parse_excel_sheet_ref(reference)),
488 }
489 }
490
491 pub fn to_sheet_ref_lossy(&self) -> Option<SheetRef<'_>> {
494 match self {
495 ReferenceType::Cell {
496 sheet,
497 row,
498 col,
499 row_abs,
500 col_abs,
501 } => {
502 let row0 = row.checked_sub(1)?;
503 let col0 = col.checked_sub(1)?;
504 let sheet_loc = match sheet.as_deref() {
505 Some(name) => SheetLocator::from_name(name),
506 None => SheetLocator::Current,
507 };
508 let coord = RelativeCoord::new(row0, col0, *row_abs, *col_abs);
509 Some(SheetRef::Cell(SheetCellRef::new(sheet_loc, coord)))
510 }
511 ReferenceType::Range {
512 sheet,
513 start_row,
514 start_col,
515 end_row,
516 end_col,
517 start_row_abs,
518 start_col_abs,
519 end_row_abs,
520 end_col_abs,
521 } => {
522 let sheet_loc = match sheet.as_deref() {
523 Some(name) => SheetLocator::from_name(name),
524 None => SheetLocator::Current,
525 };
526 let sr = start_row
527 .and_then(|v| v.checked_sub(1).map(|i| AxisBound::new(i, *start_row_abs)));
528 if start_row.is_some() && sr.is_none() {
529 return None;
530 }
531 let sc = start_col
532 .and_then(|v| v.checked_sub(1).map(|i| AxisBound::new(i, *start_col_abs)));
533 if start_col.is_some() && sc.is_none() {
534 return None;
535 }
536 let er =
537 end_row.and_then(|v| v.checked_sub(1).map(|i| AxisBound::new(i, *end_row_abs)));
538 if end_row.is_some() && er.is_none() {
539 return None;
540 }
541 let ec =
542 end_col.and_then(|v| v.checked_sub(1).map(|i| AxisBound::new(i, *end_col_abs)));
543 if end_col.is_some() && ec.is_none() {
544 return None;
545 }
546 let range = SheetRangeRef::from_parts(sheet_loc, sr, sc, er, ec).ok()?;
547 Some(SheetRef::Range(range))
548 }
549 _ => None,
550 }
551 }
552
553 fn parse_excel_sheet_ref(reference: &str) -> Result<SheetRef<'static>, ParsingError> {
554 let (spec, ref_part) = Self::extract_sheet_spec(reference);
555 if matches!(spec, SheetSpec::Range { .. }) {
556 return Err(ParsingError::InvalidReference(
557 "3D references are not supported for SheetRef".to_string(),
558 ));
559 }
560 let sheet = match spec {
561 SheetSpec::None => None,
562 SheetSpec::Single(name) => Some(name),
563 SheetSpec::Range { .. } => unreachable!(),
564 };
565
566 if ref_part.contains('[') {
567 return Err(ParsingError::InvalidReference(
568 "Table references are not supported for SheetRef".to_string(),
569 ));
570 }
571
572 let sheet_loc: SheetLocator<'static> = match sheet {
573 Some(name) => SheetLocator::from_name(name),
574 None => SheetLocator::Current,
575 };
576
577 if ref_part.contains(':') {
578 let mut parts = ref_part.splitn(2, ':');
579 let start = parts.next().unwrap();
580 let end = parts.next().ok_or_else(|| {
581 ParsingError::InvalidReference(format!("Invalid range: {ref_part}"))
582 })?;
583
584 let (start_col, start_row) = Self::parse_range_part_with_abs(start)?;
585 let (end_col, end_row) = Self::parse_range_part_with_abs(end)?;
586
587 let start_col = Self::axis_bound_from_1based(start_col)?;
588 let start_row = Self::axis_bound_from_1based(start_row)?;
589 let end_col = Self::axis_bound_from_1based(end_col)?;
590 let end_row = Self::axis_bound_from_1based(end_row)?;
591
592 let range =
593 SheetRangeRef::from_parts(sheet_loc, start_row, start_col, end_row, end_col)
594 .map_err(|err| ParsingError::InvalidReference(err.to_string()))?;
595 Ok(SheetRef::Range(range))
596 } else {
597 let (row, col, row_abs, col_abs) = parse_a1_1based(&ref_part)
598 .map_err(|err| ParsingError::InvalidReference(err.to_string()))?;
599 let coord = RelativeCoord::new(row - 1, col - 1, row_abs, col_abs);
600 Ok(SheetRef::Cell(SheetCellRef::new(sheet_loc, coord)))
601 }
602 }
603
604 fn parse_openformula_sheet_ref(reference: &str) -> Result<SheetRef<'static>, ParsingError> {
605 Self::parse_excel_sheet_ref(reference)
606 }
607
608 fn axis_bound_from_1based(
609 bound: Option<(u32, bool)>,
610 ) -> Result<Option<AxisBound>, ParsingError> {
611 match bound {
612 Some((index, abs)) => AxisBound::from_excel_1based(index, abs)
613 .map(Some)
614 .map_err(|err| ParsingError::InvalidReference(err.to_string())),
615 None => Ok(None),
616 }
617 }
618
619 fn parse_range_part_with_abs(part: &str) -> Result<RangePartWithAbs, ParsingError> {
620 if let Ok((row, col, row_abs, col_abs)) = parse_a1_1based(part) {
621 return Ok((Some((col, col_abs)), Some((row, row_abs))));
622 }
623
624 let bytes = part.as_bytes();
625 let len = bytes.len();
626 let mut i = 0usize;
627
628 let mut col_abs = false;
629 let mut row_abs = false;
630
631 if i < len && bytes[i] == b'$' {
632 col_abs = true;
633 i += 1;
634 }
635
636 let col_start = i;
637 while i < len && bytes[i].is_ascii_alphabetic() {
638 i += 1;
639 }
640
641 if i > col_start {
642 let col_str = &part[col_start..i];
643 let col1 = Self::column_to_number(col_str)?;
644
645 if i == len {
646 return Ok((Some((col1, col_abs)), None));
647 }
648
649 if i < len && bytes[i] == b'$' {
650 row_abs = true;
651 i += 1;
652 }
653
654 if i >= len {
655 return Err(ParsingError::InvalidReference(format!(
656 "Invalid range part: {part}"
657 )));
658 }
659
660 let row_start = i;
661 while i < len && bytes[i].is_ascii_digit() {
662 i += 1;
663 }
664
665 if row_start == i || i != len {
666 return Err(ParsingError::InvalidReference(format!(
667 "Invalid range part: {part}"
668 )));
669 }
670
671 let row_str = &part[row_start..i];
672 let row1 = row_str
673 .parse::<u32>()
674 .map_err(|_| ParsingError::InvalidReference(format!("Invalid row: {row_str}")))?;
675 if row1 == 0 {
676 return Err(ParsingError::InvalidReference(format!(
677 "Invalid range part: {part}"
678 )));
679 }
680
681 return Ok((Some((col1, col_abs)), Some((row1, row_abs))));
682 }
683
684 i = 0;
685 if i < len && bytes[i] == b'$' {
686 row_abs = true;
687 i += 1;
688 }
689
690 let row_start = i;
691 while i < len && bytes[i].is_ascii_digit() {
692 i += 1;
693 }
694
695 if row_start == i || i != len {
696 return Err(ParsingError::InvalidReference(format!(
697 "Invalid range part: {part}"
698 )));
699 }
700
701 let row_str = &part[row_start..i];
702 let row1 = row_str
703 .parse::<u32>()
704 .map_err(|_| ParsingError::InvalidReference(format!("Invalid row: {row_str}")))?;
705 if row1 == 0 {
706 return Err(ParsingError::InvalidReference(format!(
707 "Invalid range part: {part}"
708 )));
709 }
710
711 Ok((None, Some((row1, row_abs))))
712 }
713
714 fn parse_3d_reference(first: &str, last: &str, ref_part: &str) -> Result<Self, ParsingError> {
715 if first.is_empty() || last.is_empty() {
716 return Err(ParsingError::InvalidReference(format!(
717 "3D reference requires two sheet names: {first}:{last}!{ref_part}"
718 )));
719 }
720 if ref_part.is_empty() {
721 return Err(ParsingError::InvalidReference(format!(
722 "3D reference {first}:{last}! is missing a cell or range"
723 )));
724 }
725 if ref_part.contains('[') {
727 return Err(ParsingError::InvalidReference(format!(
728 "3D reference {first}:{last}!{ref_part} cannot target a table"
729 )));
730 }
731
732 if ref_part.contains(':') {
733 let mut parts = ref_part.splitn(2, ':');
734 let start = parts.next().unwrap();
735 let end = parts.next().ok_or_else(|| {
736 ParsingError::InvalidReference(format!("Invalid range: {ref_part}"))
737 })?;
738 let (start_col, start_row) = Self::parse_range_part_with_abs(start)?;
739 let (end_col, end_row) = Self::parse_range_part_with_abs(end)?;
740
741 let split = |bound: Option<(u32, bool)>| match bound {
742 Some((index, abs)) => (Some(index), abs),
743 None => (None, false),
744 };
745 let (start_col, start_col_abs) = split(start_col);
746 let (start_row, start_row_abs) = split(start_row);
747 let (end_col, end_col_abs) = split(end_col);
748 let (end_row, end_row_abs) = split(end_row);
749
750 Ok(ReferenceType::Range3D {
751 sheet_first: first.to_string(),
752 sheet_last: last.to_string(),
753 start_row,
754 start_col,
755 end_row,
756 end_col,
757 start_row_abs,
758 start_col_abs,
759 end_row_abs,
760 end_col_abs,
761 })
762 } else {
763 let (col, row, col_abs, row_abs) =
764 Self::parse_cell_reference(ref_part).map_err(|_| {
765 ParsingError::InvalidReference(format!(
766 "Invalid 3D reference target: {ref_part}"
767 ))
768 })?;
769 Ok(ReferenceType::Cell3D {
770 sheet_first: first.to_string(),
771 sheet_last: last.to_string(),
772 row,
773 col,
774 row_abs,
775 col_abs,
776 })
777 }
778 }
779
780 fn parse_excel_reference(reference: &str) -> Result<Self, ParsingError> {
781 if reference.starts_with('[') && reference.ends_with(']') && !reference.contains('!') {
788 return Self::parse_bracketed_structured_reference(reference);
789 }
790
791 let (sheet_spec, ref_part) = Self::extract_sheet_spec(reference);
793
794 if let SheetSpec::Range { first, last, .. } = &sheet_spec {
797 return Self::parse_3d_reference(first, last, &ref_part);
798 }
799
800 let sheet = match sheet_spec {
801 SheetSpec::None => None,
802 SheetSpec::Single(name) => Some(name),
803 SheetSpec::Range { .. } => unreachable!(),
805 };
806
807 if ref_part.contains('[') {
810 if Self::is_r1c1_shape(&ref_part) {
818 return Ok(ReferenceType::NamedRange(reference.to_string()));
819 }
820 return Self::parse_table_reference(&ref_part);
821 }
822
823 let external_sheet = sheet.as_deref().and_then(|s| {
824 let lb = s.rfind('[')?;
828 let rb_rel = s[lb..].find(']')?;
829 let rb = lb + rb_rel;
830 if lb >= rb {
831 return None;
832 }
833
834 let token = &s[..=rb];
835 let sheet_name = &s[rb + 1..];
836 if sheet_name.is_empty() {
837 None
838 } else {
839 Some((token, sheet_name))
840 }
841 });
842
843 if ref_part.contains(':') {
844 let mut parts = ref_part.splitn(2, ':');
846 let start = parts.next().unwrap();
847 let end = parts.next().ok_or_else(|| {
848 ParsingError::InvalidReference(format!("Invalid range: {ref_part}"))
849 })?;
850 let (start_col, start_row) = Self::parse_range_part_with_abs(start)?;
851 let (end_col, end_row) = Self::parse_range_part_with_abs(end)?;
852
853 let split = |bound: Option<(u32, bool)>| match bound {
854 Some((index, abs)) => (Some(index), abs),
855 None => (None, false),
856 };
857 let (start_col, start_col_abs) = split(start_col);
858 let (start_row, start_row_abs) = split(start_row);
859 let (end_col, end_col_abs) = split(end_col);
860 let (end_row, end_row_abs) = split(end_row);
861
862 if let Some((book_token, sheet_name)) = external_sheet {
863 Ok(ReferenceType::External(ExternalReference {
864 raw: reference.to_string(),
865 book: ExternalBookRef::Token(book_token.to_string()),
866 sheet: sheet_name.to_string(),
867 kind: ExternalRefKind::Range {
868 start_row,
869 start_col,
870 end_row,
871 end_col,
872 start_row_abs,
873 start_col_abs,
874 end_row_abs,
875 end_col_abs,
876 },
877 }))
878 } else {
879 Ok(ReferenceType::Range {
880 sheet,
881 start_row,
882 start_col,
883 end_row,
884 end_col,
885 start_row_abs,
886 start_col_abs,
887 end_row_abs,
888 end_col_abs,
889 })
890 }
891 } else {
892 match Self::parse_cell_reference(&ref_part) {
894 Ok((col, row, col_abs, row_abs)) => {
895 if let Some((book_token, sheet_name)) = external_sheet {
896 Ok(ReferenceType::External(ExternalReference {
897 raw: reference.to_string(),
898 book: ExternalBookRef::Token(book_token.to_string()),
899 sheet: sheet_name.to_string(),
900 kind: ExternalRefKind::Cell {
901 row,
902 col,
903 row_abs,
904 col_abs,
905 },
906 }))
907 } else {
908 Ok(ReferenceType::Cell {
909 sheet,
910 row,
911 col,
912 row_abs,
913 col_abs,
914 })
915 }
916 }
917 Err(_) => {
918 Ok(ReferenceType::NamedRange(reference.to_string()))
920 }
921 }
922 }
923 }
924
925 fn parse_cell_reference(reference: &str) -> Result<(u32, u32, bool, bool), ParsingError> {
927 parse_a1_1based(reference)
928 .map(|(row, col, row_abs, col_abs)| (col, row, col_abs, row_abs))
929 .map_err(|_| {
930 ParsingError::InvalidReference(format!("Invalid cell reference: {reference}"))
931 })
932 }
933
934 pub(crate) fn column_to_number(column: &str) -> Result<u32, ParsingError> {
936 col_index_from_letters_1based(column)
937 .map_err(|_| ParsingError::InvalidReference(format!("Invalid column: {column}")))
938 }
939
940 pub(crate) fn number_to_column(num: u32) -> String {
942 if num == 0 {
943 return String::new();
944 }
945 if num > 0 && num <= 702 {
947 return COLUMN_LOOKUP[(num - 1) as usize].clone();
948 }
949
950 col_letters_from_1based(num).unwrap_or_default()
951 }
952
953 fn format_col(col: u32, abs: bool) -> String {
954 if abs {
955 format!("${}", Self::number_to_column(col))
956 } else {
957 Self::number_to_column(col)
958 }
959 }
960
961 fn format_row(row: u32, abs: bool) -> String {
962 if abs {
963 format!("${row}")
964 } else {
965 row.to_string()
966 }
967 }
968}
969
970impl Display for ReferenceType {
971 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
972 write!(
973 f,
974 "{}",
975 match self {
976 ReferenceType::Cell {
977 sheet,
978 row,
979 col,
980 row_abs,
981 col_abs,
982 } => {
983 let col_str = Self::format_col(*col, *col_abs);
984 let row_str = Self::format_row(*row, *row_abs);
985
986 if let Some(sheet_name) = sheet {
987 format!(
988 "{}!{col_str}{row_str}",
989 formualizer_common::format_a1_sheet_name(sheet_name)
990 )
991 } else {
992 format!("{col_str}{row_str}")
993 }
994 }
995 ReferenceType::Range {
996 sheet,
997 start_row,
998 start_col,
999 end_row,
1000 end_col,
1001 start_row_abs,
1002 start_col_abs,
1003 end_row_abs,
1004 end_col_abs,
1005 } => {
1006 let start_ref = match (start_col, start_row) {
1008 (Some(col), Some(row)) => format!(
1009 "{}{}",
1010 Self::format_col(*col, *start_col_abs),
1011 Self::format_row(*row, *start_row_abs)
1012 ),
1013 (Some(col), None) => Self::format_col(*col, *start_col_abs),
1014 (None, Some(row)) => Self::format_row(*row, *start_row_abs),
1015 (None, None) => "".to_string(), };
1017
1018 let end_ref = match (end_col, end_row) {
1020 (Some(col), Some(row)) => format!(
1021 "{}{}",
1022 Self::format_col(*col, *end_col_abs),
1023 Self::format_row(*row, *end_row_abs)
1024 ),
1025 (Some(col), None) => Self::format_col(*col, *end_col_abs),
1026 (None, Some(row)) => Self::format_row(*row, *end_row_abs),
1027 (None, None) => "".to_string(), };
1029
1030 let range_part = format!("{start_ref}:{end_ref}");
1031
1032 if let Some(sheet_name) = sheet {
1033 format!(
1034 "{}!{range_part}",
1035 formualizer_common::format_a1_sheet_name(sheet_name)
1036 )
1037 } else {
1038 range_part
1039 }
1040 }
1041 ReferenceType::Cell3D {
1042 sheet_first,
1043 sheet_last,
1044 row,
1045 col,
1046 row_abs,
1047 col_abs,
1048 } => {
1049 let col_str = Self::format_col(*col, *col_abs);
1050 let row_str = Self::format_row(*row, *row_abs);
1051 let prefix = format_3d_sheet_prefix(sheet_first, sheet_last);
1052 format!("{prefix}!{col_str}{row_str}")
1053 }
1054 ReferenceType::Range3D {
1055 sheet_first,
1056 sheet_last,
1057 start_row,
1058 start_col,
1059 end_row,
1060 end_col,
1061 start_row_abs,
1062 start_col_abs,
1063 end_row_abs,
1064 end_col_abs,
1065 } => {
1066 let start_ref = match (start_col, start_row) {
1067 (Some(col), Some(row)) => format!(
1068 "{}{}",
1069 Self::format_col(*col, *start_col_abs),
1070 Self::format_row(*row, *start_row_abs)
1071 ),
1072 (Some(col), None) => Self::format_col(*col, *start_col_abs),
1073 (None, Some(row)) => Self::format_row(*row, *start_row_abs),
1074 (None, None) => "".to_string(),
1075 };
1076 let end_ref = match (end_col, end_row) {
1077 (Some(col), Some(row)) => format!(
1078 "{}{}",
1079 Self::format_col(*col, *end_col_abs),
1080 Self::format_row(*row, *end_row_abs)
1081 ),
1082 (Some(col), None) => Self::format_col(*col, *end_col_abs),
1083 (None, Some(row)) => Self::format_row(*row, *end_row_abs),
1084 (None, None) => "".to_string(),
1085 };
1086 let range_part = format!("{start_ref}:{end_ref}");
1087 let prefix = format_3d_sheet_prefix(sheet_first, sheet_last);
1088 format!("{prefix}!{range_part}")
1089 }
1090 ReferenceType::External(ext) => ext.raw.clone(),
1091 ReferenceType::Table(table_ref) => {
1092 if let Some(specifier) = &table_ref.specifier {
1093 match specifier {
1096 TableSpecifier::Column(column) => {
1097 format!("{}[{}]", table_ref.name, column.trim())
1098 }
1099 TableSpecifier::ColumnRange(start, end) => {
1100 format!("{}[{}:{}]", table_ref.name, start.trim(), end.trim())
1101 }
1102 _ => {
1103 format!("{}[{}]", table_ref.name, specifier)
1105 }
1106 }
1107 } else {
1108 table_ref.name.clone()
1109 }
1110 }
1111 ReferenceType::NamedRange(name) => name.clone(),
1112 }
1113 )
1114 }
1115}
1116
1117fn format_3d_sheet_prefix(first: &str, last: &str) -> String {
1121 let format_one = |name: &str| -> String {
1122 if sheet_name_needs_quoting(name) {
1123 let escaped = name.replace('\'', "''");
1124 format!("'{escaped}'")
1125 } else {
1126 name.to_string()
1127 }
1128 };
1129 format!("{}:{}", format_one(first), format_one(last))
1130}
1131
1132impl TryFrom<&str> for ReferenceType {
1133 type Error = ParsingError;
1134
1135 fn try_from(value: &str) -> Result<Self, Self::Error> {
1136 ReferenceType::from_string(value)
1137 }
1138}
1139
1140impl FromStr for ReferenceType {
1141 type Err = ParsingError;
1142
1143 fn from_str(s: &str) -> Result<Self, Self::Err> {
1144 ReferenceType::from_string(s)
1145 }
1146}
1147
1148impl ReferenceType {
1149 pub fn normalise(&self) -> String {
1151 format!("{self}")
1152 }
1153
1154 fn read_sheet_segment(reference: &str, start: usize) -> Option<(String, usize, bool)> {
1159 let bytes = reference.as_bytes();
1160 if start >= bytes.len() {
1161 return None;
1162 }
1163
1164 if bytes[start] == b'\'' {
1165 let mut i = start + 1;
1167 let body_start = i;
1168 while i < bytes.len() {
1169 if bytes[i] == b'\'' {
1170 if i + 1 < bytes.len() && bytes[i + 1] == b'\'' {
1171 i += 2;
1172 continue;
1173 }
1174 let raw = &reference[body_start..i];
1175 let name = raw.replace("''", "'");
1176 return Some((name, i + 1, true));
1177 }
1178 i += 1;
1179 }
1180 None
1181 } else {
1182 let mut i = start;
1185 while i < bytes.len() {
1186 let b = bytes[i];
1187 match b {
1188 b':' | b'!' | b'\'' | b' ' | b'\t' | b'\n' | b'\r' => break,
1189 _ => i += 1,
1190 }
1191 }
1192 if i == start {
1193 None
1194 } else {
1195 Some((reference[start..i].to_string(), i, false))
1196 }
1197 }
1198 }
1199
1200 fn extract_sheet_spec(reference: &str) -> (SheetSpec, String) {
1203 let Some((first_name, after_first, first_quoted)) = Self::read_sheet_segment(reference, 0)
1204 else {
1205 return Self::extract_sheet_spec_fallback(reference);
1208 };
1209 let _ = first_quoted;
1210
1211 let bytes = reference.as_bytes();
1212
1213 if after_first < bytes.len() && bytes[after_first] == b':' {
1215 let second_start = after_first + 1;
1216 if let Some((second_name, after_second, _)) =
1217 Self::read_sheet_segment(reference, second_start)
1218 && after_second < bytes.len()
1219 && bytes[after_second] == b'!'
1220 {
1221 let ref_part = reference[after_second + 1..].to_string();
1222 return (
1223 SheetSpec::Range {
1224 first: first_name,
1225 last: second_name,
1226 },
1227 ref_part,
1228 );
1229 }
1230
1231 if second_start < bytes.len() {
1237 if let Some(bang) = reference[second_start..].find('!') {
1238 let ref_part = reference[second_start + bang + 1..].to_string();
1239 return (
1240 SheetSpec::Range {
1241 first: first_name,
1242 last: String::new(),
1243 },
1244 ref_part,
1245 );
1246 }
1247 }
1248 }
1249
1250 if after_first < bytes.len() && bytes[after_first] == b'!' {
1252 let ref_part = reference[after_first + 1..].to_string();
1253 return (SheetSpec::Single(first_name), ref_part);
1254 }
1255
1256 Self::extract_sheet_spec_fallback(reference)
1259 }
1260
1261 fn extract_sheet_spec_fallback(reference: &str) -> (SheetSpec, String) {
1262 let bytes = reference.as_bytes();
1263 let mut i = 0;
1268 while i < bytes.len() {
1269 if bytes[i] == b'!' && i > 0 {
1270 let sheet = reference[..i].to_string();
1271 let ref_part = reference[i + 1..].to_string();
1272 return (SheetSpec::Single(sheet), ref_part);
1273 }
1274 i += 1;
1275 }
1276
1277 (SheetSpec::None, reference.to_string())
1278 }
1279
1280 fn is_r1c1_shape(s: &str) -> bool {
1290 let bytes = s.as_bytes();
1291 let len = bytes.len();
1292 let mut i = 0usize;
1293 let mut anchored = false;
1294
1295 if i >= len || bytes[i] != b'R' {
1296 return false;
1297 }
1298 i += 1;
1299
1300 let row_digits_start = i;
1301 while i < len && bytes[i].is_ascii_digit() {
1302 i += 1;
1303 }
1304 if i > row_digits_start {
1305 anchored = true;
1306 }
1307
1308 if i < len && bytes[i] == b'[' {
1309 i += 1;
1310 if i < len && bytes[i] == b'-' {
1311 i += 1;
1312 }
1313 let n_start = i;
1314 while i < len && bytes[i].is_ascii_digit() {
1315 i += 1;
1316 }
1317 if i == n_start || i >= len || bytes[i] != b']' {
1318 return false;
1319 }
1320 i += 1;
1321 anchored = true;
1322 }
1323
1324 if i >= len || bytes[i] != b'C' {
1325 return false;
1326 }
1327 i += 1;
1328
1329 let col_digits_start = i;
1330 while i < len && bytes[i].is_ascii_digit() {
1331 i += 1;
1332 }
1333 if i > col_digits_start {
1334 anchored = true;
1335 }
1336
1337 if i < len && bytes[i] == b'[' {
1338 i += 1;
1339 if i < len && bytes[i] == b'-' {
1340 i += 1;
1341 }
1342 let n_start = i;
1343 while i < len && bytes[i].is_ascii_digit() {
1344 i += 1;
1345 }
1346 if i == n_start || i >= len || bytes[i] != b']' {
1347 return false;
1348 }
1349 i += 1;
1350 anchored = true;
1351 }
1352
1353 i == len && anchored
1354 }
1355
1356 fn parse_table_reference(reference: &str) -> Result<Self, ParsingError> {
1362 let bracket_pos = reference.find('[').ok_or_else(|| {
1363 ParsingError::InvalidReference(format!("Missing '[' in table reference: {reference}"))
1364 })?;
1365 let table_name = reference[..bracket_pos].trim();
1366 if table_name.is_empty() {
1367 return Err(ParsingError::InvalidReference(reference.to_string()));
1368 }
1369
1370 let specifier_str = &reference[bracket_pos..];
1371 let specifier = structured_ref::parse_full_specifier(specifier_str)?;
1372
1373 Ok(ReferenceType::Table(TableReference {
1374 name: table_name.to_string(),
1375 specifier,
1376 }))
1377 }
1378
1379 fn parse_bracketed_structured_reference(reference: &str) -> Result<Self, ParsingError> {
1386 debug_assert!(reference.starts_with('[') && reference.ends_with(']'));
1387 let specifier = structured_ref::parse_full_specifier(reference)?;
1388
1389 match specifier {
1390 Some(TableSpecifier::Column(name)) => Ok(ReferenceType::Table(TableReference {
1391 name,
1392 specifier: Some(TableSpecifier::SpecialItem(SpecialItem::Data)),
1393 })),
1394 other => Ok(ReferenceType::Table(TableReference {
1395 name: String::new(),
1396 specifier: other,
1397 })),
1398 }
1399 }
1400
1401 fn parse_openformula_reference(reference: &str) -> Result<Self, ParsingError> {
1402 if reference.starts_with('[') && reference.ends_with(']') {
1403 let inner = &reference[1..reference.len() - 1];
1404 if inner.is_empty() {
1405 return Err(ParsingError::InvalidReference(
1406 "Empty OpenFormula reference".to_string(),
1407 ));
1408 }
1409
1410 let mut parts = inner.splitn(2, ':');
1411 let start_part_str = parts.next().unwrap();
1412 let end_part_str = parts.next();
1413
1414 let start_part = Self::parse_openformula_part(start_part_str)?;
1415 let end_part = if let Some(part) = end_part_str {
1416 Some(Self::parse_openformula_part(part)?)
1417 } else {
1418 None
1419 };
1420
1421 let sheet = match (&start_part.sheet, &end_part) {
1422 (Some(sheet), Some(end)) => {
1423 if let Some(end_sheet) = &end.sheet {
1424 if end_sheet != sheet {
1425 return Err(ParsingError::InvalidReference(format!(
1426 "Mismatched sheets in reference: {sheet} vs {end_sheet}"
1427 )));
1428 }
1429 }
1430 Some(sheet.clone())
1431 }
1432 (Some(sheet), None) => Some(sheet.clone()),
1433 (None, Some(end)) => end.sheet.clone(),
1434 (None, None) => None,
1435 };
1436
1437 let mut excel_like = String::new();
1438 if let Some(sheet_name) = sheet {
1439 if sheet_name_needs_quoting(&sheet_name) {
1440 let escaped = sheet_name.replace('\'', "''");
1441 excel_like.push('\'');
1442 excel_like.push_str(&escaped);
1443 excel_like.push('\'');
1444 } else {
1445 excel_like.push_str(&sheet_name);
1446 }
1447 excel_like.push('!');
1448 }
1449
1450 excel_like.push_str(&start_part.coord);
1451 if let Some(end) = end_part {
1452 excel_like.push(':');
1453 excel_like.push_str(&end.coord);
1454 }
1455
1456 return Self::parse_excel_reference(&excel_like);
1457 }
1458
1459 Err(ParsingError::InvalidReference(format!(
1460 "Unsupported OpenFormula reference: {reference}"
1461 )))
1462 }
1463
1464 fn parse_openformula_part(part: &str) -> Result<OpenFormulaRefPart, ParsingError> {
1465 let trimmed = part.trim();
1466 if trimmed.is_empty() {
1467 return Err(ParsingError::InvalidReference(
1468 "Empty component in OpenFormula reference".to_string(),
1469 ));
1470 }
1471
1472 if trimmed == "." {
1473 return Err(ParsingError::InvalidReference(
1474 "Incomplete OpenFormula reference component".to_string(),
1475 ));
1476 }
1477
1478 if trimmed.starts_with('[') {
1479 return Err(ParsingError::InvalidReference(format!(
1481 "Unexpected '[' in OpenFormula reference component: {trimmed}"
1482 )));
1483 }
1484
1485 let (sheet, coord_slice) = if let Some(stripped) = trimmed.strip_prefix('.') {
1486 (None, stripped.trim())
1487 } else if let Some(dot_idx) = Self::find_openformula_sheet_separator(trimmed) {
1488 let sheet_part = trimmed[..dot_idx].trim();
1489 let coord_part = trimmed[dot_idx + 1..].trim();
1490 if coord_part.is_empty() {
1491 return Err(ParsingError::InvalidReference(format!(
1492 "Missing coordinate in OpenFormula reference component: {trimmed}"
1493 )));
1494 }
1495 let sheet_name = Self::normalise_openformula_sheet(sheet_part)?;
1496 (Some(sheet_name), coord_part)
1497 } else {
1498 (None, trimmed)
1499 };
1500
1501 let coord = coord_slice.trim_start_matches('.').trim().to_string();
1502
1503 if coord.is_empty() {
1504 return Err(ParsingError::InvalidReference(format!(
1505 "Missing coordinate in OpenFormula reference component: {trimmed}"
1506 )));
1507 }
1508
1509 Ok(OpenFormulaRefPart { sheet, coord })
1510 }
1511
1512 fn normalise_openformula_sheet(sheet: &str) -> Result<String, ParsingError> {
1513 let without_abs = sheet.trim().trim_start_matches('$');
1514
1515 if without_abs.starts_with('\'') {
1516 if without_abs.len() < 2 || !without_abs.ends_with('\'') {
1517 return Err(ParsingError::InvalidReference(format!(
1518 "Unterminated sheet name in OpenFormula reference: {sheet}"
1519 )));
1520 }
1521 let inner = &without_abs[1..without_abs.len() - 1];
1522 Ok(inner.replace("''", "'"))
1523 } else {
1524 Ok(without_abs.to_string())
1525 }
1526 }
1527
1528 fn find_openformula_sheet_separator(part: &str) -> Option<usize> {
1529 let bytes = part.as_bytes();
1530 let mut i = 0;
1531 let mut in_quotes = false;
1532
1533 while i < bytes.len() {
1534 match bytes[i] {
1535 b'\'' => {
1536 if i + 1 < bytes.len() && bytes[i + 1] == b'\'' {
1537 i += 2;
1538 continue;
1539 }
1540 in_quotes = !in_quotes;
1541 i += 1;
1542 }
1543 b'.' if !in_quotes => return Some(i),
1544 _ => i += 1,
1545 }
1546 }
1547
1548 None
1549 }
1550
1551 pub fn to_excel_string(&self) -> String {
1558 match self {
1559 ReferenceType::Cell {
1560 sheet,
1561 row,
1562 col,
1563 row_abs,
1564 col_abs,
1565 } => {
1566 let col_str = Self::format_col(*col, *col_abs);
1567 let row_str = Self::format_row(*row, *row_abs);
1568 if let Some(s) = sheet {
1569 if sheet_name_needs_quoting(s) {
1570 let escaped_name = s.replace('\'', "''");
1571 format!("'{}'!{}{}", escaped_name, col_str, row_str)
1572 } else {
1573 format!("{}!{}{}", s, col_str, row_str)
1574 }
1575 } else {
1576 format!("{}{}", col_str, row_str)
1577 }
1578 }
1579 ReferenceType::Range {
1580 sheet,
1581 start_row,
1582 start_col,
1583 end_row,
1584 end_col,
1585 start_row_abs,
1586 start_col_abs,
1587 end_row_abs,
1588 end_col_abs,
1589 } => {
1590 let start_ref = match (start_col, start_row) {
1592 (Some(col), Some(row)) => format!(
1593 "{}{}",
1594 Self::format_col(*col, *start_col_abs),
1595 Self::format_row(*row, *start_row_abs)
1596 ),
1597 (Some(col), None) => Self::format_col(*col, *start_col_abs),
1598 (None, Some(row)) => Self::format_row(*row, *start_row_abs),
1599 (None, None) => "".to_string(), };
1601
1602 let end_ref = match (end_col, end_row) {
1604 (Some(col), Some(row)) => format!(
1605 "{}{}",
1606 Self::format_col(*col, *end_col_abs),
1607 Self::format_row(*row, *end_row_abs)
1608 ),
1609 (Some(col), None) => Self::format_col(*col, *end_col_abs),
1610 (None, Some(row)) => Self::format_row(*row, *end_row_abs),
1611 (None, None) => "".to_string(), };
1613
1614 let range_part = format!("{start_ref}:{end_ref}");
1615
1616 if let Some(s) = sheet {
1617 if sheet_name_needs_quoting(s) {
1618 let escaped_name = s.replace('\'', "''");
1619 format!("'{escaped_name}'!{range_part}")
1620 } else {
1621 format!("{s}!{range_part}")
1622 }
1623 } else {
1624 range_part
1625 }
1626 }
1627 ReferenceType::Cell3D { .. } | ReferenceType::Range3D { .. } => format!("{self}"),
1628 ReferenceType::External(ext) => ext.raw.clone(),
1629 ReferenceType::Table(table_ref) => {
1630 if let Some(specifier) = &table_ref.specifier {
1631 format!("{}[{}]", table_ref.name, specifier)
1632 } else {
1633 table_ref.name.clone()
1634 }
1635 }
1636 ReferenceType::NamedRange(name) => name.clone(),
1637 }
1638 }
1639}
1640
1641#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1643#[derive(Debug, Clone, PartialEq, Hash)]
1644pub enum ASTNodeType {
1645 Literal(LiteralValue),
1646 Omitted,
1651 Reference {
1652 original: String, reference: ReferenceType, },
1655 UnaryOp {
1656 op: String,
1657 expr: Box<ASTNode>,
1658 },
1659 BinaryOp {
1660 op: String,
1661 left: Box<ASTNode>,
1662 right: Box<ASTNode>,
1663 },
1664 Function {
1665 name: String,
1666 args: Vec<ASTNode>, },
1668 Call {
1671 callee: Box<ASTNode>,
1672 args: Vec<ASTNode>,
1673 },
1674 Array(Vec<Vec<ASTNode>>), }
1676
1677impl Display for ASTNodeType {
1678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1679 match self {
1680 ASTNodeType::Literal(value) => write!(f, "Literal({value})"),
1681 ASTNodeType::Omitted => write!(f, "Omitted"),
1682 ASTNodeType::Reference { reference, .. } => write!(f, "Reference({reference:?})"),
1683 ASTNodeType::UnaryOp { op, expr } => write!(f, "UnaryOp({op}, {expr})"),
1684 ASTNodeType::BinaryOp { op, left, right } => {
1685 write!(f, "BinaryOp({op}, {left}, {right})")
1686 }
1687 ASTNodeType::Function { name, args } => write!(f, "Function({name}, {args:?})"),
1688 ASTNodeType::Call { callee, args } => write!(f, "Call({callee}, {args:?})"),
1689 ASTNodeType::Array(rows) => write!(f, "Array({rows:?})"),
1690 }
1691 }
1692}
1693
1694#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1696#[derive(Debug, Clone, PartialEq)]
1697pub struct ASTNode {
1698 pub node_type: ASTNodeType,
1699 pub source_token: Option<Token>,
1700 pub contains_volatile: bool,
1705}
1706
1707impl ASTNode {
1708 pub fn new(node_type: ASTNodeType, source_token: Option<Token>) -> Self {
1709 ASTNode {
1710 node_type,
1711 source_token,
1712 contains_volatile: false,
1713 }
1714 }
1715
1716 pub fn new_with_volatile(
1718 node_type: ASTNodeType,
1719 source_token: Option<Token>,
1720 contains_volatile: bool,
1721 ) -> Self {
1722 ASTNode {
1723 node_type,
1724 source_token,
1725 contains_volatile,
1726 }
1727 }
1728
1729 pub fn contains_volatile(&self) -> bool {
1731 self.contains_volatile
1732 }
1733
1734 pub fn fingerprint(&self) -> u64 {
1735 self.calculate_hash()
1736 }
1737
1738 pub fn calculate_hash(&self) -> u64 {
1740 let mut hasher = FormulaHasher::new();
1741 self.hash_node(&mut hasher);
1742 hasher.finish()
1743 }
1744
1745 fn hash_node(&self, hasher: &mut FormulaHasher) {
1746 match &self.node_type {
1747 ASTNodeType::Literal(value) => {
1748 hasher.write(&[1]); value.hash(hasher);
1750 }
1751 ASTNodeType::Omitted => hasher.write(&[8]),
1752 ASTNodeType::Reference { reference, .. } => {
1753 hasher.write(&[2]); reference.hash(hasher);
1755 }
1756 ASTNodeType::UnaryOp { op, expr } => {
1757 hasher.write(&[3]); hasher.write(op.as_bytes());
1759 expr.hash_node(hasher);
1760 }
1761 ASTNodeType::BinaryOp { op, left, right } => {
1762 hasher.write(&[4]); hasher.write(op.as_bytes());
1764 left.hash_node(hasher);
1765 right.hash_node(hasher);
1766 }
1767 ASTNodeType::Function { name, args } => {
1768 hasher.write(&[5]); let name_lower = name.to_lowercase();
1771 hasher.write(name_lower.as_bytes());
1772 hasher.write_usize(args.len());
1773 for arg in args {
1774 arg.hash_node(hasher);
1775 }
1776 }
1777 ASTNodeType::Call { callee, args } => {
1778 hasher.write(&[7]); callee.hash_node(hasher);
1780 hasher.write_usize(args.len());
1781 for arg in args {
1782 arg.hash_node(hasher);
1783 }
1784 }
1785 ASTNodeType::Array(rows) => {
1786 hasher.write(&[6]); hasher.write_usize(rows.len());
1788 for row in rows {
1789 hasher.write_usize(row.len());
1790 for item in row {
1791 item.hash_node(hasher);
1792 }
1793 }
1794 }
1795 }
1796 }
1797
1798 pub fn get_dependencies(&self) -> Vec<&ReferenceType> {
1799 let mut dependencies = Vec::new();
1800 self.collect_dependencies(&mut dependencies);
1801 dependencies
1802 }
1803
1804 pub fn get_dependency_strings(&self) -> Vec<String> {
1805 self.get_dependencies()
1806 .into_iter()
1807 .map(|dep| format!("{dep}"))
1808 .collect()
1809 }
1810
1811 fn collect_dependencies<'a>(&'a self, dependencies: &mut Vec<&'a ReferenceType>) {
1812 match &self.node_type {
1813 ASTNodeType::Reference { reference, .. } => {
1814 dependencies.push(reference);
1815 }
1816 ASTNodeType::UnaryOp { expr, .. } => {
1817 expr.collect_dependencies(dependencies);
1818 }
1819 ASTNodeType::BinaryOp { left, right, .. } => {
1820 left.collect_dependencies(dependencies);
1821 right.collect_dependencies(dependencies);
1822 }
1823 ASTNodeType::Function { args, .. } => {
1824 for arg in args {
1825 arg.collect_dependencies(dependencies);
1826 }
1827 }
1828 ASTNodeType::Call { callee, args } => {
1829 callee.collect_dependencies(dependencies);
1830 for arg in args {
1831 arg.collect_dependencies(dependencies);
1832 }
1833 }
1834 ASTNodeType::Array(rows) => {
1835 for row in rows {
1836 for item in row {
1837 item.collect_dependencies(dependencies);
1838 }
1839 }
1840 }
1841 _ => {}
1842 }
1843 }
1844
1845 pub fn refs(&self) -> RefIter<'_> {
1848 RefIter {
1849 stack: smallvec::smallvec![self],
1850 }
1851 }
1852
1853 pub fn visit_refs<V: FnMut(RefView<'_>)>(&self, mut visitor: V) {
1855 let mut stack: Vec<&ASTNode> = Vec::with_capacity(8);
1856 stack.push(self);
1857 while let Some(node) = stack.pop() {
1858 match &node.node_type {
1859 ASTNodeType::Reference { reference, .. } => visitor(RefView::from(reference)),
1860 ASTNodeType::UnaryOp { expr, .. } => stack.push(expr),
1861 ASTNodeType::BinaryOp { left, right, .. } => {
1862 stack.push(right);
1864 stack.push(left);
1865 }
1866 ASTNodeType::Function { args, .. } => {
1867 for a in args.iter().rev() {
1868 stack.push(a);
1869 }
1870 }
1871 ASTNodeType::Call { callee, args } => {
1872 for a in args.iter().rev() {
1873 stack.push(a);
1874 }
1875 stack.push(callee);
1876 }
1877 ASTNodeType::Array(rows) => {
1878 for r in rows.iter().rev() {
1879 for item in r.iter().rev() {
1880 stack.push(item);
1881 }
1882 }
1883 }
1884 ASTNodeType::Literal(_) | ASTNodeType::Omitted => {}
1885 }
1886 }
1887 }
1888
1889 pub fn collect_references(&self, policy: &CollectPolicy) -> SmallVec<[ReferenceType; 4]> {
1891 let mut out: SmallVec<[ReferenceType; 4]> = SmallVec::new();
1892 self.visit_refs(|rv| match rv {
1893 RefView::Cell {
1894 sheet,
1895 row,
1896 col,
1897 row_abs,
1898 col_abs,
1899 } => out.push(ReferenceType::Cell {
1900 sheet: sheet.map(|s| s.to_string()),
1901 row,
1902 col,
1903 row_abs,
1904 col_abs,
1905 }),
1906 RefView::Range {
1907 sheet,
1908 start_row,
1909 start_col,
1910 end_row,
1911 end_col,
1912 start_row_abs,
1913 start_col_abs,
1914 end_row_abs,
1915 end_col_abs,
1916 } => {
1917 if policy.expand_small_ranges {
1919 if let (Some(sr), Some(sc), Some(er), Some(ec)) =
1920 (start_row, start_col, end_row, end_col)
1921 {
1922 let rows = er.saturating_sub(sr) + 1;
1923 let cols = ec.saturating_sub(sc) + 1;
1924 let area = rows.saturating_mul(cols);
1925 if area as usize <= policy.range_expansion_limit {
1926 let row_abs = start_row_abs && end_row_abs;
1927 let col_abs = start_col_abs && end_col_abs;
1928 for r in sr..=er {
1929 for c in sc..=ec {
1930 out.push(ReferenceType::Cell {
1931 sheet: sheet.map(|s| s.to_string()),
1932 row: r,
1933 col: c,
1934 row_abs,
1935 col_abs,
1936 });
1937 }
1938 }
1939 return; }
1941 }
1942 }
1943 out.push(ReferenceType::Range {
1944 sheet: sheet.map(|s| s.to_string()),
1945 start_row,
1946 start_col,
1947 end_row,
1948 end_col,
1949 start_row_abs,
1950 start_col_abs,
1951 end_row_abs,
1952 end_col_abs,
1953 });
1954 }
1955 RefView::Cell3D {
1956 sheet_first,
1957 sheet_last,
1958 row,
1959 col,
1960 row_abs,
1961 col_abs,
1962 } => out.push(ReferenceType::Cell3D {
1963 sheet_first: sheet_first.to_string(),
1964 sheet_last: sheet_last.to_string(),
1965 row,
1966 col,
1967 row_abs,
1968 col_abs,
1969 }),
1970 RefView::Range3D {
1971 sheet_first,
1972 sheet_last,
1973 start_row,
1974 start_col,
1975 end_row,
1976 end_col,
1977 start_row_abs,
1978 start_col_abs,
1979 end_row_abs,
1980 end_col_abs,
1981 } => out.push(ReferenceType::Range3D {
1982 sheet_first: sheet_first.to_string(),
1983 sheet_last: sheet_last.to_string(),
1984 start_row,
1985 start_col,
1986 end_row,
1987 end_col,
1988 start_row_abs,
1989 start_col_abs,
1990 end_row_abs,
1991 end_col_abs,
1992 }),
1993 RefView::External {
1994 raw,
1995 book,
1996 sheet,
1997 kind,
1998 } => out.push(ReferenceType::External(ExternalReference {
1999 raw: raw.to_string(),
2000 book: ExternalBookRef::Token(book.to_string()),
2001 sheet: sheet.to_string(),
2002 kind,
2003 })),
2004 RefView::Table { name, specifier } => out.push(ReferenceType::Table(TableReference {
2005 name: name.to_string(),
2006 specifier: specifier.cloned(),
2007 })),
2008 RefView::NamedRange { name } => {
2009 if policy.include_names {
2010 out.push(ReferenceType::NamedRange(name.to_string()));
2011 }
2012 }
2013 });
2014 out
2015 }
2016 pub fn update_sheet_references(&mut self, target_name: Option<&str>, new_name: &str) {
2022 match &mut self.node_type {
2023 ASTNodeType::Reference {
2024 reference: ReferenceType::Cell { sheet, .. } | ReferenceType::Range { sheet, .. },
2025 ..
2026 } => {
2027 if let Some(current_sheet) = sheet
2028 && (target_name.is_none() || target_name == Some(current_sheet.as_str()))
2029 {
2030 *sheet = Some(new_name.to_string());
2031 }
2032 }
2033 ASTNodeType::Reference {
2034 reference:
2035 ReferenceType::Cell3D {
2036 sheet_first,
2037 sheet_last,
2038 ..
2039 }
2040 | ReferenceType::Range3D {
2041 sheet_first,
2042 sheet_last,
2043 ..
2044 },
2045 ..
2046 } => {
2047 if target_name.is_none() || target_name == Some(sheet_first.as_str()) {
2048 *sheet_first = new_name.to_string();
2049 }
2050 if target_name.is_none() || target_name == Some(sheet_last.as_str()) {
2051 *sheet_last = new_name.to_string();
2052 }
2053 }
2054 ASTNodeType::UnaryOp { expr, .. } => {
2055 expr.update_sheet_references(target_name, new_name);
2056 }
2057 ASTNodeType::BinaryOp { left, right, .. } => {
2058 left.update_sheet_references(target_name, new_name);
2059 right.update_sheet_references(target_name, new_name);
2060 }
2061 ASTNodeType::Function { args, .. } => {
2062 for arg in args {
2063 arg.update_sheet_references(target_name, new_name);
2064 }
2065 }
2066 ASTNodeType::Call { callee, args } => {
2067 callee.update_sheet_references(target_name, new_name);
2068 for arg in args {
2069 arg.update_sheet_references(target_name, new_name);
2070 }
2071 }
2072 ASTNodeType::Array(rows) => {
2073 for row in rows {
2074 for cell in row {
2075 cell.update_sheet_references(target_name, new_name);
2076 }
2077 }
2078 }
2079 _ => {}
2080 }
2081 }
2082}
2083
2084#[derive(Clone, Copy, Debug)]
2086pub enum RefView<'a> {
2087 Cell {
2088 sheet: Option<&'a str>,
2089 row: u32,
2090 col: u32,
2091 row_abs: bool,
2092 col_abs: bool,
2093 },
2094 Range {
2095 sheet: Option<&'a str>,
2096 start_row: Option<u32>,
2097 start_col: Option<u32>,
2098 end_row: Option<u32>,
2099 end_col: Option<u32>,
2100 start_row_abs: bool,
2101 start_col_abs: bool,
2102 end_row_abs: bool,
2103 end_col_abs: bool,
2104 },
2105 Cell3D {
2107 sheet_first: &'a str,
2108 sheet_last: &'a str,
2109 row: u32,
2110 col: u32,
2111 row_abs: bool,
2112 col_abs: bool,
2113 },
2114 Range3D {
2116 sheet_first: &'a str,
2117 sheet_last: &'a str,
2118 start_row: Option<u32>,
2119 start_col: Option<u32>,
2120 end_row: Option<u32>,
2121 end_col: Option<u32>,
2122 start_row_abs: bool,
2123 start_col_abs: bool,
2124 end_row_abs: bool,
2125 end_col_abs: bool,
2126 },
2127 External {
2128 raw: &'a str,
2129 book: &'a str,
2130 sheet: &'a str,
2131 kind: ExternalRefKind,
2132 },
2133 Table {
2134 name: &'a str,
2135 specifier: Option<&'a TableSpecifier>,
2136 },
2137 NamedRange {
2138 name: &'a str,
2139 },
2140}
2141
2142impl<'a> From<&'a ReferenceType> for RefView<'a> {
2143 fn from(r: &'a ReferenceType) -> Self {
2144 match r {
2145 ReferenceType::Cell {
2146 sheet,
2147 row,
2148 col,
2149 row_abs,
2150 col_abs,
2151 } => RefView::Cell {
2152 sheet: sheet.as_deref(),
2153 row: *row,
2154 col: *col,
2155 row_abs: *row_abs,
2156 col_abs: *col_abs,
2157 },
2158 ReferenceType::Range {
2159 sheet,
2160 start_row,
2161 start_col,
2162 end_row,
2163 end_col,
2164 start_row_abs,
2165 start_col_abs,
2166 end_row_abs,
2167 end_col_abs,
2168 } => RefView::Range {
2169 sheet: sheet.as_deref(),
2170 start_row: *start_row,
2171 start_col: *start_col,
2172 end_row: *end_row,
2173 end_col: *end_col,
2174 start_row_abs: *start_row_abs,
2175 start_col_abs: *start_col_abs,
2176 end_row_abs: *end_row_abs,
2177 end_col_abs: *end_col_abs,
2178 },
2179 ReferenceType::Cell3D {
2180 sheet_first,
2181 sheet_last,
2182 row,
2183 col,
2184 row_abs,
2185 col_abs,
2186 } => RefView::Cell3D {
2187 sheet_first: sheet_first.as_str(),
2188 sheet_last: sheet_last.as_str(),
2189 row: *row,
2190 col: *col,
2191 row_abs: *row_abs,
2192 col_abs: *col_abs,
2193 },
2194 ReferenceType::Range3D {
2195 sheet_first,
2196 sheet_last,
2197 start_row,
2198 start_col,
2199 end_row,
2200 end_col,
2201 start_row_abs,
2202 start_col_abs,
2203 end_row_abs,
2204 end_col_abs,
2205 } => RefView::Range3D {
2206 sheet_first: sheet_first.as_str(),
2207 sheet_last: sheet_last.as_str(),
2208 start_row: *start_row,
2209 start_col: *start_col,
2210 end_row: *end_row,
2211 end_col: *end_col,
2212 start_row_abs: *start_row_abs,
2213 start_col_abs: *start_col_abs,
2214 end_row_abs: *end_row_abs,
2215 end_col_abs: *end_col_abs,
2216 },
2217 ReferenceType::External(ext) => RefView::External {
2218 raw: ext.raw.as_str(),
2219 book: ext.book.token(),
2220 sheet: ext.sheet.as_str(),
2221 kind: ext.kind,
2222 },
2223 ReferenceType::Table(tr) => RefView::Table {
2224 name: tr.name.as_str(),
2225 specifier: tr.specifier.as_ref(),
2226 },
2227 ReferenceType::NamedRange(name) => RefView::NamedRange { name },
2228 }
2229 }
2230}
2231
2232pub struct RefIter<'a> {
2234 stack: smallvec::SmallVec<[&'a ASTNode; 8]>,
2235}
2236
2237impl<'a> Iterator for RefIter<'a> {
2238 type Item = RefView<'a>;
2239 fn next(&mut self) -> Option<Self::Item> {
2240 while let Some(node) = self.stack.pop() {
2241 match &node.node_type {
2242 ASTNodeType::Reference { reference, .. } => return Some(RefView::from(reference)),
2243 ASTNodeType::UnaryOp { expr, .. } => self.stack.push(expr),
2244 ASTNodeType::BinaryOp { left, right, .. } => {
2245 self.stack.push(right);
2246 self.stack.push(left);
2247 }
2248 ASTNodeType::Function { args, .. } => {
2249 for a in args.iter().rev() {
2250 self.stack.push(a);
2251 }
2252 }
2253 ASTNodeType::Call { callee, args } => {
2254 for a in args.iter().rev() {
2255 self.stack.push(a);
2256 }
2257 self.stack.push(callee);
2258 }
2259 ASTNodeType::Array(rows) => {
2260 for r in rows.iter().rev() {
2261 for item in r.iter().rev() {
2262 self.stack.push(item);
2263 }
2264 }
2265 }
2266 ASTNodeType::Literal(_) | ASTNodeType::Omitted => {}
2267 }
2268 }
2269 None
2270 }
2271}
2272
2273#[derive(Debug, Clone)]
2275pub struct CollectPolicy {
2276 pub expand_small_ranges: bool,
2277 pub range_expansion_limit: usize,
2278 pub include_names: bool,
2279}
2280
2281impl Default for CollectPolicy {
2282 fn default() -> Self {
2283 Self {
2284 expand_small_ranges: false,
2285 range_expansion_limit: 0,
2286 include_names: true,
2287 }
2288 }
2289}
2290
2291impl Display for ASTNode {
2292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2293 write!(f, "{}", self.node_type)
2294 }
2295}
2296
2297impl std::hash::Hash for ASTNode {
2298 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2299 let hash = self.calculate_hash();
2300 state.write_u64(hash);
2301 }
2302}
2303
2304impl From<TokenizerError> for ParserError {
2305 fn from(err: TokenizerError) -> Self {
2306 ParserError {
2307 message: err.message,
2308 position: Some(err.pos),
2309 }
2310 }
2311}
2312
2313pub struct Parser {
2319 source: Arc<str>,
2320 tokens: Arc<[TokenSpan]>,
2321 position: usize,
2322 volatility_classifier: Option<VolatilityClassifierBox>,
2323 dialect: FormulaDialect,
2324 in_call_args_depth: usize,
2327}
2328
2329impl Parser {
2330 pub fn new<T: AsRef<str>>(formula: T) -> Result<Self, TokenizerError> {
2332 Self::new_with_dialect(formula, FormulaDialect::Excel)
2333 }
2334
2335 pub fn try_from_formula(formula: &str) -> Result<Self, TokenizerError> {
2337 Self::new(formula)
2338 }
2339
2340 pub fn new_with_dialect<T: AsRef<str>>(
2342 formula: T,
2343 dialect: FormulaDialect,
2344 ) -> Result<Self, TokenizerError> {
2345 let source: Arc<str> = Arc::from(formula.as_ref());
2346 let spans = crate::tokenizer::tokenize_spans_with_dialect(source.as_ref(), dialect)?;
2347 Ok(Self::from_source_and_tokens(
2348 source,
2349 Arc::from(spans.into_boxed_slice()),
2350 dialect,
2351 ))
2352 }
2353
2354 pub fn from_token_stream(stream: &TokenStream) -> Self {
2356 Self::from_source_and_tokens(
2357 Arc::from(stream.source()),
2358 Arc::from(stream.spans.clone().into_boxed_slice()),
2359 stream.dialect(),
2360 )
2361 }
2362
2363 fn from_source_and_tokens(
2364 source: Arc<str>,
2365 tokens: Arc<[TokenSpan]>,
2366 dialect: FormulaDialect,
2367 ) -> Self {
2368 Parser {
2369 source,
2370 tokens,
2371 position: 0,
2372 volatility_classifier: None,
2373 dialect,
2374 in_call_args_depth: 0,
2375 }
2376 }
2377
2378 pub fn with_volatility_classifier<F>(mut self, f: F) -> Self
2381 where
2382 F: Fn(&str) -> bool + Send + Sync + 'static,
2383 {
2384 self.volatility_classifier = Some(Box::new(f));
2385 self
2386 }
2387
2388 fn skip_whitespace(&mut self) {
2389 while self.position < self.tokens.len()
2390 && self.tokens[self.position].token_type == TokenType::Whitespace
2391 {
2392 self.position += 1;
2393 }
2394 }
2395
2396 fn span_value(&self, span: &TokenSpan) -> &str {
2397 &self.source[span.start..span.end]
2398 }
2399
2400 fn semantic_span_value(&self, span: &TokenSpan) -> &str {
2401 let value = self.span_value(span);
2402 if span.token_type == TokenType::OpInfix
2403 && value.as_bytes().contains(&b' ')
2404 && value
2405 .as_bytes()
2406 .iter()
2407 .all(|byte| matches!(byte, b' ' | b'\t' | b'\r' | b'\n'))
2408 {
2409 " "
2410 } else {
2411 value
2412 }
2413 }
2414
2415 fn span_to_token(&self, span: &TokenSpan) -> Token {
2416 Token::new_with_span(
2417 self.semantic_span_value(span).to_string(),
2418 span.token_type,
2419 span.subtype,
2420 span.start,
2421 span.end,
2422 )
2423 }
2424
2425 fn span_precedence(&self, span: &TokenSpan) -> Option<(u8, Associativity)> {
2426 if !matches!(
2427 span.token_type,
2428 TokenType::OpPrefix | TokenType::OpInfix | TokenType::OpPostfix
2429 ) {
2430 return None;
2431 }
2432
2433 let op = if span.token_type == TokenType::OpPrefix {
2434 "u"
2435 } else {
2436 self.semantic_span_value(span)
2437 };
2438
2439 match op {
2440 "#" => Some((11, Associativity::Left)),
2441 ":" => Some((10, Associativity::Left)),
2442 " " => Some((9, Associativity::Left)),
2443 "," => Some((8, Associativity::Left)),
2444 "%" => Some((7, Associativity::Left)),
2445 "u" => Some((6, Associativity::Right)),
2446 "^" => Some((5, Associativity::Right)),
2447 "*" | "/" => Some((4, Associativity::Left)),
2448 "+" | "-" => Some((3, Associativity::Left)),
2449 "&" => Some((2, Associativity::Left)),
2450 "=" | "<" | ">" | "<=" | ">=" | "<>" => Some((1, Associativity::Left)),
2451 _ => None,
2452 }
2453 }
2454
2455 pub fn parse(&mut self) -> Result<ASTNode, ParserError> {
2456 if self.tokens.is_empty() {
2457 return Err(ParserError {
2458 message: "No tokens to parse".to_string(),
2459 position: None,
2460 });
2461 }
2462
2463 self.skip_whitespace();
2464 if self.position >= self.tokens.len() {
2465 return Err(ParserError {
2466 message: "No tokens to parse".to_string(),
2467 position: None,
2468 });
2469 }
2470
2471 if self.tokens[self.position].token_type == TokenType::Literal {
2472 let span = self.tokens[self.position];
2473 self.position += 1;
2474 self.skip_whitespace();
2475 if self.position < self.tokens.len() {
2476 return Err(ParserError {
2477 message: format!(
2478 "Unexpected token at position {}: {:?}",
2479 self.position, self.tokens[self.position]
2480 ),
2481 position: Some(self.position),
2482 });
2483 }
2484
2485 let token = self.span_to_token(&span);
2486 return Ok(ASTNode::new(
2487 ASTNodeType::Literal(LiteralValue::Text(token.value.clone())),
2488 Some(token),
2489 ));
2490 }
2491
2492 let ast = self.parse_expression()?;
2493 self.skip_whitespace();
2494 if self.position < self.tokens.len() {
2495 return Err(ParserError {
2496 message: format!(
2497 "Unexpected token at position {}: {:?}",
2498 self.position, self.tokens[self.position]
2499 ),
2500 position: Some(self.position),
2501 });
2502 }
2503 Ok(ast)
2504 }
2505
2506 fn parse_expression(&mut self) -> Result<ASTNode, ParserError> {
2507 self.parse_bp(0)
2508 }
2509
2510 fn parse_bp(&mut self, min_precedence: u8) -> Result<ASTNode, ParserError> {
2511 let mut left = self.parse_prefix()?;
2512
2513 loop {
2514 self.skip_whitespace();
2515 if self.position >= self.tokens.len() {
2516 break;
2517 }
2518
2519 if self.tokens[self.position].token_type == TokenType::Paren
2522 && self.tokens[self.position].subtype == TokenSubType::Open
2523 {
2524 self.position += 1;
2525 let args = self.parse_call_arguments()?;
2526 let call_volatile =
2527 left.contains_volatile || args.iter().any(|a| a.contains_volatile);
2528 left = ASTNode::new_with_volatile(
2529 ASTNodeType::Call {
2530 callee: Box::new(left),
2531 args,
2532 },
2533 None,
2534 call_volatile,
2535 );
2536 continue;
2537 }
2538
2539 if self.tokens[self.position].token_type == TokenType::OpPostfix {
2540 let (precedence, _) = self
2541 .span_precedence(&self.tokens[self.position])
2542 .unwrap_or((0, Associativity::Left));
2543 if precedence < min_precedence {
2544 break;
2545 }
2546
2547 let op_span = self.tokens[self.position];
2548 self.position += 1;
2549 let op_token = self.span_to_token(&op_span);
2550 let contains_volatile = left.contains_volatile;
2551 left = ASTNode::new_with_volatile(
2552 ASTNodeType::UnaryOp {
2553 op: op_token.value.clone(),
2554 expr: Box::new(left),
2555 },
2556 Some(op_token),
2557 contains_volatile,
2558 );
2559 continue;
2560 }
2561
2562 let token = &self.tokens[self.position];
2563 if token.token_type != TokenType::OpInfix {
2564 break;
2565 }
2566
2567 if self.in_call_args_depth > 0 && self.span_value(token) == "," {
2570 break;
2571 }
2572
2573 let (precedence, associativity) = self
2574 .span_precedence(token)
2575 .unwrap_or((0, Associativity::Left));
2576 if precedence < min_precedence {
2577 break;
2578 }
2579
2580 let op_span = self.tokens[self.position];
2581 self.position += 1;
2582
2583 let next_min_precedence = if associativity == Associativity::Left {
2584 precedence + 1
2585 } else {
2586 precedence
2587 };
2588
2589 let right = self.parse_bp(next_min_precedence)?;
2590 let op_token = self.span_to_token(&op_span);
2591 let contains_volatile = left.contains_volatile || right.contains_volatile;
2592 left = ASTNode::new_with_volatile(
2593 ASTNodeType::BinaryOp {
2594 op: op_token.value.clone(),
2595 left: Box::new(left),
2596 right: Box::new(right),
2597 },
2598 Some(op_token),
2599 contains_volatile,
2600 );
2601 }
2602
2603 Ok(left)
2604 }
2605
2606 fn parse_prefix(&mut self) -> Result<ASTNode, ParserError> {
2607 self.skip_whitespace();
2608 if self.position < self.tokens.len()
2609 && self.tokens[self.position].token_type == TokenType::OpPrefix
2610 {
2611 let op_span = self.tokens[self.position];
2612 self.position += 1;
2613
2614 let (precedence, _) = self
2615 .span_precedence(&op_span)
2616 .unwrap_or((0, Associativity::Right));
2617
2618 let expr = self.parse_bp(precedence)?;
2619 let op_token = self.span_to_token(&op_span);
2620 let contains_volatile = expr.contains_volatile;
2621 return Ok(ASTNode::new_with_volatile(
2622 ASTNodeType::UnaryOp {
2623 op: op_token.value.clone(),
2624 expr: Box::new(expr),
2625 },
2626 Some(op_token),
2627 contains_volatile,
2628 ));
2629 }
2630
2631 self.parse_primary()
2632 }
2633
2634 fn parse_primary(&mut self) -> Result<ASTNode, ParserError> {
2635 self.skip_whitespace();
2636 if self.position >= self.tokens.len() {
2637 return Err(ParserError {
2638 message: "Unexpected end of tokens".to_string(),
2639 position: Some(self.position),
2640 });
2641 }
2642
2643 let token = &self.tokens[self.position];
2644 match token.token_type {
2645 TokenType::Operand => {
2646 let span = self.tokens[self.position];
2647 self.position += 1;
2648 self.parse_operand(span)
2649 }
2650 TokenType::Func => {
2651 let span = self.tokens[self.position];
2652 self.position += 1;
2653 self.parse_function(span)
2654 }
2655 TokenType::Paren if token.subtype == TokenSubType::Open => {
2656 self.position += 1;
2657 let expr = self.parse_expression()?;
2658 self.skip_whitespace();
2659 if self.position >= self.tokens.len()
2660 || self.tokens[self.position].token_type != TokenType::Paren
2661 || self.tokens[self.position].subtype != TokenSubType::Close
2662 {
2663 return Err(ParserError {
2664 message: "Expected closing parenthesis".to_string(),
2665 position: Some(self.position),
2666 });
2667 }
2668 self.position += 1;
2669 Ok(expr)
2670 }
2671 TokenType::Array if token.subtype == TokenSubType::Open => {
2672 self.position += 1;
2673 self.parse_array()
2674 }
2675 _ => Err(ParserError {
2676 message: format!("Unexpected token: {token:?}"),
2677 position: Some(self.position),
2678 }),
2679 }
2680 }
2681
2682 fn parse_operand(&mut self, span: TokenSpan) -> Result<ASTNode, ParserError> {
2683 let value = self.span_value(&span);
2684 let token = self.span_to_token(&span);
2685
2686 match span.subtype {
2687 TokenSubType::Number => {
2688 let value = value.parse::<f64>().map_err(|_| ParserError {
2689 message: format!("Invalid number: {value}"),
2690 position: Some(self.position),
2691 })?;
2692 Ok(ASTNode::new(
2693 ASTNodeType::Literal(LiteralValue::Number(value)),
2694 Some(token),
2695 ))
2696 }
2697 TokenSubType::Text => {
2698 let mut text = value.to_string();
2699 if text.starts_with('"') && text.ends_with('"') && text.len() >= 2 {
2700 text = text[1..text.len() - 1].to_string();
2701 text = text.replace("\"\"", "\"");
2702 }
2703 Ok(ASTNode::new(
2704 ASTNodeType::Literal(LiteralValue::Text(text)),
2705 Some(token),
2706 ))
2707 }
2708 TokenSubType::Logical => {
2709 let v = value.eq_ignore_ascii_case("TRUE");
2710 Ok(ASTNode::new(
2711 ASTNodeType::Literal(LiteralValue::Boolean(v)),
2712 Some(token),
2713 ))
2714 }
2715 TokenSubType::Error => {
2716 let error = ExcelError::from_error_string(value);
2717 Ok(ASTNode::new(
2718 ASTNodeType::Literal(LiteralValue::Error(error)),
2719 Some(token),
2720 ))
2721 }
2722 TokenSubType::Range => {
2723 let reference = ReferenceType::from_string_with_dialect(value, self.dialect)
2724 .map_err(|e| ParserError {
2725 message: format!("Invalid reference '{value}': {e}"),
2726 position: Some(self.position),
2727 })?;
2728 Ok(ASTNode::new(
2729 ASTNodeType::Reference {
2730 original: value.to_string(),
2731 reference,
2732 },
2733 Some(token),
2734 ))
2735 }
2736 _ => Err(ParserError {
2737 message: format!("Unexpected operand subtype: {:?}", span.subtype),
2738 position: Some(self.position),
2739 }),
2740 }
2741 }
2742
2743 fn parse_function(&mut self, func_span: TokenSpan) -> Result<ASTNode, ParserError> {
2744 let func_value = self.span_value(&func_span);
2745 if func_value.is_empty() {
2746 return Err(ParserError {
2747 message: "Invalid function token".to_string(),
2748 position: Some(self.position),
2749 });
2750 }
2751 let name = func_value[..func_value.len() - 1].to_string();
2752 let args = self.parse_function_arguments()?;
2753
2754 let this_is_volatile = self
2755 .volatility_classifier
2756 .as_ref()
2757 .map(|f| f(name.as_str()))
2758 .unwrap_or(false);
2759 let args_volatile = args.iter().any(|a| a.contains_volatile);
2760
2761 let func_token = self.span_to_token(&func_span);
2762 Ok(ASTNode::new_with_volatile(
2763 ASTNodeType::Function { name, args },
2764 Some(func_token),
2765 this_is_volatile || args_volatile,
2766 ))
2767 }
2768
2769 fn parse_call_arguments(&mut self) -> Result<Vec<ASTNode>, ParserError> {
2774 let mut args: Vec<ASTNode> = Vec::new();
2775
2776 self.skip_whitespace();
2777 if self.position < self.tokens.len()
2778 && self.tokens[self.position].token_type == TokenType::Paren
2779 && self.tokens[self.position].subtype == TokenSubType::Close
2780 {
2781 self.position += 1;
2782 return Ok(args);
2783 }
2784
2785 self.in_call_args_depth += 1;
2786 let result = (|| -> Result<Vec<ASTNode>, ParserError> {
2787 let mut expecting_argument = true;
2788 let mut saw_argument = false;
2789 loop {
2790 self.skip_whitespace();
2791 if self.position >= self.tokens.len() {
2792 return Err(ParserError {
2793 message: "Unterminated call argument list".to_string(),
2794 position: Some(self.position),
2795 });
2796 }
2797
2798 let token = &self.tokens[self.position];
2799 let is_separator = (token.token_type == TokenType::Sep
2800 && token.subtype == TokenSubType::Arg)
2801 || (token.token_type == TokenType::OpInfix && self.span_value(token) == ",");
2802 let is_close =
2803 token.token_type == TokenType::Paren && token.subtype == TokenSubType::Close;
2804
2805 if expecting_argument {
2806 if is_close {
2807 if saw_argument {
2808 args.push(ASTNode::new(ASTNodeType::Omitted, None));
2809 }
2810 self.position += 1;
2811 return Ok(std::mem::take(&mut args));
2812 }
2813 if is_separator {
2814 args.push(ASTNode::new(ASTNodeType::Omitted, None));
2815 saw_argument = true;
2816 self.position += 1;
2817 } else {
2818 args.push(self.parse_expression()?);
2819 saw_argument = true;
2820 expecting_argument = false;
2821 }
2822 } else if is_separator {
2823 self.position += 1;
2824 expecting_argument = true;
2825 } else if is_close {
2826 self.position += 1;
2827 return Ok(std::mem::take(&mut args));
2828 } else {
2829 return Err(ParserError {
2830 message: format!("Expected ',' or ')' in call arguments, got {token:?}"),
2831 position: Some(self.position),
2832 });
2833 }
2834 }
2835 })();
2836 self.in_call_args_depth -= 1;
2837 result
2838 }
2839
2840 fn parse_function_arguments(&mut self) -> Result<Vec<ASTNode>, ParserError> {
2841 let mut args = Vec::new();
2842
2843 self.skip_whitespace();
2844 if self.position < self.tokens.len()
2845 && self.tokens[self.position].token_type == TokenType::Func
2846 && self.tokens[self.position].subtype == TokenSubType::Close
2847 {
2848 self.position += 1;
2849 return Ok(args);
2850 }
2851
2852 let mut expecting_argument = true;
2853 let mut saw_argument = false;
2854 loop {
2855 self.skip_whitespace();
2856 if self.position >= self.tokens.len() {
2857 return Err(ParserError {
2858 message: "Unterminated function argument list".to_string(),
2859 position: Some(self.position),
2860 });
2861 }
2862
2863 let token = &self.tokens[self.position];
2864 let is_separator =
2865 token.token_type == TokenType::Sep && token.subtype == TokenSubType::Arg;
2866 let is_close =
2867 token.token_type == TokenType::Func && token.subtype == TokenSubType::Close;
2868
2869 if expecting_argument {
2870 if is_close {
2871 if saw_argument {
2872 args.push(ASTNode::new(ASTNodeType::Omitted, None));
2873 }
2874 self.position += 1;
2875 return Ok(args);
2876 }
2877 if is_separator {
2878 args.push(ASTNode::new(ASTNodeType::Omitted, None));
2879 saw_argument = true;
2880 self.position += 1;
2881 } else {
2882 args.push(self.parse_expression()?);
2883 saw_argument = true;
2884 expecting_argument = false;
2885 }
2886 } else if is_separator {
2887 self.position += 1;
2888 expecting_argument = true;
2889 } else if is_close {
2890 self.position += 1;
2891 return Ok(args);
2892 } else {
2893 return Err(ParserError {
2894 message: format!("Expected ',' or ')' in function arguments, got {token:?}"),
2895 position: Some(self.position),
2896 });
2897 }
2898 }
2899 }
2900
2901 fn parse_array(&mut self) -> Result<ASTNode, ParserError> {
2902 let mut rows = Vec::new();
2903 let mut current_row = Vec::new();
2904
2905 self.skip_whitespace();
2906 if self.position < self.tokens.len()
2907 && self.tokens[self.position].token_type == TokenType::Array
2908 && self.tokens[self.position].subtype == TokenSubType::Close
2909 {
2910 self.position += 1;
2911 return Ok(ASTNode::new(ASTNodeType::Array(rows), None));
2912 }
2913
2914 current_row.push(self.parse_expression()?);
2915
2916 while self.position < self.tokens.len() {
2917 self.skip_whitespace();
2918 if self.position >= self.tokens.len() {
2919 break;
2920 }
2921 let token = &self.tokens[self.position];
2922
2923 if token.token_type == TokenType::Sep {
2924 if token.subtype == TokenSubType::Arg {
2925 self.position += 1;
2926 current_row.push(self.parse_expression()?);
2927 } else if token.subtype == TokenSubType::Row {
2928 self.position += 1;
2929 rows.push(current_row);
2930 current_row = vec![self.parse_expression()?];
2931 }
2932 } else if token.token_type == TokenType::Array && token.subtype == TokenSubType::Close {
2933 self.position += 1;
2934 rows.push(current_row);
2935 break;
2936 } else {
2937 return Err(ParserError {
2938 message: format!("Unexpected token in array: {token:?}"),
2939 position: Some(self.position),
2940 });
2941 }
2942 }
2943
2944 let contains_volatile = rows
2945 .iter()
2946 .flat_map(|r| r.iter())
2947 .any(|n| n.contains_volatile);
2948
2949 Ok(ASTNode::new_with_volatile(
2950 ASTNodeType::Array(rows),
2951 None,
2952 contains_volatile,
2953 ))
2954 }
2955}
2956
2957impl TryFrom<&str> for Parser {
2958 type Error = TokenizerError;
2959
2960 fn try_from(formula: &str) -> Result<Self, Self::Error> {
2961 Self::new(formula)
2962 }
2963}
2964
2965impl TryFrom<String> for Parser {
2966 type Error = TokenizerError;
2967
2968 fn try_from(formula: String) -> Result<Self, Self::Error> {
2969 Self::new(formula)
2970 }
2971}
2972
2973impl From<&TokenStream> for Parser {
2974 fn from(stream: &TokenStream) -> Self {
2975 Self::from_token_stream(stream)
2976 }
2977}
2978
2979impl FromStr for ASTNode {
2980 type Err = ParserError;
2981
2982 fn from_str(formula: &str) -> Result<Self, Self::Err> {
2983 parse(formula)
2984 }
2985}
2986
2987impl TryFrom<&str> for ASTNode {
2988 type Error = ParserError;
2989
2990 fn try_from(formula: &str) -> Result<Self, Self::Error> {
2991 parse(formula)
2992 }
2993}
2994
2995impl TryFrom<String> for ASTNode {
2996 type Error = ParserError;
2997
2998 fn try_from(formula: String) -> Result<Self, Self::Error> {
2999 parse(formula)
3000 }
3001}
3002
3003impl Parser {
3004 pub fn builder() -> ParserBuilder {
3005 ParserBuilder::default()
3006 }
3007}
3008
3009#[derive(Default)]
3010pub struct ParserBuilder {
3011 dialect: FormulaDialect,
3012 volatility_classifier: Option<VolatilityClassifierArc>,
3013}
3014
3015impl ParserBuilder {
3016 pub fn dialect(mut self, dialect: FormulaDialect) -> Self {
3017 self.dialect = dialect;
3018 self
3019 }
3020
3021 pub fn with_volatility_classifier<F>(mut self, f: F) -> Self
3022 where
3023 F: Fn(&str) -> bool + Send + Sync + 'static,
3024 {
3025 self.volatility_classifier = Some(Arc::new(f));
3026 self
3027 }
3028
3029 pub fn build<T: AsRef<str>>(self, formula: T) -> Result<Parser, TokenizerError> {
3030 let mut parser = Parser::new_with_dialect(formula, self.dialect)?;
3031 if let Some(classifier) = self.volatility_classifier {
3032 parser = parser.with_volatility_classifier(move |name| classifier(name));
3033 }
3034 Ok(parser)
3035 }
3036
3037 pub fn parse<T: AsRef<str>>(self, formula: T) -> Result<ASTNode, ParserError> {
3038 let mut parser = self.build(formula)?;
3039 parser.parse()
3040 }
3041}
3042
3043pub fn normalise_reference(reference: &str) -> Result<String, ParsingError> {
3045 let ref_type = ReferenceType::from_string(reference)?;
3046 Ok(ref_type.to_string())
3047}
3048
3049pub fn parse<T: AsRef<str>>(formula: T) -> Result<ASTNode, ParserError> {
3050 parse_with_dialect(formula, FormulaDialect::Excel)
3051}
3052
3053pub fn parse_with_dialect<T: AsRef<str>>(
3054 formula: T,
3055 dialect: FormulaDialect,
3056) -> Result<ASTNode, ParserError> {
3057 let mut parser = Parser::new_with_dialect(formula, dialect)?;
3058 parser.parse()
3059}
3060
3061pub fn parse_with_volatility_classifier<T, F>(
3064 formula: T,
3065 classifier: F,
3066) -> Result<ASTNode, ParserError>
3067where
3068 T: AsRef<str>,
3069 F: Fn(&str) -> bool + Send + Sync + 'static,
3070{
3071 parse_with_dialect_and_volatility_classifier(formula, FormulaDialect::Excel, classifier)
3072}
3073
3074pub fn parse_with_dialect_and_volatility_classifier<T, F>(
3075 formula: T,
3076 dialect: FormulaDialect,
3077 classifier: F,
3078) -> Result<ASTNode, ParserError>
3079where
3080 T: AsRef<str>,
3081 F: Fn(&str) -> bool + Send + Sync + 'static,
3082{
3083 let mut parser =
3084 Parser::new_with_dialect(formula, dialect)?.with_volatility_classifier(classifier);
3085 parser.parse()
3086}
3087
3088pub struct BatchParser {
3093 include_whitespace: bool,
3094 volatility_classifier: Option<VolatilityClassifierArc>,
3095 token_cache: std::collections::HashMap<String, (Arc<str>, Arc<[TokenSpan]>)>,
3096 dialect: FormulaDialect,
3097}
3098
3099impl BatchParser {
3100 pub fn builder() -> BatchParserBuilder {
3101 BatchParserBuilder::default()
3102 }
3103
3104 pub fn parse(&mut self, formula: &str) -> Result<ASTNode, ParserError> {
3106 let (source, spans) = if let Some((source, tokens)) = self.token_cache.get(formula) {
3107 (Arc::clone(source), Arc::clone(tokens))
3108 } else {
3109 let source: Arc<str> = Arc::from(formula);
3110 let mut spans =
3111 crate::tokenizer::tokenize_spans_with_dialect(source.as_ref(), self.dialect)?;
3112 if !self.include_whitespace {
3113 spans.retain(|t| t.token_type != TokenType::Whitespace);
3114 }
3115
3116 let spans: Arc<[TokenSpan]> = Arc::from(spans.into_boxed_slice());
3117 self.token_cache.insert(
3118 formula.to_string(),
3119 (Arc::clone(&source), Arc::clone(&spans)),
3120 );
3121 (source, spans)
3122 };
3123
3124 let mut parser = Parser::from_source_and_tokens(source, spans, self.dialect);
3125 if let Some(classifier) = self.volatility_classifier.clone() {
3126 parser = parser.with_volatility_classifier(move |name| classifier(name));
3127 }
3128 parser.parse()
3129 }
3130}
3131
3132#[derive(Default)]
3133pub struct BatchParserBuilder {
3134 include_whitespace: bool,
3135 volatility_classifier: Option<VolatilityClassifierArc>,
3136 dialect: FormulaDialect,
3137}
3138
3139impl BatchParserBuilder {
3140 pub fn include_whitespace(mut self, include: bool) -> Self {
3141 self.include_whitespace = include;
3142 self
3143 }
3144
3145 pub fn with_volatility_classifier<F>(mut self, f: F) -> Self
3146 where
3147 F: Fn(&str) -> bool + Send + Sync + 'static,
3148 {
3149 self.volatility_classifier = Some(Arc::new(f));
3150 self
3151 }
3152
3153 pub fn dialect(mut self, dialect: FormulaDialect) -> Self {
3154 self.dialect = dialect;
3155 self
3156 }
3157
3158 pub fn build(self) -> BatchParser {
3159 BatchParser {
3160 include_whitespace: self.include_whitespace,
3161 volatility_classifier: self.volatility_classifier,
3162 token_cache: std::collections::HashMap::new(),
3163 dialect: self.dialect,
3164 }
3165 }
3166}