1use std::convert::TryFrom;
2use std::error::Error;
3use std::fmt::{self, Display};
4use std::sync::Arc;
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9use crate::types::FormulaDialect;
10
11const TOKEN_ENDERS: &str = ",;}) +-*/^&=><%@";
12
13const fn build_token_enders() -> [bool; 256] {
14 let mut tbl = [false; 256];
15 let bytes = TOKEN_ENDERS.as_bytes();
16 let mut i = 0;
17 while i < bytes.len() {
18 tbl[bytes[i] as usize] = true;
19 i += 1;
20 }
21 tbl
22}
23static TOKEN_ENDERS_TABLE: [bool; 256] = build_token_enders();
24
25#[inline(always)]
26fn is_token_ender(c: u8) -> bool {
27 TOKEN_ENDERS_TABLE[c as usize]
28}
29
30static ERROR_CODES: &[&str] = &[
47 "#GETTING_DATA",
48 "#DIV/0!",
49 "#VALUE!",
50 "#SPILL!",
51 "#NAME?",
52 "#NULL!",
53 "#CALC!",
54 "#NUM!",
55 "#REF!",
56 "#N/A",
57];
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum Associativity {
62 Left,
63 Right,
64}
65
66#[derive(Debug)]
68pub struct TokenizerError {
69 pub message: String,
70 pub pos: usize,
71}
72
73#[non_exhaustive]
75#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub enum RecoveryAction {
78 SkippedUnmatchedCloser,
80 UnterminatedString,
82 UnmatchedBracket,
84 InvalidErrorLiteral,
86 UnmatchedOpener,
88}
89
90#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct TokenDiagnostic {
94 pub span: TokenSpan,
95 pub message: String,
96 pub recovery: RecoveryAction,
97}
98
99impl TokenDiagnostic {
100 fn new(span: TokenSpan, message: String, recovery: RecoveryAction) -> Self {
101 Self {
102 span,
103 message,
104 recovery,
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
110struct SpanTokenizerError {
111 kind: SpanTokenizerErrorKind,
112 pos: usize,
113 message: String,
114 span_start: Option<usize>,
115 span_end: Option<usize>,
116}
117
118#[derive(Debug, Clone, Copy)]
119enum SpanTokenizerErrorKind {
120 NoMatchingOpener,
121 UnmatchedOpening,
122 UnterminatedString,
123 UnmatchedBracket,
124 MismatchedPair,
125 InvalidErrorLiteral,
126}
127
128impl From<SpanTokenizerError> for TokenizerError {
129 fn from(value: SpanTokenizerError) -> Self {
130 TokenizerError {
131 message: value.message,
132 pos: value.pos,
133 }
134 }
135}
136
137impl fmt::Display for TokenizerError {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 write!(f, "TokenizerError: {}", self.message)
140 }
141}
142
143impl Error for TokenizerError {}
144
145#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
148pub enum TokenType {
149 Literal,
150 Operand,
151 Func,
152 Array,
153 Paren,
154 Sep,
155 OpPrefix,
156 OpInfix,
157 OpPostfix,
158 Whitespace,
159}
160impl Display for TokenType {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 write!(f, "{self:?}")
163 }
164}
165
166#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
169pub enum TokenSubType {
170 None,
171 Text,
172 Number,
173 Logical,
174 Error,
175 Range,
176 Open,
177 Close,
178 Arg,
179 Row,
180}
181impl Display for TokenSubType {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 write!(f, "{self:?}")
184 }
185}
186
187#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
189#[derive(Debug, Clone, PartialEq, Hash)]
190pub struct Token {
191 pub value: String, pub token_type: TokenType,
193 pub subtype: TokenSubType,
194 pub start: usize,
195 pub end: usize,
196}
197
198impl Display for Token {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 write!(
201 f,
202 "<{} subtype: {:?} value: {}>",
203 self.token_type, self.subtype, self.value
204 )
205 }
206}
207
208impl Token {
209 pub fn new(value: String, token_type: TokenType, subtype: TokenSubType) -> Self {
210 Token {
211 value,
212 token_type,
213 subtype,
214 start: 0,
215 end: 0,
216 }
217 }
218
219 pub fn new_with_span(
220 value: String,
221 token_type: TokenType,
222 subtype: TokenSubType,
223 start: usize,
224 end: usize,
225 ) -> Self {
226 Token {
227 value,
228 token_type,
229 subtype,
230 start,
231 end,
232 }
233 }
234
235 fn from_slice(
236 source: &str,
237 token_type: TokenType,
238 subtype: TokenSubType,
239 start: usize,
240 end: usize,
241 ) -> Self {
242 Token {
243 value: source[start..end].to_string(),
244 token_type,
245 subtype,
246 start,
247 end,
248 }
249 }
250
251 pub fn is_operator(&self) -> bool {
252 matches!(
253 self.token_type,
254 TokenType::OpPrefix | TokenType::OpInfix | TokenType::OpPostfix
255 )
256 }
257
258 pub fn get_precedence(&self) -> Option<(u8, Associativity)> {
259 let op = if self.token_type == TokenType::OpPrefix {
261 "u"
262 } else {
263 self.value.as_str()
264 };
265
266 match op {
277 "#" => Some((11, Associativity::Left)),
278 ":" => Some((10, Associativity::Left)),
279 " " => Some((9, Associativity::Left)),
280 "," => Some((8, Associativity::Left)),
281 "%" => Some((7, Associativity::Left)),
282 "u" => Some((6, Associativity::Right)),
283 "^" => Some((5, Associativity::Right)),
284 "*" | "/" => Some((4, Associativity::Left)),
285 "+" | "-" => Some((3, Associativity::Left)),
286 "&" => Some((2, Associativity::Left)),
287 "=" | "<" | ">" | "<=" | ">=" | "<>" => Some((1, Associativity::Left)),
288 _ => None,
289 }
290 }
291
292 pub fn make_operand(value: String) -> Self {
294 let subtype = if value.starts_with('"') {
295 TokenSubType::Text
296 } else if value.starts_with('#') {
297 TokenSubType::Error
298 } else if value.eq_ignore_ascii_case("TRUE") || value.eq_ignore_ascii_case("FALSE") {
299 TokenSubType::Logical
300 } else if value.parse::<f64>().is_ok() {
301 TokenSubType::Number
302 } else {
303 TokenSubType::Range
304 };
305 Token::new(value, TokenType::Operand, subtype)
306 }
307
308 pub fn make_operand_with_span(value: String, start: usize, end: usize) -> Self {
310 let subtype = if value.starts_with('"') {
311 TokenSubType::Text
312 } else if value.starts_with('#') {
313 TokenSubType::Error
314 } else if value.eq_ignore_ascii_case("TRUE") || value.eq_ignore_ascii_case("FALSE") {
315 TokenSubType::Logical
316 } else if value.parse::<f64>().is_ok() {
317 TokenSubType::Number
318 } else {
319 TokenSubType::Range
320 };
321 Token::new_with_span(value, TokenType::Operand, subtype, start, end)
322 }
323
324 fn make_operand_from_slice(source: &str, start: usize, end: usize) -> Self {
325 let value_str = &source[start..end];
326 let subtype = if value_str.starts_with('"') {
327 TokenSubType::Text
328 } else if value_str.starts_with('#') {
329 TokenSubType::Error
330 } else if value_str.eq_ignore_ascii_case("TRUE") || value_str.eq_ignore_ascii_case("FALSE")
331 {
332 TokenSubType::Logical
333 } else if value_str.parse::<f64>().is_ok() {
334 TokenSubType::Number
335 } else {
336 TokenSubType::Range
337 };
338 Token::from_slice(source, TokenType::Operand, subtype, start, end)
339 }
340
341 pub fn make_subexp(value: &str, func: bool) -> Self {
346 let last_char = value.chars().last().expect("Empty token value");
347 assert!(matches!(last_char, '{' | '}' | '(' | ')'));
348 let token_type = if func {
349 TokenType::Func
350 } else if "{}".contains(last_char) {
351 TokenType::Array
352 } else if "()".contains(last_char) {
353 TokenType::Paren
354 } else {
355 TokenType::Func
356 };
357 let subtype = if ")}".contains(last_char) {
358 TokenSubType::Close
359 } else {
360 TokenSubType::Open
361 };
362 Token::new(value.to_string(), token_type, subtype)
363 }
364
365 pub fn make_subexp_with_span(value: &str, func: bool, start: usize, end: usize) -> Self {
367 let last_char = value.chars().last().expect("Empty token value");
368 assert!(matches!(last_char, '{' | '}' | '(' | ')'));
369 let token_type = if func {
370 TokenType::Func
371 } else if "{}".contains(last_char) {
372 TokenType::Array
373 } else if "()".contains(last_char) {
374 TokenType::Paren
375 } else {
376 TokenType::Func
377 };
378 let subtype = if ")}".contains(last_char) {
379 TokenSubType::Close
380 } else {
381 TokenSubType::Open
382 };
383 Token::new_with_span(value.to_string(), token_type, subtype, start, end)
384 }
385
386 fn make_subexp_from_slice(source: &str, func: bool, start: usize, end: usize) -> Self {
387 let value_str = &source[start..end];
388 let last_char = value_str.chars().last().expect("Empty token value");
389 let token_type = if func {
390 TokenType::Func
391 } else if "{}".contains(last_char) {
392 TokenType::Array
393 } else if "()".contains(last_char) {
394 TokenType::Paren
395 } else {
396 TokenType::Func
397 };
398 let subtype = if ")}".contains(last_char) {
399 TokenSubType::Close
400 } else {
401 TokenSubType::Open
402 };
403 Token::from_slice(source, token_type, subtype, start, end)
404 }
405
406 pub fn get_closer(&self) -> Result<Token, TokenizerError> {
408 if self.subtype != TokenSubType::Open {
409 return Err(TokenizerError {
410 message: "Token is not an opener".to_string(),
411 pos: 0,
412 });
413 }
414 let closer_value = if self.token_type == TokenType::Array {
415 "}"
416 } else {
417 ")"
418 };
419 Ok(Token::make_subexp(
420 closer_value,
421 self.token_type == TokenType::Func,
422 ))
423 }
424
425 pub fn make_separator(value: &str) -> Self {
427 assert!(value == "," || value == ";");
428 let subtype = if value == "," {
429 TokenSubType::Arg
430 } else {
431 TokenSubType::Row
432 };
433 Token::new(value.to_string(), TokenType::Sep, subtype)
434 }
435
436 pub fn make_separator_with_span(value: &str, start: usize, end: usize) -> Self {
438 assert!(value == "," || value == ";");
439 let subtype = if value == "," {
440 TokenSubType::Arg
441 } else {
442 TokenSubType::Row
443 };
444 Token::new_with_span(value.to_string(), TokenType::Sep, subtype, start, end)
445 }
446}
447
448#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
449#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
450pub struct TokenSpan {
451 pub token_type: TokenType,
452 pub subtype: TokenSubType,
453 pub start: usize,
454 pub end: usize,
455}
456
457#[derive(Debug, Clone, Copy, PartialEq, Eq)]
458pub struct TokenView<'a> {
459 pub span: &'a TokenSpan,
460 pub value: &'a str,
461}
462
463#[derive(Debug, Clone)]
469pub struct TokenStream {
470 source: Arc<str>,
471 pub spans: Vec<TokenSpan>,
472 dialect: FormulaDialect,
473 diagnostics: Vec<TokenDiagnostic>,
474}
475
476fn is_whitespace_intersection_span(source: &str, span: &TokenSpan) -> bool {
477 if span.token_type != TokenType::OpInfix {
478 return false;
479 }
480 let Some(value) = source.get(span.start..span.end) else {
481 return false;
482 };
483 value.as_bytes().contains(&b' ')
484 && value
485 .as_bytes()
486 .iter()
487 .all(|byte| matches!(byte, b' ' | b'\t' | b'\r' | b'\n'))
488}
489
490impl TokenStream {
491 pub fn new(formula: &str) -> Result<Self, TokenizerError> {
492 Self::new_with_dialect(formula, FormulaDialect::Excel)
493 }
494
495 pub fn new_with_dialect(
496 formula: &str,
497 dialect: FormulaDialect,
498 ) -> Result<Self, TokenizerError> {
499 let source: Arc<str> = Arc::from(formula);
500 let spans = tokenize_spans_with_dialect(source.as_ref(), dialect)?;
501 Ok(TokenStream {
502 source,
503 spans,
504 dialect,
505 diagnostics: Vec::new(),
506 })
507 }
508
509 pub fn new_best_effort(formula: &str) -> Self {
510 Self::new_best_effort_with_dialect(formula, FormulaDialect::Excel)
511 }
512
513 pub fn new_best_effort_with_dialect(formula: &str, dialect: FormulaDialect) -> Self {
514 let source: Arc<str> = Arc::from(formula);
515 let mut tokenizer = SpanTokenizer::new(source.as_ref(), dialect);
516 let spans = tokenizer.parse_best_effort();
517 let diagnostics = tokenizer.diagnostics;
518 TokenStream {
519 source,
520 spans,
521 dialect,
522 diagnostics,
523 }
524 }
525
526 pub fn diagnostics(&self) -> Vec<TokenDiagnostic> {
527 self.diagnostics.clone()
528 }
529
530 pub fn diagnostics_ref(&self) -> &[TokenDiagnostic] {
531 &self.diagnostics
532 }
533
534 pub fn has_errors(&self) -> bool {
535 !self.diagnostics.is_empty()
536 }
537
538 pub fn invalid_spans_iter(&self) -> impl Iterator<Item = &TokenSpan> {
539 self.spans.iter().filter(|span| {
540 self.diagnostics.iter().any(|diag| {
541 diag.span.start == span.start
542 && diag.span.end == span.end
543 && diag.span.token_type == span.token_type
544 })
545 })
546 }
547
548 pub fn invalid_spans(&self) -> Vec<&TokenSpan> {
549 self.invalid_spans_iter().collect()
550 }
551
552 pub fn source(&self) -> &str {
553 &self.source
554 }
555
556 pub fn dialect(&self) -> FormulaDialect {
557 self.dialect
558 }
559
560 pub fn len(&self) -> usize {
561 self.spans.len()
562 }
563
564 pub fn is_empty(&self) -> bool {
565 self.spans.is_empty()
566 }
567
568 pub fn get(&self, index: usize) -> Option<TokenView<'_>> {
569 let span = self.spans.get(index)?;
570 let value = self.source.get(span.start..span.end)?;
571 Some(TokenView { span, value })
572 }
573
574 pub fn to_tokens(&self) -> Vec<Token> {
575 self.spans
576 .iter()
577 .map(|s| {
578 let value = if is_whitespace_intersection_span(&self.source, s) {
579 " ".to_string()
580 } else {
581 self.source
582 .get(s.start..s.end)
583 .unwrap_or_default()
584 .to_string()
585 };
586 Token::new_with_span(value, s.token_type, s.subtype, s.start, s.end)
587 })
588 .collect()
589 }
590
591 pub fn render(&self) -> String {
596 let mut out = String::with_capacity(self.source.len());
597 for span in &self.spans {
598 if let Some(s) = self.source.get(span.start..span.end) {
599 out.push_str(s);
600 }
601 }
602 out
603 }
604
605 pub fn render_formula(&self) -> String {
607 if self.source.as_bytes().first() == Some(&b'=') {
608 format!("={}", self.render())
609 } else {
610 self.render()
611 }
612 }
613}
614
615pub(crate) fn tokenize_spans_with_dialect(
616 formula: &str,
617 dialect: FormulaDialect,
618) -> Result<Vec<TokenSpan>, TokenizerError> {
619 let mut tokenizer = SpanTokenizer::new(formula, dialect);
620 tokenizer.parse()?;
621 Ok(tokenizer.spans)
622}
623
624fn operand_subtype(value_str: &str) -> TokenSubType {
625 if value_str.starts_with('"') {
626 TokenSubType::Text
627 } else if value_str.starts_with('#') {
628 TokenSubType::Error
629 } else if value_str.eq_ignore_ascii_case("TRUE") || value_str.eq_ignore_ascii_case("FALSE") {
630 TokenSubType::Logical
631 } else if value_str.parse::<f64>().is_ok() {
632 TokenSubType::Number
633 } else {
634 TokenSubType::Range
635 }
636}
637
638fn is_cell_reference_like(value: &str) -> bool {
639 let bytes = value.as_bytes();
640 let mut i = 0;
641
642 if i < bytes.len() && bytes[i] == b'$' {
643 i += 1;
644 }
645
646 let col_start = i;
647 while i < bytes.len() && bytes[i].is_ascii_alphabetic() {
648 i += 1;
649 }
650 if i == col_start {
651 return false;
652 }
653
654 if i < bytes.len() && bytes[i] == b'$' {
655 i += 1;
656 }
657
658 let row_start = i;
659 while i < bytes.len() && bytes[i].is_ascii_digit() {
660 i += 1;
661 }
662
663 i == bytes.len() && i > row_start
664}
665
666fn reference_value_contains_range_colon(value: &str) -> bool {
667 let value_part = value
668 .rsplit_once('!')
669 .map_or(value, |(_, value_part)| value_part);
670 value_part.contains(':')
671}
672
673fn value_has_structured_reference_bracket(value: &str) -> bool {
678 value
679 .rsplit_once('!')
680 .map_or(value, |(_, value_part)| value_part)
681 .contains('[')
682}
683
684fn is_reference_operand_value(value: &str) -> bool {
685 operand_subtype(value) == TokenSubType::Range
686 && (reference_value_contains_range_colon(value)
687 || value.contains('!')
688 || value.contains('[')
689 || is_cell_reference_like(value))
690}
691
692fn next_starts_reference_expression(formula: &str, mut offset: usize) -> bool {
693 let bytes = formula.as_bytes();
694 while offset < bytes.len() && matches!(bytes[offset], b' ' | b'\t' | b'\r' | b'\n') {
695 offset += 1;
696 }
697 if offset >= bytes.len() {
698 return false;
699 }
700
701 matches!(bytes[offset], b'(' | b'[' | b'\'' | b'$') || bytes[offset].is_ascii_alphabetic()
702}
703
704fn next_reference_has_sheet_qualifier(formula: &str, mut offset: usize) -> bool {
705 let bytes = formula.as_bytes();
706 while offset < bytes.len() && matches!(bytes[offset], b' ' | b'\t' | b'\r' | b'\n') {
707 offset += 1;
708 }
709
710 let mut in_quote = false;
711 while offset < bytes.len() {
712 match bytes[offset] {
713 b'\'' => {
714 if in_quote && offset + 1 < bytes.len() && bytes[offset + 1] == b'\'' {
715 offset += 2;
716 continue;
717 }
718 in_quote = !in_quote;
719 }
720 b'!' => return true,
721 b':' if !in_quote => return false,
722 b',' | b';' | b'}' | b')' | b' ' | b'\t' | b'\r' | b'\n' | b'+' | b'-' | b'*'
723 | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@'
724 if !in_quote =>
725 {
726 return false;
727 }
728 _ => {}
729 }
730 offset += 1;
731 }
732
733 false
734}
735
736struct SpanTokenizer<'a> {
737 formula: &'a str,
738 spans: Vec<TokenSpan>,
739 token_stack: Vec<TokenSpan>,
740 offset: usize,
741 token_start: usize,
742 token_end: usize,
743 dialect: FormulaDialect,
744 diagnostics: Vec<TokenDiagnostic>,
745}
746
747impl<'a> SpanTokenizer<'a> {
748 fn new(formula: &'a str, dialect: FormulaDialect) -> Self {
749 SpanTokenizer {
750 formula,
751 spans: Vec::with_capacity(formula.len() / 2),
752 token_stack: Vec::with_capacity(16),
753 offset: 0,
754 token_start: 0,
755 token_end: 0,
756 dialect,
757 diagnostics: Vec::new(),
758 }
759 }
760
761 #[inline]
762 fn current_byte(&self) -> Option<u8> {
763 self.formula.as_bytes().get(self.offset).copied()
764 }
765
766 #[inline]
767 fn has_token(&self) -> bool {
768 self.token_end > self.token_start
769 }
770
771 #[inline]
772 fn start_token(&mut self) {
773 self.token_start = self.offset;
774 self.token_end = self.offset;
775 }
776
777 #[inline]
778 fn extend_token(&mut self) {
779 self.token_end = self.offset;
780 }
781
782 fn push_span(
783 &mut self,
784 token_type: TokenType,
785 subtype: TokenSubType,
786 start: usize,
787 end: usize,
788 ) {
789 self.spans.push(TokenSpan {
790 token_type,
791 subtype,
792 start,
793 end,
794 });
795 }
796
797 fn save_token(&mut self) {
798 if self.has_token() {
799 let value_str = &self.formula[self.token_start..self.token_end];
800 let subtype = operand_subtype(value_str);
801 self.push_span(
802 TokenType::Operand,
803 subtype,
804 self.token_start,
805 self.token_end,
806 );
807 }
808 }
809
810 fn check_scientific_notation(&mut self) -> bool {
811 if let Some(curr_byte) = self.current_byte() {
812 if (curr_byte == b'+' || curr_byte == b'-')
813 && self.has_token()
814 && self.is_scientific_notation_base()
815 && self
816 .formula
817 .as_bytes()
818 .get(self.offset + 1)
819 .is_some_and(|b| b.is_ascii_digit())
820 {
821 self.offset += 1;
822 self.extend_token();
823 return true;
824 }
825 }
826 false
827 }
828
829 fn is_scientific_notation_base(&self) -> bool {
830 if !self.has_token() {
831 return false;
832 }
833
834 let token_slice = &self.formula.as_bytes()[self.token_start..self.token_end];
835 if token_slice.len() < 2 {
836 return false;
837 }
838
839 let last = token_slice[token_slice.len() - 1];
840 if !(last == b'E' || last == b'e') {
841 return false;
842 }
843
844 let first = token_slice[0];
845 if !first.is_ascii_digit() {
846 return false;
847 }
848
849 let mut dot_seen = false;
850 for &ch in &token_slice[1..token_slice.len() - 1] {
851 match ch {
852 b'0'..=b'9' => {}
853 b'.' if !dot_seen => dot_seen = true,
854 _ => return false,
855 }
856 }
857 true
858 }
859
860 fn parse(&mut self) -> Result<(), TokenizerError> {
861 self.parse_with_recovery(false).map_err(Into::into)
862 }
863
864 pub(crate) fn parse_best_effort(&mut self) -> Vec<TokenSpan> {
865 let _ = self.parse_with_recovery(true);
866 self.spans.clone()
867 }
868
869 fn parse_with_recovery(&mut self, best_effort: bool) -> Result<(), SpanTokenizerError> {
870 if self.formula.is_empty() {
871 return Ok(());
872 }
873
874 if self.formula.as_bytes()[0] != b'=' {
875 self.push_span(
876 TokenType::Literal,
877 TokenSubType::None,
878 0,
879 self.formula.len(),
880 );
881 return Ok(());
882 }
883
884 self.offset = 1;
885 self.start_token();
886
887 while self.offset < self.formula.len() {
888 if self.check_scientific_notation() {
889 continue;
890 }
891
892 let curr_byte = self.formula.as_bytes()[self.offset];
893
894 if is_token_ender(curr_byte) && self.has_token() {
895 self.save_token();
896 self.start_token();
897 }
898
899 let parse_result = match curr_byte {
900 b'"' | b'\'' => self.parse_string(),
901 b'[' => self.parse_brackets(),
902 b'#' => {
903 if self.should_emit_hash_postfix() {
904 self.emit_hash_postfix();
905 Ok(())
906 } else {
907 self.parse_error()
908 }
909 }
910 b' ' | b'\t' | b'\r' | b'\n' => self.parse_whitespace(),
911 b':' => {
912 if self.should_emit_colon_infix() {
913 self.emit_infix_operator(self.offset, self.offset + 1);
914 Ok(())
915 } else {
916 if !self.has_token() {
917 self.start_token();
918 }
919 self.offset += 1;
920 self.extend_token();
921 Ok(())
922 }
923 }
924 b'+' | b'-' | b'*' | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@' => {
925 self.parse_operator()
926 }
927 b'{' | b'(' => self.parse_opener(),
928 b')' | b'}' => self.parse_closer(),
929 b';' | b',' => self.parse_separator(),
930 _ => {
931 if !self.has_token() {
932 self.start_token();
933 }
934 self.offset += 1;
935 self.extend_token();
936 Ok(())
937 }
938 };
939
940 if let Err(err) = parse_result {
941 if best_effort {
942 self.recover_from_error(err);
943 } else {
944 return Err(err);
945 }
946 }
947 }
948
949 if self.has_token() {
950 self.save_token();
951 }
952
953 if !self.token_stack.is_empty() {
954 if best_effort {
955 while let Some(open_token) = self.token_stack.pop() {
956 if let Some(span) = self.spans.iter().find(|span| {
957 span.start == open_token.start
958 && span.end == open_token.end
959 && span.token_type == open_token.token_type
960 && span.subtype == open_token.subtype
961 }) {
962 self.diagnostics.push(TokenDiagnostic::new(
963 *span,
964 "Unmatched opening parenthesis or bracket".to_string(),
965 RecoveryAction::UnmatchedOpener,
966 ));
967 }
968 }
969 } else {
970 return Err(SpanTokenizerError {
971 kind: SpanTokenizerErrorKind::UnmatchedOpening,
972 pos: self.offset,
973 message: "Unmatched opening parenthesis or bracket".to_string(),
974 span_start: None,
975 span_end: None,
976 });
977 }
978 }
979
980 Ok(())
981 }
982
983 fn recover_from_error(&mut self, error: SpanTokenizerError) {
984 match error.kind {
985 SpanTokenizerErrorKind::NoMatchingOpener => {
986 let span = TokenSpan {
987 token_type: TokenType::Operand,
988 subtype: TokenSubType::None,
989 start: error.pos,
990 end: error.pos + 1,
991 };
992 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
993 self.offset = span.end;
994 self.start_token();
995 self.diagnostics.push(TokenDiagnostic::new(
996 span,
997 format!("No matching opener for closer at position {}", error.pos),
998 RecoveryAction::SkippedUnmatchedCloser,
999 ));
1000 }
1001 SpanTokenizerErrorKind::UnmatchedOpening => {
1002 debug_assert!(
1003 false,
1004 "UnmatchedOpening is handled at end-of-input and should not be routed through recover_from_error"
1005 );
1006 }
1007 SpanTokenizerErrorKind::UnterminatedString => {
1008 let start = error.span_start.unwrap_or(error.pos);
1009 let span = TokenSpan {
1010 token_type: TokenType::Operand,
1011 subtype: TokenSubType::None,
1012 start,
1013 end: self.formula.len(),
1014 };
1015 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1016 self.offset = span.end;
1017 self.start_token();
1018 self.diagnostics.push(TokenDiagnostic::new(
1019 span,
1020 "Reached end of formula while parsing string".to_string(),
1021 RecoveryAction::UnterminatedString,
1022 ));
1023 }
1024 SpanTokenizerErrorKind::UnmatchedBracket => {
1025 let start = error.span_start.unwrap_or(error.pos);
1026 let end = error.span_end.unwrap_or(self.formula.len());
1027 let span = TokenSpan {
1028 token_type: TokenType::Operand,
1029 subtype: TokenSubType::None,
1030 start,
1031 end,
1032 };
1033 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1034 self.offset = span.end;
1035 self.start_token();
1036 self.diagnostics.push(TokenDiagnostic::new(
1037 span,
1038 "Encountered unmatched '['".to_string(),
1039 RecoveryAction::UnmatchedBracket,
1040 ));
1041 }
1042 SpanTokenizerErrorKind::MismatchedPair => {
1043 let span = TokenSpan {
1044 token_type: TokenType::Operand,
1045 subtype: TokenSubType::None,
1046 start: error.pos,
1047 end: error.pos + 1,
1048 };
1049 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1050 self.offset = span.end;
1051 self.start_token();
1052 self.diagnostics.push(TokenDiagnostic::new(
1053 span,
1054 "Mismatched ( and { pair".to_string(),
1055 RecoveryAction::SkippedUnmatchedCloser,
1056 ));
1057 }
1058 SpanTokenizerErrorKind::InvalidErrorLiteral => {
1059 let start = error.span_start.unwrap_or(error.pos);
1060 let end = error.span_end.unwrap_or(error.pos + 1);
1061 let span = TokenSpan {
1062 token_type: TokenType::Operand,
1063 subtype: TokenSubType::None,
1064 start,
1065 end,
1066 };
1067 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1068 self.offset = span.end;
1069 self.start_token();
1070 self.diagnostics.push(TokenDiagnostic::new(
1071 span,
1072 "Invalid error code".to_string(),
1073 RecoveryAction::InvalidErrorLiteral,
1074 ));
1075 }
1076 }
1077 }
1078
1079 fn parse_string(&mut self) -> Result<(), SpanTokenizerError> {
1080 let delim = self.formula.as_bytes()[self.offset];
1081 assert!(delim == b'"' || delim == b'\'');
1082
1083 let is_dollar_ref = delim == b'\''
1084 && self.has_token()
1085 && self.token_end - self.token_start == 1
1086 && self.formula.as_bytes()[self.token_start] == b'$';
1087
1088 let glue_to_token = delim == b'\''
1093 && self.has_token()
1094 && self.token_end > 0
1095 && self.formula.as_bytes()[self.token_end - 1] == b':';
1096
1097 if !is_dollar_ref && !glue_to_token && self.has_token() {
1098 self.save_token();
1099 self.start_token();
1100 }
1101
1102 let string_start = if is_dollar_ref {
1103 self.token_start
1104 } else {
1105 self.offset
1106 };
1107 self.offset += 1;
1108
1109 while self.offset < self.formula.len() {
1110 if self.formula.as_bytes()[self.offset] == delim {
1111 self.offset += 1;
1112 if self.offset < self.formula.len() && self.formula.as_bytes()[self.offset] == delim
1113 {
1114 self.offset += 1;
1115 } else {
1116 if delim == b'"' {
1117 let value_str = &self.formula[string_start..self.offset];
1118 let subtype = operand_subtype(value_str);
1119 self.push_span(TokenType::Operand, subtype, string_start, self.offset);
1120 self.start_token();
1121 } else {
1122 self.token_end = self.offset;
1123 }
1124 return Ok(());
1125 }
1126 } else {
1127 self.offset += 1;
1128 }
1129 }
1130
1131 Err(SpanTokenizerError {
1132 kind: SpanTokenizerErrorKind::UnterminatedString,
1133 pos: self.offset,
1134 message: "Reached end of formula while parsing string".to_string(),
1135 span_start: Some(string_start),
1136 span_end: Some(self.formula.len()),
1137 })
1138 }
1139
1140 fn parse_brackets(&mut self) -> Result<(), SpanTokenizerError> {
1141 assert_eq!(self.formula.as_bytes()[self.offset], b'[');
1142
1143 if !self.has_token() {
1144 self.start_token();
1145 }
1146
1147 let bracket_start = self.offset;
1148 let mut open_count = 1;
1149 self.offset += 1;
1150
1151 while self.offset < self.formula.len() {
1152 match self.formula.as_bytes()[self.offset] {
1153 b'\'' => {
1158 if self.offset + 1 < self.formula.len() {
1159 self.offset += 2;
1160 continue;
1161 }
1162 self.offset += 1;
1165 continue;
1166 }
1167 b'[' => open_count += 1,
1168 b']' => {
1169 open_count -= 1;
1170 if open_count == 0 {
1171 self.offset += 1;
1172 self.extend_token();
1173 return Ok(());
1174 }
1175 }
1176 _ => {}
1177 }
1178 self.offset += 1;
1179 }
1180
1181 Err(SpanTokenizerError {
1182 kind: SpanTokenizerErrorKind::UnmatchedBracket,
1183 pos: self.offset,
1184 message: "Encountered unmatched '['".to_string(),
1185 span_start: Some(bracket_start),
1186 span_end: Some(self.formula.len()),
1187 })
1188 }
1189
1190 fn parse_error(&mut self) -> Result<(), SpanTokenizerError> {
1191 let has_sheet_prefix = self.has_token()
1197 && self.token_end > 0
1198 && self.formula.as_bytes()[self.token_end - 1] == b'!';
1199 if has_sheet_prefix {
1200 if self.token_end - self.token_start <= 1 {
1201 return Err(SpanTokenizerError {
1202 kind: SpanTokenizerErrorKind::InvalidErrorLiteral,
1203 pos: self.offset,
1204 message: format!(
1205 "Empty sheet qualifier before error literal at position {}",
1206 self.offset
1207 ),
1208 span_start: Some(self.token_start),
1209 span_end: Some(self.offset),
1210 });
1211 }
1212 self.start_token();
1214 } else if self.has_token() {
1215 self.save_token();
1216 self.start_token();
1217 }
1218
1219 let error_start = self.offset;
1220
1221 for &err_code in ERROR_CODES {
1222 let err_bytes = err_code.as_bytes();
1223 if self.offset + err_bytes.len() <= self.formula.len() {
1224 let slice = &self.formula.as_bytes()[self.offset..self.offset + err_bytes.len()];
1225 if slice.eq_ignore_ascii_case(err_bytes) {
1226 self.push_span(
1227 TokenType::Operand,
1228 TokenSubType::Error,
1229 error_start,
1230 self.offset + err_bytes.len(),
1231 );
1232 self.offset += err_bytes.len();
1233 self.start_token();
1234 return Ok(());
1235 }
1236 }
1237 }
1238
1239 let mut end = self.offset + 1;
1240 while end < self.formula.len() {
1241 let ch = self.formula.as_bytes()[end];
1242 if is_token_ender(ch)
1243 || ch == b' '
1244 || ch == b'\t'
1245 || ch == b'\r'
1246 || ch == b'\n'
1247 || ch == b'('
1248 || ch == b'{'
1249 || ch == b'['
1250 || ch == b'"'
1251 || ch == b'\''
1252 {
1253 break;
1254 }
1255 end += 1;
1256 }
1257
1258 Err(SpanTokenizerError {
1259 kind: SpanTokenizerErrorKind::InvalidErrorLiteral,
1260 pos: self.offset,
1261 message: format!("Invalid error code at position {}", self.offset),
1262 span_start: Some(error_start),
1263 span_end: Some(end),
1264 })
1265 }
1266
1267 fn parse_whitespace(&mut self) -> Result<(), SpanTokenizerError> {
1268 self.save_token();
1269
1270 let ws_start = self.offset;
1271 let mut contains_intersection_space = false;
1272 while self.offset < self.formula.len() {
1273 match self.formula.as_bytes()[self.offset] {
1274 b' ' => {
1275 contains_intersection_space = true;
1276 self.offset += 1;
1277 }
1278 b'\t' | b'\r' | b'\n' => self.offset += 1,
1279 _ => break,
1280 }
1281 }
1282
1283 let token_type = if contains_intersection_space
1286 && self.prev_is_reference_producing()
1287 && next_starts_reference_expression(self.formula, self.offset)
1288 {
1289 TokenType::OpInfix
1290 } else {
1291 TokenType::Whitespace
1292 };
1293 self.push_span(token_type, TokenSubType::None, ws_start, self.offset);
1294 self.start_token();
1295 Ok(())
1296 }
1297
1298 fn prev_is_reference_producing(&self) -> bool {
1299 match self.prev_non_whitespace() {
1300 Some(prev) => match prev.token_type {
1301 TokenType::OpPostfix => true,
1302 TokenType::Paren | TokenType::Func | TokenType::Array
1303 if prev.subtype == TokenSubType::Close =>
1304 {
1305 true
1306 }
1307 TokenType::Operand if prev.subtype == TokenSubType::Range => self
1308 .formula
1309 .get(prev.start..prev.end)
1310 .is_some_and(is_reference_operand_value),
1311 _ => false,
1312 },
1313 None => false,
1314 }
1315 }
1316
1317 fn should_emit_colon_infix(&self) -> bool {
1318 if self.has_token() {
1319 let value = &self.formula[self.token_start..self.token_end];
1320 if value.ends_with('!') {
1321 return false;
1322 }
1323 return reference_value_contains_range_colon(value)
1324 || value_has_structured_reference_bracket(value)
1325 || (value.contains('!')
1326 && next_reference_has_sheet_qualifier(self.formula, self.offset + 1));
1327 }
1328 self.prev_is_reference_producing()
1329 }
1330
1331 fn emit_infix_operator(&mut self, start: usize, end: usize) {
1332 self.save_token();
1333 self.start_token();
1334 self.push_span(TokenType::OpInfix, TokenSubType::None, start, end);
1335 self.offset = end;
1336 self.start_token();
1337 }
1338
1339 fn prev_non_whitespace(&self) -> Option<&TokenSpan> {
1340 self.spans
1341 .iter()
1342 .rev()
1343 .find(|t| t.token_type != TokenType::Whitespace)
1344 }
1345
1346 fn should_emit_hash_postfix(&self) -> bool {
1351 if self.has_token() {
1352 if self.formula.as_bytes()[self.token_end - 1] == b'!' {
1356 return false;
1357 }
1358 let value = &self.formula[self.token_start..self.token_end];
1359 return operand_subtype(value) == TokenSubType::Range;
1360 }
1361 match self.prev_non_whitespace() {
1362 Some(prev) => match prev.token_type {
1363 TokenType::OpPostfix => true,
1364 TokenType::Paren | TokenType::Func | TokenType::Array
1365 if prev.subtype == TokenSubType::Close =>
1366 {
1367 true
1368 }
1369 TokenType::Operand if prev.subtype == TokenSubType::Range => true,
1370 _ => false,
1371 },
1372 None => false,
1373 }
1374 }
1375
1376 fn emit_hash_postfix(&mut self) {
1377 self.save_token();
1378 self.start_token();
1379 self.push_span(
1380 TokenType::OpPostfix,
1381 TokenSubType::None,
1382 self.offset,
1383 self.offset + 1,
1384 );
1385 self.offset += 1;
1386 self.start_token();
1387 }
1388
1389 fn parse_operator(&mut self) -> Result<(), SpanTokenizerError> {
1390 self.save_token();
1391
1392 if self.offset + 1 < self.formula.len() {
1393 let two_char = &self.formula.as_bytes()[self.offset..self.offset + 2];
1394 if two_char == b">=" || two_char == b"<=" || two_char == b"<>" {
1395 self.push_span(
1396 TokenType::OpInfix,
1397 TokenSubType::None,
1398 self.offset,
1399 self.offset + 2,
1400 );
1401 self.offset += 2;
1402 self.start_token();
1403 return Ok(());
1404 }
1405 }
1406
1407 let curr_byte = self.formula.as_bytes()[self.offset];
1408 let token_type = match curr_byte {
1409 b'@' => TokenType::OpPrefix,
1410 b'%' => TokenType::OpPostfix,
1411 b'+' | b'-' => {
1412 if self.spans.is_empty() {
1413 TokenType::OpPrefix
1414 } else {
1415 let prev = self.prev_non_whitespace();
1416 if let Some(p) = prev {
1417 if p.subtype == TokenSubType::Close
1418 || p.token_type == TokenType::OpPostfix
1419 || p.token_type == TokenType::Operand
1420 {
1421 TokenType::OpInfix
1422 } else {
1423 TokenType::OpPrefix
1424 }
1425 } else {
1426 TokenType::OpPrefix
1427 }
1428 }
1429 }
1430 _ => TokenType::OpInfix,
1431 };
1432
1433 self.push_span(token_type, TokenSubType::None, self.offset, self.offset + 1);
1434 self.offset += 1;
1435 self.start_token();
1436 Ok(())
1437 }
1438
1439 fn parse_opener(&mut self) -> Result<(), SpanTokenizerError> {
1440 let curr_byte = self.formula.as_bytes()[self.offset];
1441 assert!(curr_byte == b'(' || curr_byte == b'{');
1442
1443 let token = if curr_byte == b'{' {
1444 self.save_token();
1445 TokenSpan {
1446 token_type: TokenType::Array,
1447 subtype: TokenSubType::Open,
1448 start: self.offset,
1449 end: self.offset + 1,
1450 }
1451 } else if self.has_token() {
1452 let token = TokenSpan {
1453 token_type: TokenType::Func,
1454 subtype: TokenSubType::Open,
1455 start: self.token_start,
1456 end: self.offset + 1,
1457 };
1458 self.token_start = self.offset + 1;
1459 self.token_end = self.offset + 1;
1460 token
1461 } else {
1462 TokenSpan {
1463 token_type: TokenType::Paren,
1464 subtype: TokenSubType::Open,
1465 start: self.offset,
1466 end: self.offset + 1,
1467 }
1468 };
1469
1470 self.spans.push(token);
1471 self.token_stack.push(token);
1472 self.offset += 1;
1473 self.start_token();
1474 Ok(())
1475 }
1476
1477 fn parse_closer(&mut self) -> Result<(), SpanTokenizerError> {
1478 self.save_token();
1479
1480 let curr_byte = self.formula.as_bytes()[self.offset];
1481 assert!(curr_byte == b')' || curr_byte == b'}');
1482
1483 if let Some(open_token) = self.token_stack.last().copied() {
1484 let expected = if open_token.token_type == TokenType::Array {
1485 b'}'
1486 } else {
1487 b')'
1488 };
1489 if curr_byte != expected {
1490 return Err(SpanTokenizerError {
1491 kind: SpanTokenizerErrorKind::MismatchedPair,
1492 pos: self.offset,
1493 message: "Mismatched ( and { pair".to_string(),
1494 span_start: Some(self.offset),
1495 span_end: Some(self.offset + 1),
1496 });
1497 }
1498
1499 self.token_stack.pop();
1500 self.push_span(
1501 open_token.token_type,
1502 TokenSubType::Close,
1503 self.offset,
1504 self.offset + 1,
1505 );
1506 } else {
1507 return Err(SpanTokenizerError {
1508 kind: SpanTokenizerErrorKind::NoMatchingOpener,
1509 pos: self.offset,
1510 message: format!("No matching opener for closer at position {}", self.offset),
1511 span_start: Some(self.offset),
1512 span_end: Some(self.offset + 1),
1513 });
1514 }
1515
1516 self.offset += 1;
1517 self.start_token();
1518 Ok(())
1519 }
1520
1521 fn parse_separator(&mut self) -> Result<(), SpanTokenizerError> {
1522 self.save_token();
1523
1524 let curr_byte = self.formula.as_bytes()[self.offset];
1525 assert!(curr_byte == b';' || curr_byte == b',');
1526
1527 let top_token = self.token_stack.last();
1528 let in_function_or_array = matches!(
1529 top_token.map(|t| t.token_type),
1530 Some(TokenType::Func | TokenType::Array)
1531 );
1532 let in_array = matches!(top_token.map(|t| t.token_type), Some(TokenType::Array));
1533
1534 let (token_type, subtype) = match curr_byte {
1535 b',' => {
1536 if in_function_or_array {
1537 (TokenType::Sep, TokenSubType::Arg)
1538 } else {
1539 (TokenType::OpInfix, TokenSubType::None)
1540 }
1541 }
1542 b';' => {
1543 if in_array {
1544 (TokenType::Sep, TokenSubType::Row)
1545 } else if self.dialect == FormulaDialect::OpenFormula && in_function_or_array {
1546 (TokenType::Sep, TokenSubType::Arg)
1547 } else if self.dialect == FormulaDialect::OpenFormula {
1548 (TokenType::OpInfix, TokenSubType::None)
1549 } else {
1550 (TokenType::Sep, TokenSubType::Row)
1551 }
1552 }
1553 _ => (TokenType::OpInfix, TokenSubType::None),
1554 };
1555
1556 self.push_span(token_type, subtype, self.offset, self.offset + 1);
1557 self.offset += 1;
1558 self.start_token();
1559 Ok(())
1560 }
1561}
1562
1563pub struct Tokenizer {
1565 formula: String, pub items: Vec<Token>,
1567 token_stack: Vec<Token>,
1568 offset: usize, token_start: usize, token_end: usize, dialect: FormulaDialect,
1572}
1573
1574impl Tokenizer {
1575 pub fn new(formula: &str) -> Result<Self, TokenizerError> {
1577 Self::new_with_dialect(formula, FormulaDialect::Excel)
1578 }
1579
1580 pub fn new_best_effort(formula: &str) -> Self {
1582 Self::new_best_effort_with_dialect(formula, FormulaDialect::Excel)
1583 }
1584
1585 pub fn new_best_effort_with_dialect(formula: &str, dialect: FormulaDialect) -> Self {
1587 let stream = TokenStream::new_best_effort_with_dialect(formula, dialect);
1588 Self::from_token_stream(&stream)
1589 }
1590
1591 pub fn new_with_dialect(
1593 formula: &str,
1594 dialect: FormulaDialect,
1595 ) -> Result<Self, TokenizerError> {
1596 let mut tokenizer = Tokenizer {
1597 formula: formula.to_string(),
1598 items: Vec::with_capacity(formula.len() / 2), token_stack: Vec::with_capacity(16),
1600 offset: 0,
1601 token_start: 0,
1602 token_end: 0,
1603 dialect,
1604 };
1605 tokenizer.parse()?;
1606 Ok(tokenizer)
1607 }
1608
1609 pub fn from_token_stream(stream: &TokenStream) -> Self {
1610 Tokenizer {
1611 formula: stream.source.to_string(),
1612 items: stream.to_tokens(),
1613 token_stack: Vec::with_capacity(16),
1614 offset: 0,
1615 token_start: 0,
1616 token_end: 0,
1617 dialect: stream.dialect,
1618 }
1619 }
1620
1621 #[inline]
1623 fn current_byte(&self) -> Option<u8> {
1624 self.formula.as_bytes().get(self.offset).copied()
1625 }
1626
1627 #[inline]
1629 fn has_token(&self) -> bool {
1630 self.token_end > self.token_start
1631 }
1632
1633 #[inline]
1635 fn start_token(&mut self) {
1636 self.token_start = self.offset;
1637 self.token_end = self.offset;
1638 }
1639
1640 #[inline]
1642 fn extend_token(&mut self) {
1643 self.token_end = self.offset;
1644 }
1645
1646 fn parse(&mut self) -> Result<(), TokenizerError> {
1648 if self.formula.is_empty() {
1649 return Ok(());
1650 }
1651
1652 if self.formula.as_bytes()[0] != b'=' {
1654 self.items.push(Token::new_with_span(
1655 self.formula.clone(),
1656 TokenType::Literal,
1657 TokenSubType::None,
1658 0,
1659 self.formula.len(),
1660 ));
1661 return Ok(());
1662 }
1663
1664 self.offset = 1;
1666 self.start_token();
1667
1668 while self.offset < self.formula.len() {
1669 if self.check_scientific_notation()? {
1670 continue;
1671 }
1672
1673 let curr_byte = self.formula.as_bytes()[self.offset];
1674
1675 if is_token_ender(curr_byte) && self.has_token() {
1677 self.save_token();
1678 self.start_token();
1679 }
1680
1681 match curr_byte {
1683 b'"' | b'\'' => self.parse_string()?,
1684 b'[' => self.parse_brackets()?,
1685 b'#' => {
1686 if self.should_emit_hash_postfix() {
1687 self.emit_hash_postfix();
1688 } else {
1689 self.parse_error()?
1690 }
1691 }
1692 b' ' | b'\t' | b'\r' | b'\n' => self.parse_whitespace()?,
1693 b':' => {
1694 if self.should_emit_colon_infix() {
1695 self.emit_infix_operator(self.offset, self.offset + 1);
1696 } else {
1697 if !self.has_token() {
1698 self.start_token();
1699 }
1700 self.offset += 1;
1701 self.extend_token();
1702 }
1703 }
1704 b'+' | b'-' | b'*' | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@' => {
1706 self.parse_operator()?
1707 }
1708 b'{' | b'(' => self.parse_opener()?,
1709 b')' | b'}' => self.parse_closer()?,
1710 b';' | b',' => self.parse_separator()?,
1711 _ => {
1712 if !self.has_token() {
1714 self.start_token();
1715 }
1716 self.offset += 1;
1717 self.extend_token();
1718 }
1719 }
1720 }
1721
1722 if self.has_token() {
1724 self.save_token();
1725 }
1726
1727 if !self.token_stack.is_empty() {
1729 return Err(TokenizerError {
1730 message: "Unmatched opening parenthesis or bracket".to_string(),
1731 pos: self.offset,
1732 });
1733 }
1734
1735 Ok(())
1736 }
1737
1738 fn check_scientific_notation(&mut self) -> Result<bool, TokenizerError> {
1746 if let Some(curr_byte) = self.current_byte() {
1747 if (curr_byte == b'+' || curr_byte == b'-')
1748 && self.has_token()
1749 && self.is_scientific_notation_base()
1750 && self
1751 .formula
1752 .as_bytes()
1753 .get(self.offset + 1)
1754 .is_some_and(|b| b.is_ascii_digit())
1755 {
1756 self.offset += 1;
1757 self.extend_token();
1758 return Ok(true);
1759 }
1760 }
1761 Ok(false)
1762 }
1763
1764 fn is_scientific_notation_base(&self) -> bool {
1767 if !self.has_token() {
1768 return false;
1769 }
1770
1771 let token_slice = &self.formula.as_bytes()[self.token_start..self.token_end];
1772 if token_slice.len() < 2 {
1773 return false;
1774 }
1775
1776 let last = token_slice[token_slice.len() - 1];
1777 if !(last == b'E' || last == b'e') {
1778 return false;
1779 }
1780
1781 let first = token_slice[0];
1782 if !first.is_ascii_digit() {
1783 return false;
1784 }
1785
1786 let mut dot_seen = false;
1787 for &ch in &token_slice[1..token_slice.len() - 1] {
1789 match ch {
1790 b'0'..=b'9' => {}
1791 b'.' if !dot_seen => dot_seen = true,
1792 _ => return false,
1793 }
1794 }
1795 true
1796 }
1797
1798 fn save_token(&mut self) {
1800 if self.has_token() {
1801 let token =
1802 Token::make_operand_from_slice(&self.formula, self.token_start, self.token_end);
1803 self.items.push(token);
1804 }
1805 }
1806
1807 fn parse_string(&mut self) -> Result<(), TokenizerError> {
1809 let delim = self.formula.as_bytes()[self.offset];
1810 assert!(delim == b'"' || delim == b'\'');
1811
1812 let is_dollar_ref = delim == b'\''
1814 && self.has_token()
1815 && self.token_end - self.token_start == 1
1816 && self.formula.as_bytes()[self.token_start] == b'$';
1817
1818 let glue_to_token = delim == b'\''
1823 && self.has_token()
1824 && self.token_end > 0
1825 && self.formula.as_bytes()[self.token_end - 1] == b':';
1826
1827 if !is_dollar_ref && !glue_to_token && self.has_token() {
1828 self.save_token();
1829 self.start_token();
1830 }
1831
1832 let string_start = if is_dollar_ref {
1833 self.token_start
1834 } else {
1835 self.offset
1836 };
1837 self.offset += 1; while self.offset < self.formula.len() {
1840 if self.formula.as_bytes()[self.offset] == delim {
1841 self.offset += 1;
1842 if self.offset < self.formula.len() && self.formula.as_bytes()[self.offset] == delim
1844 {
1845 self.offset += 1; } else {
1847 if delim == b'"' {
1849 let token = Token::make_operand_from_slice(
1850 &self.formula,
1851 string_start,
1852 self.offset,
1853 );
1854 self.items.push(token);
1855 self.start_token();
1856 } else {
1857 self.token_end = self.offset;
1859 }
1860 return Ok(());
1861 }
1862 } else {
1863 self.offset += 1;
1864 }
1865 }
1866
1867 Err(TokenizerError {
1868 message: "Reached end of formula while parsing string".to_string(),
1869 pos: self.offset,
1870 })
1871 }
1872
1873 fn parse_brackets(&mut self) -> Result<(), TokenizerError> {
1875 assert_eq!(self.formula.as_bytes()[self.offset], b'[');
1876
1877 if !self.has_token() {
1878 self.start_token();
1879 }
1880
1881 let mut open_count = 1;
1882 self.offset += 1;
1883
1884 while self.offset < self.formula.len() {
1885 match self.formula.as_bytes()[self.offset] {
1886 b'\'' => {
1891 if self.offset + 1 < self.formula.len() {
1892 self.offset += 2;
1893 continue;
1894 }
1895 self.offset += 1;
1896 continue;
1897 }
1898 b'[' => open_count += 1,
1899 b']' => {
1900 open_count -= 1;
1901 if open_count == 0 {
1902 self.offset += 1;
1903 self.extend_token();
1904 return Ok(());
1905 }
1906 }
1907 _ => {}
1908 }
1909 self.offset += 1;
1910 }
1911
1912 Err(TokenizerError {
1913 message: "Encountered unmatched '['".to_string(),
1914 pos: self.offset,
1915 })
1916 }
1917
1918 fn should_emit_hash_postfix(&self) -> bool {
1920 if self.has_token() {
1921 if self.formula.as_bytes()[self.token_end - 1] == b'!' {
1922 return false;
1923 }
1924 let value = &self.formula[self.token_start..self.token_end];
1925 let is_range = !value.starts_with('"')
1929 && !value.starts_with('#')
1930 && value != "TRUE"
1931 && value != "FALSE"
1932 && value.parse::<f64>().is_err();
1933 return is_range;
1934 }
1935 let prev = self
1936 .items
1937 .iter()
1938 .rev()
1939 .find(|t| t.token_type != TokenType::Whitespace);
1940 match prev {
1941 Some(p) => match p.token_type {
1942 TokenType::OpPostfix => true,
1943 TokenType::Paren | TokenType::Func | TokenType::Array
1944 if p.subtype == TokenSubType::Close =>
1945 {
1946 true
1947 }
1948 TokenType::Operand if p.subtype == TokenSubType::Range => true,
1949 _ => false,
1950 },
1951 None => false,
1952 }
1953 }
1954
1955 fn emit_hash_postfix(&mut self) {
1956 self.save_token();
1957 self.start_token();
1958 self.items.push(Token::from_slice(
1959 &self.formula,
1960 TokenType::OpPostfix,
1961 TokenSubType::None,
1962 self.offset,
1963 self.offset + 1,
1964 ));
1965 self.offset += 1;
1966 self.start_token();
1967 }
1968
1969 fn parse_error(&mut self) -> Result<(), TokenizerError> {
1971 let has_sheet_prefix = self.has_token()
1976 && self.token_end > 0
1977 && self.formula.as_bytes()[self.token_end - 1] == b'!';
1978 if has_sheet_prefix {
1979 if self.token_end - self.token_start <= 1 {
1980 return Err(TokenizerError {
1981 message: format!(
1982 "Empty sheet qualifier before error literal at position {}",
1983 self.offset
1984 ),
1985 pos: self.offset,
1986 });
1987 }
1988 self.start_token();
1990 } else if self.has_token() {
1991 self.save_token();
1992 self.start_token();
1993 }
1994
1995 let error_start = self.offset;
1996
1997 for &err_code in ERROR_CODES {
1999 let err_bytes = err_code.as_bytes();
2000 if self.offset + err_bytes.len() <= self.formula.len() {
2001 let slice = &self.formula.as_bytes()[self.offset..self.offset + err_bytes.len()];
2002 if slice.eq_ignore_ascii_case(err_bytes) {
2003 let token = Token::make_operand_from_slice(
2004 &self.formula,
2005 error_start,
2006 self.offset + err_bytes.len(),
2007 );
2008 self.items.push(token);
2009 self.offset += err_bytes.len();
2010 self.start_token();
2011 return Ok(());
2012 }
2013 }
2014 }
2015
2016 Err(TokenizerError {
2017 message: format!("Invalid error code at position {}", self.offset),
2018 pos: self.offset,
2019 })
2020 }
2021
2022 fn parse_whitespace(&mut self) -> Result<(), TokenizerError> {
2024 self.save_token();
2025
2026 let ws_start = self.offset;
2027 let mut contains_intersection_space = false;
2028 while self.offset < self.formula.len() {
2029 match self.formula.as_bytes()[self.offset] {
2030 b' ' => {
2031 contains_intersection_space = true;
2032 self.offset += 1;
2033 }
2034 b'\t' | b'\r' | b'\n' => self.offset += 1,
2035 _ => break,
2036 }
2037 }
2038
2039 let token_type = if contains_intersection_space
2042 && self.prev_is_reference_producing()
2043 && next_starts_reference_expression(&self.formula, self.offset)
2044 {
2045 TokenType::OpInfix
2046 } else {
2047 TokenType::Whitespace
2048 };
2049
2050 let token = if token_type == TokenType::OpInfix {
2051 Token::new_with_span(
2052 " ".to_string(),
2053 token_type,
2054 TokenSubType::None,
2055 ws_start,
2056 self.offset,
2057 )
2058 } else {
2059 Token::from_slice(
2060 &self.formula,
2061 token_type,
2062 TokenSubType::None,
2063 ws_start,
2064 self.offset,
2065 )
2066 };
2067 self.items.push(token);
2068 self.start_token();
2069 Ok(())
2070 }
2071
2072 fn prev_non_whitespace(&self) -> Option<&Token> {
2073 self.items
2074 .iter()
2075 .rev()
2076 .find(|t| t.token_type != TokenType::Whitespace)
2077 }
2078
2079 fn prev_is_reference_producing(&self) -> bool {
2080 match self.prev_non_whitespace() {
2081 Some(prev) => match prev.token_type {
2082 TokenType::OpPostfix => true,
2083 TokenType::Paren | TokenType::Func | TokenType::Array
2084 if prev.subtype == TokenSubType::Close =>
2085 {
2086 true
2087 }
2088 TokenType::Operand if prev.subtype == TokenSubType::Range => {
2089 is_reference_operand_value(&prev.value)
2090 }
2091 _ => false,
2092 },
2093 None => false,
2094 }
2095 }
2096
2097 fn should_emit_colon_infix(&self) -> bool {
2098 if self.has_token() {
2099 let value = &self.formula[self.token_start..self.token_end];
2100 if value.ends_with('!') {
2101 return false;
2102 }
2103 return reference_value_contains_range_colon(value)
2104 || value_has_structured_reference_bracket(value)
2105 || (value.contains('!')
2106 && next_reference_has_sheet_qualifier(&self.formula, self.offset + 1));
2107 }
2108 self.prev_is_reference_producing()
2109 }
2110
2111 fn emit_infix_operator(&mut self, start: usize, end: usize) {
2112 self.save_token();
2113 self.start_token();
2114 self.items.push(Token::from_slice(
2115 &self.formula,
2116 TokenType::OpInfix,
2117 TokenSubType::None,
2118 start,
2119 end,
2120 ));
2121 self.offset = end;
2122 self.start_token();
2123 }
2124
2125 fn parse_operator(&mut self) -> Result<(), TokenizerError> {
2127 self.save_token();
2128
2129 if self.offset + 1 < self.formula.len() {
2131 let two_char = &self.formula.as_bytes()[self.offset..self.offset + 2];
2132 if two_char == b">=" || two_char == b"<=" || two_char == b"<>" {
2133 self.items.push(Token::from_slice(
2134 &self.formula,
2135 TokenType::OpInfix,
2136 TokenSubType::None,
2137 self.offset,
2138 self.offset + 2,
2139 ));
2140 self.offset += 2;
2141 self.start_token();
2142 return Ok(());
2143 }
2144 }
2145
2146 let curr_byte = self.formula.as_bytes()[self.offset];
2147 let token_type = match curr_byte {
2148 b'@' => TokenType::OpPrefix,
2149 b'%' => TokenType::OpPostfix,
2150 b'+' | b'-' => {
2151 if self.items.is_empty() {
2153 TokenType::OpPrefix
2154 } else {
2155 let prev = self
2156 .items
2157 .iter()
2158 .rev()
2159 .find(|t| t.token_type != TokenType::Whitespace);
2160 if let Some(p) = prev {
2161 if p.subtype == TokenSubType::Close
2162 || p.token_type == TokenType::OpPostfix
2163 || p.token_type == TokenType::Operand
2164 {
2165 TokenType::OpInfix
2166 } else {
2167 TokenType::OpPrefix
2168 }
2169 } else {
2170 TokenType::OpPrefix
2171 }
2172 }
2173 }
2174 _ => TokenType::OpInfix,
2175 };
2176
2177 self.items.push(Token::from_slice(
2178 &self.formula,
2179 token_type,
2180 TokenSubType::None,
2181 self.offset,
2182 self.offset + 1,
2183 ));
2184 self.offset += 1;
2185 self.start_token();
2186 Ok(())
2187 }
2188
2189 fn parse_opener(&mut self) -> Result<(), TokenizerError> {
2191 let curr_byte = self.formula.as_bytes()[self.offset];
2192 assert!(curr_byte == b'(' || curr_byte == b'{');
2193
2194 let token = if curr_byte == b'{' {
2195 self.save_token();
2196 Token::make_subexp_from_slice(&self.formula, false, self.offset, self.offset + 1)
2197 } else if self.has_token() {
2198 let token = Token::make_subexp_from_slice(
2200 &self.formula,
2201 true,
2202 self.token_start,
2203 self.offset + 1,
2204 );
2205 self.token_start = self.offset + 1;
2206 self.token_end = self.offset + 1;
2207 token
2208 } else {
2209 Token::make_subexp_from_slice(&self.formula, false, self.offset, self.offset + 1)
2210 };
2211
2212 self.items.push(token.clone());
2213 self.token_stack.push(token);
2214 self.offset += 1;
2215 self.start_token();
2216 Ok(())
2217 }
2218
2219 fn parse_closer(&mut self) -> Result<(), TokenizerError> {
2221 self.save_token();
2222
2223 let curr_byte = self.formula.as_bytes()[self.offset];
2224 assert!(curr_byte == b')' || curr_byte == b'}');
2225
2226 if let Some(open_token) = self.token_stack.pop() {
2227 let closer = open_token.get_closer()?;
2228 if (curr_byte == b'}' && closer.value != "}")
2229 || (curr_byte == b')' && closer.value != ")")
2230 {
2231 return Err(TokenizerError {
2232 message: "Mismatched ( and { pair".to_string(),
2233 pos: self.offset,
2234 });
2235 }
2236
2237 self.items.push(Token::from_slice(
2238 &self.formula,
2239 closer.token_type,
2240 TokenSubType::Close,
2241 self.offset,
2242 self.offset + 1,
2243 ));
2244 } else {
2245 return Err(TokenizerError {
2246 message: format!("No matching opener for closer at position {}", self.offset),
2247 pos: self.offset,
2248 });
2249 }
2250
2251 self.offset += 1;
2252 self.start_token();
2253 Ok(())
2254 }
2255
2256 fn parse_separator(&mut self) -> Result<(), TokenizerError> {
2258 self.save_token();
2259
2260 let curr_byte = self.formula.as_bytes()[self.offset];
2261 assert!(curr_byte == b';' || curr_byte == b',');
2262
2263 let top_token = self.token_stack.last();
2264 let in_function_or_array = matches!(
2265 top_token.map(|t| t.token_type),
2266 Some(TokenType::Func | TokenType::Array)
2267 );
2268 let in_array = matches!(top_token.map(|t| t.token_type), Some(TokenType::Array));
2269
2270 let (token_type, subtype) = match curr_byte {
2271 b',' => {
2272 if in_function_or_array {
2273 (TokenType::Sep, TokenSubType::Arg)
2274 } else {
2275 (TokenType::OpInfix, TokenSubType::None)
2276 }
2277 }
2278 b';' => {
2279 if in_array {
2280 (TokenType::Sep, TokenSubType::Row)
2282 } else if self.dialect == FormulaDialect::OpenFormula && in_function_or_array {
2283 (TokenType::Sep, TokenSubType::Arg)
2285 } else if self.dialect == FormulaDialect::OpenFormula {
2286 (TokenType::OpInfix, TokenSubType::None)
2287 } else {
2288 (TokenType::Sep, TokenSubType::Row)
2289 }
2290 }
2291 _ => (TokenType::OpInfix, TokenSubType::None),
2292 };
2293
2294 self.items.push(Token::from_slice(
2295 &self.formula,
2296 token_type,
2297 subtype,
2298 self.offset,
2299 self.offset + 1,
2300 ));
2301
2302 self.offset += 1;
2303 self.start_token();
2304 Ok(())
2305 }
2306
2307 pub fn render(&self) -> String {
2309 if self.items.is_empty() {
2310 "".to_string()
2311 } else if self.items[0].token_type == TokenType::Literal {
2312 self.items[0].value.clone()
2313 } else {
2314 let concatenated: String = self.items.iter().map(|t| t.value.clone()).collect();
2315 format!("={concatenated}")
2316 }
2317 }
2318
2319 pub fn dialect(&self) -> FormulaDialect {
2321 self.dialect
2322 }
2323}
2324
2325impl TryFrom<&str> for Tokenizer {
2326 type Error = TokenizerError;
2327
2328 fn try_from(value: &str) -> Result<Self, Self::Error> {
2329 Tokenizer::new(value)
2330 }
2331}
2332
2333impl TryFrom<String> for Tokenizer {
2334 type Error = TokenizerError;
2335
2336 fn try_from(value: String) -> Result<Self, Self::Error> {
2337 Tokenizer::new(&value)
2338 }
2339}