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 is_reference_operand_value(value: &str) -> bool {
674 operand_subtype(value) == TokenSubType::Range
675 && (reference_value_contains_range_colon(value)
676 || value.contains('!')
677 || value.contains('[')
678 || is_cell_reference_like(value))
679}
680
681fn next_starts_reference_expression(formula: &str, mut offset: usize) -> bool {
682 let bytes = formula.as_bytes();
683 while offset < bytes.len() && matches!(bytes[offset], b' ' | b'\t' | b'\r' | b'\n') {
684 offset += 1;
685 }
686 if offset >= bytes.len() {
687 return false;
688 }
689
690 matches!(bytes[offset], b'(' | b'[' | b'\'' | b'$') || bytes[offset].is_ascii_alphabetic()
691}
692
693fn next_reference_has_sheet_qualifier(formula: &str, mut offset: usize) -> bool {
694 let bytes = formula.as_bytes();
695 while offset < bytes.len() && matches!(bytes[offset], b' ' | b'\t' | b'\r' | b'\n') {
696 offset += 1;
697 }
698
699 let mut in_quote = false;
700 while offset < bytes.len() {
701 match bytes[offset] {
702 b'\'' => {
703 if in_quote && offset + 1 < bytes.len() && bytes[offset + 1] == b'\'' {
704 offset += 2;
705 continue;
706 }
707 in_quote = !in_quote;
708 }
709 b'!' => return true,
710 b':' if !in_quote => return false,
711 b',' | b';' | b'}' | b')' | b' ' | b'\t' | b'\r' | b'\n' | b'+' | b'-' | b'*'
712 | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@'
713 if !in_quote =>
714 {
715 return false;
716 }
717 _ => {}
718 }
719 offset += 1;
720 }
721
722 false
723}
724
725struct SpanTokenizer<'a> {
726 formula: &'a str,
727 spans: Vec<TokenSpan>,
728 token_stack: Vec<TokenSpan>,
729 offset: usize,
730 token_start: usize,
731 token_end: usize,
732 dialect: FormulaDialect,
733 diagnostics: Vec<TokenDiagnostic>,
734}
735
736impl<'a> SpanTokenizer<'a> {
737 fn new(formula: &'a str, dialect: FormulaDialect) -> Self {
738 SpanTokenizer {
739 formula,
740 spans: Vec::with_capacity(formula.len() / 2),
741 token_stack: Vec::with_capacity(16),
742 offset: 0,
743 token_start: 0,
744 token_end: 0,
745 dialect,
746 diagnostics: Vec::new(),
747 }
748 }
749
750 #[inline]
751 fn current_byte(&self) -> Option<u8> {
752 self.formula.as_bytes().get(self.offset).copied()
753 }
754
755 #[inline]
756 fn has_token(&self) -> bool {
757 self.token_end > self.token_start
758 }
759
760 #[inline]
761 fn start_token(&mut self) {
762 self.token_start = self.offset;
763 self.token_end = self.offset;
764 }
765
766 #[inline]
767 fn extend_token(&mut self) {
768 self.token_end = self.offset;
769 }
770
771 fn push_span(
772 &mut self,
773 token_type: TokenType,
774 subtype: TokenSubType,
775 start: usize,
776 end: usize,
777 ) {
778 self.spans.push(TokenSpan {
779 token_type,
780 subtype,
781 start,
782 end,
783 });
784 }
785
786 fn save_token(&mut self) {
787 if self.has_token() {
788 let value_str = &self.formula[self.token_start..self.token_end];
789 let subtype = operand_subtype(value_str);
790 self.push_span(
791 TokenType::Operand,
792 subtype,
793 self.token_start,
794 self.token_end,
795 );
796 }
797 }
798
799 fn check_scientific_notation(&mut self) -> bool {
800 if let Some(curr_byte) = self.current_byte() {
801 if (curr_byte == b'+' || curr_byte == b'-')
802 && self.has_token()
803 && self.is_scientific_notation_base()
804 && self
805 .formula
806 .as_bytes()
807 .get(self.offset + 1)
808 .is_some_and(|b| b.is_ascii_digit())
809 {
810 self.offset += 1;
811 self.extend_token();
812 return true;
813 }
814 }
815 false
816 }
817
818 fn is_scientific_notation_base(&self) -> bool {
819 if !self.has_token() {
820 return false;
821 }
822
823 let token_slice = &self.formula.as_bytes()[self.token_start..self.token_end];
824 if token_slice.len() < 2 {
825 return false;
826 }
827
828 let last = token_slice[token_slice.len() - 1];
829 if !(last == b'E' || last == b'e') {
830 return false;
831 }
832
833 let first = token_slice[0];
834 if !first.is_ascii_digit() {
835 return false;
836 }
837
838 let mut dot_seen = false;
839 for &ch in &token_slice[1..token_slice.len() - 1] {
840 match ch {
841 b'0'..=b'9' => {}
842 b'.' if !dot_seen => dot_seen = true,
843 _ => return false,
844 }
845 }
846 true
847 }
848
849 fn parse(&mut self) -> Result<(), TokenizerError> {
850 self.parse_with_recovery(false).map_err(Into::into)
851 }
852
853 pub(crate) fn parse_best_effort(&mut self) -> Vec<TokenSpan> {
854 let _ = self.parse_with_recovery(true);
855 self.spans.clone()
856 }
857
858 fn parse_with_recovery(&mut self, best_effort: bool) -> Result<(), SpanTokenizerError> {
859 if self.formula.is_empty() {
860 return Ok(());
861 }
862
863 if self.formula.as_bytes()[0] != b'=' {
864 self.push_span(
865 TokenType::Literal,
866 TokenSubType::None,
867 0,
868 self.formula.len(),
869 );
870 return Ok(());
871 }
872
873 self.offset = 1;
874 self.start_token();
875
876 while self.offset < self.formula.len() {
877 if self.check_scientific_notation() {
878 continue;
879 }
880
881 let curr_byte = self.formula.as_bytes()[self.offset];
882
883 if is_token_ender(curr_byte) && self.has_token() {
884 self.save_token();
885 self.start_token();
886 }
887
888 let parse_result = match curr_byte {
889 b'"' | b'\'' => self.parse_string(),
890 b'[' => self.parse_brackets(),
891 b'#' => {
892 if self.should_emit_hash_postfix() {
893 self.emit_hash_postfix();
894 Ok(())
895 } else {
896 self.parse_error()
897 }
898 }
899 b' ' | b'\t' | b'\r' | b'\n' => self.parse_whitespace(),
900 b':' => {
901 if self.should_emit_colon_infix() {
902 self.emit_infix_operator(self.offset, self.offset + 1);
903 Ok(())
904 } else {
905 if !self.has_token() {
906 self.start_token();
907 }
908 self.offset += 1;
909 self.extend_token();
910 Ok(())
911 }
912 }
913 b'+' | b'-' | b'*' | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@' => {
914 self.parse_operator()
915 }
916 b'{' | b'(' => self.parse_opener(),
917 b')' | b'}' => self.parse_closer(),
918 b';' | b',' => self.parse_separator(),
919 _ => {
920 if !self.has_token() {
921 self.start_token();
922 }
923 self.offset += 1;
924 self.extend_token();
925 Ok(())
926 }
927 };
928
929 if let Err(err) = parse_result {
930 if best_effort {
931 self.recover_from_error(err);
932 } else {
933 return Err(err);
934 }
935 }
936 }
937
938 if self.has_token() {
939 self.save_token();
940 }
941
942 if !self.token_stack.is_empty() {
943 if best_effort {
944 while let Some(open_token) = self.token_stack.pop() {
945 if let Some(span) = self.spans.iter().find(|span| {
946 span.start == open_token.start
947 && span.end == open_token.end
948 && span.token_type == open_token.token_type
949 && span.subtype == open_token.subtype
950 }) {
951 self.diagnostics.push(TokenDiagnostic::new(
952 *span,
953 "Unmatched opening parenthesis or bracket".to_string(),
954 RecoveryAction::UnmatchedOpener,
955 ));
956 }
957 }
958 } else {
959 return Err(SpanTokenizerError {
960 kind: SpanTokenizerErrorKind::UnmatchedOpening,
961 pos: self.offset,
962 message: "Unmatched opening parenthesis or bracket".to_string(),
963 span_start: None,
964 span_end: None,
965 });
966 }
967 }
968
969 Ok(())
970 }
971
972 fn recover_from_error(&mut self, error: SpanTokenizerError) {
973 match error.kind {
974 SpanTokenizerErrorKind::NoMatchingOpener => {
975 let span = TokenSpan {
976 token_type: TokenType::Operand,
977 subtype: TokenSubType::None,
978 start: error.pos,
979 end: error.pos + 1,
980 };
981 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
982 self.offset = span.end;
983 self.start_token();
984 self.diagnostics.push(TokenDiagnostic::new(
985 span,
986 format!("No matching opener for closer at position {}", error.pos),
987 RecoveryAction::SkippedUnmatchedCloser,
988 ));
989 }
990 SpanTokenizerErrorKind::UnmatchedOpening => {
991 debug_assert!(
992 false,
993 "UnmatchedOpening is handled at end-of-input and should not be routed through recover_from_error"
994 );
995 }
996 SpanTokenizerErrorKind::UnterminatedString => {
997 let start = error.span_start.unwrap_or(error.pos);
998 let span = TokenSpan {
999 token_type: TokenType::Operand,
1000 subtype: TokenSubType::None,
1001 start,
1002 end: self.formula.len(),
1003 };
1004 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1005 self.offset = span.end;
1006 self.start_token();
1007 self.diagnostics.push(TokenDiagnostic::new(
1008 span,
1009 "Reached end of formula while parsing string".to_string(),
1010 RecoveryAction::UnterminatedString,
1011 ));
1012 }
1013 SpanTokenizerErrorKind::UnmatchedBracket => {
1014 let start = error.span_start.unwrap_or(error.pos);
1015 let end = error.span_end.unwrap_or(self.formula.len());
1016 let span = TokenSpan {
1017 token_type: TokenType::Operand,
1018 subtype: TokenSubType::None,
1019 start,
1020 end,
1021 };
1022 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1023 self.offset = span.end;
1024 self.start_token();
1025 self.diagnostics.push(TokenDiagnostic::new(
1026 span,
1027 "Encountered unmatched '['".to_string(),
1028 RecoveryAction::UnmatchedBracket,
1029 ));
1030 }
1031 SpanTokenizerErrorKind::MismatchedPair => {
1032 let span = TokenSpan {
1033 token_type: TokenType::Operand,
1034 subtype: TokenSubType::None,
1035 start: error.pos,
1036 end: error.pos + 1,
1037 };
1038 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1039 self.offset = span.end;
1040 self.start_token();
1041 self.diagnostics.push(TokenDiagnostic::new(
1042 span,
1043 "Mismatched ( and { pair".to_string(),
1044 RecoveryAction::SkippedUnmatchedCloser,
1045 ));
1046 }
1047 SpanTokenizerErrorKind::InvalidErrorLiteral => {
1048 let start = error.span_start.unwrap_or(error.pos);
1049 let end = error.span_end.unwrap_or(error.pos + 1);
1050 let span = TokenSpan {
1051 token_type: TokenType::Operand,
1052 subtype: TokenSubType::None,
1053 start,
1054 end,
1055 };
1056 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1057 self.offset = span.end;
1058 self.start_token();
1059 self.diagnostics.push(TokenDiagnostic::new(
1060 span,
1061 "Invalid error code".to_string(),
1062 RecoveryAction::InvalidErrorLiteral,
1063 ));
1064 }
1065 }
1066 }
1067
1068 fn parse_string(&mut self) -> Result<(), SpanTokenizerError> {
1069 let delim = self.formula.as_bytes()[self.offset];
1070 assert!(delim == b'"' || delim == b'\'');
1071
1072 let is_dollar_ref = delim == b'\''
1073 && self.has_token()
1074 && self.token_end - self.token_start == 1
1075 && self.formula.as_bytes()[self.token_start] == b'$';
1076
1077 let glue_to_token = delim == b'\''
1082 && self.has_token()
1083 && self.token_end > 0
1084 && self.formula.as_bytes()[self.token_end - 1] == b':';
1085
1086 if !is_dollar_ref && !glue_to_token && self.has_token() {
1087 self.save_token();
1088 self.start_token();
1089 }
1090
1091 let string_start = if is_dollar_ref {
1092 self.token_start
1093 } else {
1094 self.offset
1095 };
1096 self.offset += 1;
1097
1098 while self.offset < self.formula.len() {
1099 if self.formula.as_bytes()[self.offset] == delim {
1100 self.offset += 1;
1101 if self.offset < self.formula.len() && self.formula.as_bytes()[self.offset] == delim
1102 {
1103 self.offset += 1;
1104 } else {
1105 if delim == b'"' {
1106 let value_str = &self.formula[string_start..self.offset];
1107 let subtype = operand_subtype(value_str);
1108 self.push_span(TokenType::Operand, subtype, string_start, self.offset);
1109 self.start_token();
1110 } else {
1111 self.token_end = self.offset;
1112 }
1113 return Ok(());
1114 }
1115 } else {
1116 self.offset += 1;
1117 }
1118 }
1119
1120 Err(SpanTokenizerError {
1121 kind: SpanTokenizerErrorKind::UnterminatedString,
1122 pos: self.offset,
1123 message: "Reached end of formula while parsing string".to_string(),
1124 span_start: Some(string_start),
1125 span_end: Some(self.formula.len()),
1126 })
1127 }
1128
1129 fn parse_brackets(&mut self) -> Result<(), SpanTokenizerError> {
1130 assert_eq!(self.formula.as_bytes()[self.offset], b'[');
1131
1132 if !self.has_token() {
1133 self.start_token();
1134 }
1135
1136 let bracket_start = self.offset;
1137 let mut open_count = 1;
1138 self.offset += 1;
1139
1140 while self.offset < self.formula.len() {
1141 match self.formula.as_bytes()[self.offset] {
1142 b'\'' => {
1147 if self.offset + 1 < self.formula.len() {
1148 self.offset += 2;
1149 continue;
1150 }
1151 self.offset += 1;
1154 continue;
1155 }
1156 b'[' => open_count += 1,
1157 b']' => {
1158 open_count -= 1;
1159 if open_count == 0 {
1160 self.offset += 1;
1161 self.extend_token();
1162 return Ok(());
1163 }
1164 }
1165 _ => {}
1166 }
1167 self.offset += 1;
1168 }
1169
1170 Err(SpanTokenizerError {
1171 kind: SpanTokenizerErrorKind::UnmatchedBracket,
1172 pos: self.offset,
1173 message: "Encountered unmatched '['".to_string(),
1174 span_start: Some(bracket_start),
1175 span_end: Some(self.formula.len()),
1176 })
1177 }
1178
1179 fn parse_error(&mut self) -> Result<(), SpanTokenizerError> {
1180 let has_sheet_prefix = self.has_token()
1186 && self.token_end > 0
1187 && self.formula.as_bytes()[self.token_end - 1] == b'!';
1188 if has_sheet_prefix {
1189 if self.token_end - self.token_start <= 1 {
1190 return Err(SpanTokenizerError {
1191 kind: SpanTokenizerErrorKind::InvalidErrorLiteral,
1192 pos: self.offset,
1193 message: format!(
1194 "Empty sheet qualifier before error literal at position {}",
1195 self.offset
1196 ),
1197 span_start: Some(self.token_start),
1198 span_end: Some(self.offset),
1199 });
1200 }
1201 self.start_token();
1203 } else if self.has_token() {
1204 self.save_token();
1205 self.start_token();
1206 }
1207
1208 let error_start = self.offset;
1209
1210 for &err_code in ERROR_CODES {
1211 let err_bytes = err_code.as_bytes();
1212 if self.offset + err_bytes.len() <= self.formula.len() {
1213 let slice = &self.formula.as_bytes()[self.offset..self.offset + err_bytes.len()];
1214 if slice.eq_ignore_ascii_case(err_bytes) {
1215 self.push_span(
1216 TokenType::Operand,
1217 TokenSubType::Error,
1218 error_start,
1219 self.offset + err_bytes.len(),
1220 );
1221 self.offset += err_bytes.len();
1222 self.start_token();
1223 return Ok(());
1224 }
1225 }
1226 }
1227
1228 let mut end = self.offset + 1;
1229 while end < self.formula.len() {
1230 let ch = self.formula.as_bytes()[end];
1231 if is_token_ender(ch)
1232 || ch == b' '
1233 || ch == b'\t'
1234 || ch == b'\r'
1235 || ch == b'\n'
1236 || ch == b'('
1237 || ch == b'{'
1238 || ch == b'['
1239 || ch == b'"'
1240 || ch == b'\''
1241 {
1242 break;
1243 }
1244 end += 1;
1245 }
1246
1247 Err(SpanTokenizerError {
1248 kind: SpanTokenizerErrorKind::InvalidErrorLiteral,
1249 pos: self.offset,
1250 message: format!("Invalid error code at position {}", self.offset),
1251 span_start: Some(error_start),
1252 span_end: Some(end),
1253 })
1254 }
1255
1256 fn parse_whitespace(&mut self) -> Result<(), SpanTokenizerError> {
1257 self.save_token();
1258
1259 let ws_start = self.offset;
1260 let mut contains_intersection_space = false;
1261 while self.offset < self.formula.len() {
1262 match self.formula.as_bytes()[self.offset] {
1263 b' ' => {
1264 contains_intersection_space = true;
1265 self.offset += 1;
1266 }
1267 b'\t' | b'\r' | b'\n' => self.offset += 1,
1268 _ => break,
1269 }
1270 }
1271
1272 let token_type = if contains_intersection_space
1275 && self.prev_is_reference_producing()
1276 && next_starts_reference_expression(self.formula, self.offset)
1277 {
1278 TokenType::OpInfix
1279 } else {
1280 TokenType::Whitespace
1281 };
1282 self.push_span(token_type, TokenSubType::None, ws_start, self.offset);
1283 self.start_token();
1284 Ok(())
1285 }
1286
1287 fn prev_is_reference_producing(&self) -> bool {
1288 match self.prev_non_whitespace() {
1289 Some(prev) => match prev.token_type {
1290 TokenType::OpPostfix => true,
1291 TokenType::Paren | TokenType::Func | TokenType::Array
1292 if prev.subtype == TokenSubType::Close =>
1293 {
1294 true
1295 }
1296 TokenType::Operand if prev.subtype == TokenSubType::Range => self
1297 .formula
1298 .get(prev.start..prev.end)
1299 .is_some_and(is_reference_operand_value),
1300 _ => false,
1301 },
1302 None => false,
1303 }
1304 }
1305
1306 fn should_emit_colon_infix(&self) -> bool {
1307 if self.has_token() {
1308 let value = &self.formula[self.token_start..self.token_end];
1309 if value.ends_with('!') {
1310 return false;
1311 }
1312 return reference_value_contains_range_colon(value)
1313 || value.contains('[')
1314 || (value.contains('!')
1315 && next_reference_has_sheet_qualifier(self.formula, self.offset + 1));
1316 }
1317 self.prev_is_reference_producing()
1318 }
1319
1320 fn emit_infix_operator(&mut self, start: usize, end: usize) {
1321 self.save_token();
1322 self.start_token();
1323 self.push_span(TokenType::OpInfix, TokenSubType::None, start, end);
1324 self.offset = end;
1325 self.start_token();
1326 }
1327
1328 fn prev_non_whitespace(&self) -> Option<&TokenSpan> {
1329 self.spans
1330 .iter()
1331 .rev()
1332 .find(|t| t.token_type != TokenType::Whitespace)
1333 }
1334
1335 fn should_emit_hash_postfix(&self) -> bool {
1340 if self.has_token() {
1341 if self.formula.as_bytes()[self.token_end - 1] == b'!' {
1345 return false;
1346 }
1347 let value = &self.formula[self.token_start..self.token_end];
1348 return operand_subtype(value) == TokenSubType::Range;
1349 }
1350 match self.prev_non_whitespace() {
1351 Some(prev) => match prev.token_type {
1352 TokenType::OpPostfix => true,
1353 TokenType::Paren | TokenType::Func | TokenType::Array
1354 if prev.subtype == TokenSubType::Close =>
1355 {
1356 true
1357 }
1358 TokenType::Operand if prev.subtype == TokenSubType::Range => true,
1359 _ => false,
1360 },
1361 None => false,
1362 }
1363 }
1364
1365 fn emit_hash_postfix(&mut self) {
1366 self.save_token();
1367 self.start_token();
1368 self.push_span(
1369 TokenType::OpPostfix,
1370 TokenSubType::None,
1371 self.offset,
1372 self.offset + 1,
1373 );
1374 self.offset += 1;
1375 self.start_token();
1376 }
1377
1378 fn parse_operator(&mut self) -> Result<(), SpanTokenizerError> {
1379 self.save_token();
1380
1381 if self.offset + 1 < self.formula.len() {
1382 let two_char = &self.formula.as_bytes()[self.offset..self.offset + 2];
1383 if two_char == b">=" || two_char == b"<=" || two_char == b"<>" {
1384 self.push_span(
1385 TokenType::OpInfix,
1386 TokenSubType::None,
1387 self.offset,
1388 self.offset + 2,
1389 );
1390 self.offset += 2;
1391 self.start_token();
1392 return Ok(());
1393 }
1394 }
1395
1396 let curr_byte = self.formula.as_bytes()[self.offset];
1397 let token_type = match curr_byte {
1398 b'@' => TokenType::OpPrefix,
1399 b'%' => TokenType::OpPostfix,
1400 b'+' | b'-' => {
1401 if self.spans.is_empty() {
1402 TokenType::OpPrefix
1403 } else {
1404 let prev = self.prev_non_whitespace();
1405 if let Some(p) = prev {
1406 if p.subtype == TokenSubType::Close
1407 || p.token_type == TokenType::OpPostfix
1408 || p.token_type == TokenType::Operand
1409 {
1410 TokenType::OpInfix
1411 } else {
1412 TokenType::OpPrefix
1413 }
1414 } else {
1415 TokenType::OpPrefix
1416 }
1417 }
1418 }
1419 _ => TokenType::OpInfix,
1420 };
1421
1422 self.push_span(token_type, TokenSubType::None, self.offset, self.offset + 1);
1423 self.offset += 1;
1424 self.start_token();
1425 Ok(())
1426 }
1427
1428 fn parse_opener(&mut self) -> Result<(), SpanTokenizerError> {
1429 let curr_byte = self.formula.as_bytes()[self.offset];
1430 assert!(curr_byte == b'(' || curr_byte == b'{');
1431
1432 let token = if curr_byte == b'{' {
1433 self.save_token();
1434 TokenSpan {
1435 token_type: TokenType::Array,
1436 subtype: TokenSubType::Open,
1437 start: self.offset,
1438 end: self.offset + 1,
1439 }
1440 } else if self.has_token() {
1441 let token = TokenSpan {
1442 token_type: TokenType::Func,
1443 subtype: TokenSubType::Open,
1444 start: self.token_start,
1445 end: self.offset + 1,
1446 };
1447 self.token_start = self.offset + 1;
1448 self.token_end = self.offset + 1;
1449 token
1450 } else {
1451 TokenSpan {
1452 token_type: TokenType::Paren,
1453 subtype: TokenSubType::Open,
1454 start: self.offset,
1455 end: self.offset + 1,
1456 }
1457 };
1458
1459 self.spans.push(token);
1460 self.token_stack.push(token);
1461 self.offset += 1;
1462 self.start_token();
1463 Ok(())
1464 }
1465
1466 fn parse_closer(&mut self) -> Result<(), SpanTokenizerError> {
1467 self.save_token();
1468
1469 let curr_byte = self.formula.as_bytes()[self.offset];
1470 assert!(curr_byte == b')' || curr_byte == b'}');
1471
1472 if let Some(open_token) = self.token_stack.last().copied() {
1473 let expected = if open_token.token_type == TokenType::Array {
1474 b'}'
1475 } else {
1476 b')'
1477 };
1478 if curr_byte != expected {
1479 return Err(SpanTokenizerError {
1480 kind: SpanTokenizerErrorKind::MismatchedPair,
1481 pos: self.offset,
1482 message: "Mismatched ( and { pair".to_string(),
1483 span_start: Some(self.offset),
1484 span_end: Some(self.offset + 1),
1485 });
1486 }
1487
1488 self.token_stack.pop();
1489 self.push_span(
1490 open_token.token_type,
1491 TokenSubType::Close,
1492 self.offset,
1493 self.offset + 1,
1494 );
1495 } else {
1496 return Err(SpanTokenizerError {
1497 kind: SpanTokenizerErrorKind::NoMatchingOpener,
1498 pos: self.offset,
1499 message: format!("No matching opener for closer at position {}", self.offset),
1500 span_start: Some(self.offset),
1501 span_end: Some(self.offset + 1),
1502 });
1503 }
1504
1505 self.offset += 1;
1506 self.start_token();
1507 Ok(())
1508 }
1509
1510 fn parse_separator(&mut self) -> Result<(), SpanTokenizerError> {
1511 self.save_token();
1512
1513 let curr_byte = self.formula.as_bytes()[self.offset];
1514 assert!(curr_byte == b';' || curr_byte == b',');
1515
1516 let top_token = self.token_stack.last();
1517 let in_function_or_array = matches!(
1518 top_token.map(|t| t.token_type),
1519 Some(TokenType::Func | TokenType::Array)
1520 );
1521 let in_array = matches!(top_token.map(|t| t.token_type), Some(TokenType::Array));
1522
1523 let (token_type, subtype) = match curr_byte {
1524 b',' => {
1525 if in_function_or_array {
1526 (TokenType::Sep, TokenSubType::Arg)
1527 } else {
1528 (TokenType::OpInfix, TokenSubType::None)
1529 }
1530 }
1531 b';' => {
1532 if in_array {
1533 (TokenType::Sep, TokenSubType::Row)
1534 } else if self.dialect == FormulaDialect::OpenFormula && in_function_or_array {
1535 (TokenType::Sep, TokenSubType::Arg)
1536 } else if self.dialect == FormulaDialect::OpenFormula {
1537 (TokenType::OpInfix, TokenSubType::None)
1538 } else {
1539 (TokenType::Sep, TokenSubType::Row)
1540 }
1541 }
1542 _ => (TokenType::OpInfix, TokenSubType::None),
1543 };
1544
1545 self.push_span(token_type, subtype, self.offset, self.offset + 1);
1546 self.offset += 1;
1547 self.start_token();
1548 Ok(())
1549 }
1550}
1551
1552pub struct Tokenizer {
1554 formula: String, pub items: Vec<Token>,
1556 token_stack: Vec<Token>,
1557 offset: usize, token_start: usize, token_end: usize, dialect: FormulaDialect,
1561}
1562
1563impl Tokenizer {
1564 pub fn new(formula: &str) -> Result<Self, TokenizerError> {
1566 Self::new_with_dialect(formula, FormulaDialect::Excel)
1567 }
1568
1569 pub fn new_best_effort(formula: &str) -> Self {
1571 Self::new_best_effort_with_dialect(formula, FormulaDialect::Excel)
1572 }
1573
1574 pub fn new_best_effort_with_dialect(formula: &str, dialect: FormulaDialect) -> Self {
1576 let stream = TokenStream::new_best_effort_with_dialect(formula, dialect);
1577 Self::from_token_stream(&stream)
1578 }
1579
1580 pub fn new_with_dialect(
1582 formula: &str,
1583 dialect: FormulaDialect,
1584 ) -> Result<Self, TokenizerError> {
1585 let mut tokenizer = Tokenizer {
1586 formula: formula.to_string(),
1587 items: Vec::with_capacity(formula.len() / 2), token_stack: Vec::with_capacity(16),
1589 offset: 0,
1590 token_start: 0,
1591 token_end: 0,
1592 dialect,
1593 };
1594 tokenizer.parse()?;
1595 Ok(tokenizer)
1596 }
1597
1598 pub fn from_token_stream(stream: &TokenStream) -> Self {
1599 Tokenizer {
1600 formula: stream.source.to_string(),
1601 items: stream.to_tokens(),
1602 token_stack: Vec::with_capacity(16),
1603 offset: 0,
1604 token_start: 0,
1605 token_end: 0,
1606 dialect: stream.dialect,
1607 }
1608 }
1609
1610 #[inline]
1612 fn current_byte(&self) -> Option<u8> {
1613 self.formula.as_bytes().get(self.offset).copied()
1614 }
1615
1616 #[inline]
1618 fn has_token(&self) -> bool {
1619 self.token_end > self.token_start
1620 }
1621
1622 #[inline]
1624 fn start_token(&mut self) {
1625 self.token_start = self.offset;
1626 self.token_end = self.offset;
1627 }
1628
1629 #[inline]
1631 fn extend_token(&mut self) {
1632 self.token_end = self.offset;
1633 }
1634
1635 fn parse(&mut self) -> Result<(), TokenizerError> {
1637 if self.formula.is_empty() {
1638 return Ok(());
1639 }
1640
1641 if self.formula.as_bytes()[0] != b'=' {
1643 self.items.push(Token::new_with_span(
1644 self.formula.clone(),
1645 TokenType::Literal,
1646 TokenSubType::None,
1647 0,
1648 self.formula.len(),
1649 ));
1650 return Ok(());
1651 }
1652
1653 self.offset = 1;
1655 self.start_token();
1656
1657 while self.offset < self.formula.len() {
1658 if self.check_scientific_notation()? {
1659 continue;
1660 }
1661
1662 let curr_byte = self.formula.as_bytes()[self.offset];
1663
1664 if is_token_ender(curr_byte) && self.has_token() {
1666 self.save_token();
1667 self.start_token();
1668 }
1669
1670 match curr_byte {
1672 b'"' | b'\'' => self.parse_string()?,
1673 b'[' => self.parse_brackets()?,
1674 b'#' => {
1675 if self.should_emit_hash_postfix() {
1676 self.emit_hash_postfix();
1677 } else {
1678 self.parse_error()?
1679 }
1680 }
1681 b' ' | b'\t' | b'\r' | b'\n' => self.parse_whitespace()?,
1682 b':' => {
1683 if self.should_emit_colon_infix() {
1684 self.emit_infix_operator(self.offset, self.offset + 1);
1685 } else {
1686 if !self.has_token() {
1687 self.start_token();
1688 }
1689 self.offset += 1;
1690 self.extend_token();
1691 }
1692 }
1693 b'+' | b'-' | b'*' | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@' => {
1695 self.parse_operator()?
1696 }
1697 b'{' | b'(' => self.parse_opener()?,
1698 b')' | b'}' => self.parse_closer()?,
1699 b';' | b',' => self.parse_separator()?,
1700 _ => {
1701 if !self.has_token() {
1703 self.start_token();
1704 }
1705 self.offset += 1;
1706 self.extend_token();
1707 }
1708 }
1709 }
1710
1711 if self.has_token() {
1713 self.save_token();
1714 }
1715
1716 if !self.token_stack.is_empty() {
1718 return Err(TokenizerError {
1719 message: "Unmatched opening parenthesis or bracket".to_string(),
1720 pos: self.offset,
1721 });
1722 }
1723
1724 Ok(())
1725 }
1726
1727 fn check_scientific_notation(&mut self) -> Result<bool, TokenizerError> {
1735 if let Some(curr_byte) = self.current_byte() {
1736 if (curr_byte == b'+' || curr_byte == b'-')
1737 && self.has_token()
1738 && self.is_scientific_notation_base()
1739 && self
1740 .formula
1741 .as_bytes()
1742 .get(self.offset + 1)
1743 .is_some_and(|b| b.is_ascii_digit())
1744 {
1745 self.offset += 1;
1746 self.extend_token();
1747 return Ok(true);
1748 }
1749 }
1750 Ok(false)
1751 }
1752
1753 fn is_scientific_notation_base(&self) -> bool {
1756 if !self.has_token() {
1757 return false;
1758 }
1759
1760 let token_slice = &self.formula.as_bytes()[self.token_start..self.token_end];
1761 if token_slice.len() < 2 {
1762 return false;
1763 }
1764
1765 let last = token_slice[token_slice.len() - 1];
1766 if !(last == b'E' || last == b'e') {
1767 return false;
1768 }
1769
1770 let first = token_slice[0];
1771 if !first.is_ascii_digit() {
1772 return false;
1773 }
1774
1775 let mut dot_seen = false;
1776 for &ch in &token_slice[1..token_slice.len() - 1] {
1778 match ch {
1779 b'0'..=b'9' => {}
1780 b'.' if !dot_seen => dot_seen = true,
1781 _ => return false,
1782 }
1783 }
1784 true
1785 }
1786
1787 fn save_token(&mut self) {
1789 if self.has_token() {
1790 let token =
1791 Token::make_operand_from_slice(&self.formula, self.token_start, self.token_end);
1792 self.items.push(token);
1793 }
1794 }
1795
1796 fn parse_string(&mut self) -> Result<(), TokenizerError> {
1798 let delim = self.formula.as_bytes()[self.offset];
1799 assert!(delim == b'"' || delim == b'\'');
1800
1801 let is_dollar_ref = delim == b'\''
1803 && self.has_token()
1804 && self.token_end - self.token_start == 1
1805 && self.formula.as_bytes()[self.token_start] == b'$';
1806
1807 let glue_to_token = delim == b'\''
1812 && self.has_token()
1813 && self.token_end > 0
1814 && self.formula.as_bytes()[self.token_end - 1] == b':';
1815
1816 if !is_dollar_ref && !glue_to_token && self.has_token() {
1817 self.save_token();
1818 self.start_token();
1819 }
1820
1821 let string_start = if is_dollar_ref {
1822 self.token_start
1823 } else {
1824 self.offset
1825 };
1826 self.offset += 1; while self.offset < self.formula.len() {
1829 if self.formula.as_bytes()[self.offset] == delim {
1830 self.offset += 1;
1831 if self.offset < self.formula.len() && self.formula.as_bytes()[self.offset] == delim
1833 {
1834 self.offset += 1; } else {
1836 if delim == b'"' {
1838 let token = Token::make_operand_from_slice(
1839 &self.formula,
1840 string_start,
1841 self.offset,
1842 );
1843 self.items.push(token);
1844 self.start_token();
1845 } else {
1846 self.token_end = self.offset;
1848 }
1849 return Ok(());
1850 }
1851 } else {
1852 self.offset += 1;
1853 }
1854 }
1855
1856 Err(TokenizerError {
1857 message: "Reached end of formula while parsing string".to_string(),
1858 pos: self.offset,
1859 })
1860 }
1861
1862 fn parse_brackets(&mut self) -> Result<(), TokenizerError> {
1864 assert_eq!(self.formula.as_bytes()[self.offset], b'[');
1865
1866 if !self.has_token() {
1867 self.start_token();
1868 }
1869
1870 let mut open_count = 1;
1871 self.offset += 1;
1872
1873 while self.offset < self.formula.len() {
1874 match self.formula.as_bytes()[self.offset] {
1875 b'\'' => {
1880 if self.offset + 1 < self.formula.len() {
1881 self.offset += 2;
1882 continue;
1883 }
1884 self.offset += 1;
1885 continue;
1886 }
1887 b'[' => open_count += 1,
1888 b']' => {
1889 open_count -= 1;
1890 if open_count == 0 {
1891 self.offset += 1;
1892 self.extend_token();
1893 return Ok(());
1894 }
1895 }
1896 _ => {}
1897 }
1898 self.offset += 1;
1899 }
1900
1901 Err(TokenizerError {
1902 message: "Encountered unmatched '['".to_string(),
1903 pos: self.offset,
1904 })
1905 }
1906
1907 fn should_emit_hash_postfix(&self) -> bool {
1909 if self.has_token() {
1910 if self.formula.as_bytes()[self.token_end - 1] == b'!' {
1911 return false;
1912 }
1913 let value = &self.formula[self.token_start..self.token_end];
1914 let is_range = !value.starts_with('"')
1918 && !value.starts_with('#')
1919 && value != "TRUE"
1920 && value != "FALSE"
1921 && value.parse::<f64>().is_err();
1922 return is_range;
1923 }
1924 let prev = self
1925 .items
1926 .iter()
1927 .rev()
1928 .find(|t| t.token_type != TokenType::Whitespace);
1929 match prev {
1930 Some(p) => match p.token_type {
1931 TokenType::OpPostfix => true,
1932 TokenType::Paren | TokenType::Func | TokenType::Array
1933 if p.subtype == TokenSubType::Close =>
1934 {
1935 true
1936 }
1937 TokenType::Operand if p.subtype == TokenSubType::Range => true,
1938 _ => false,
1939 },
1940 None => false,
1941 }
1942 }
1943
1944 fn emit_hash_postfix(&mut self) {
1945 self.save_token();
1946 self.start_token();
1947 self.items.push(Token::from_slice(
1948 &self.formula,
1949 TokenType::OpPostfix,
1950 TokenSubType::None,
1951 self.offset,
1952 self.offset + 1,
1953 ));
1954 self.offset += 1;
1955 self.start_token();
1956 }
1957
1958 fn parse_error(&mut self) -> Result<(), TokenizerError> {
1960 let has_sheet_prefix = self.has_token()
1965 && self.token_end > 0
1966 && self.formula.as_bytes()[self.token_end - 1] == b'!';
1967 if has_sheet_prefix {
1968 if self.token_end - self.token_start <= 1 {
1969 return Err(TokenizerError {
1970 message: format!(
1971 "Empty sheet qualifier before error literal at position {}",
1972 self.offset
1973 ),
1974 pos: self.offset,
1975 });
1976 }
1977 self.start_token();
1979 } else if self.has_token() {
1980 self.save_token();
1981 self.start_token();
1982 }
1983
1984 let error_start = self.offset;
1985
1986 for &err_code in ERROR_CODES {
1988 let err_bytes = err_code.as_bytes();
1989 if self.offset + err_bytes.len() <= self.formula.len() {
1990 let slice = &self.formula.as_bytes()[self.offset..self.offset + err_bytes.len()];
1991 if slice.eq_ignore_ascii_case(err_bytes) {
1992 let token = Token::make_operand_from_slice(
1993 &self.formula,
1994 error_start,
1995 self.offset + err_bytes.len(),
1996 );
1997 self.items.push(token);
1998 self.offset += err_bytes.len();
1999 self.start_token();
2000 return Ok(());
2001 }
2002 }
2003 }
2004
2005 Err(TokenizerError {
2006 message: format!("Invalid error code at position {}", self.offset),
2007 pos: self.offset,
2008 })
2009 }
2010
2011 fn parse_whitespace(&mut self) -> Result<(), TokenizerError> {
2013 self.save_token();
2014
2015 let ws_start = self.offset;
2016 let mut contains_intersection_space = false;
2017 while self.offset < self.formula.len() {
2018 match self.formula.as_bytes()[self.offset] {
2019 b' ' => {
2020 contains_intersection_space = true;
2021 self.offset += 1;
2022 }
2023 b'\t' | b'\r' | b'\n' => self.offset += 1,
2024 _ => break,
2025 }
2026 }
2027
2028 let token_type = if contains_intersection_space
2031 && self.prev_is_reference_producing()
2032 && next_starts_reference_expression(&self.formula, self.offset)
2033 {
2034 TokenType::OpInfix
2035 } else {
2036 TokenType::Whitespace
2037 };
2038
2039 let token = if token_type == TokenType::OpInfix {
2040 Token::new_with_span(
2041 " ".to_string(),
2042 token_type,
2043 TokenSubType::None,
2044 ws_start,
2045 self.offset,
2046 )
2047 } else {
2048 Token::from_slice(
2049 &self.formula,
2050 token_type,
2051 TokenSubType::None,
2052 ws_start,
2053 self.offset,
2054 )
2055 };
2056 self.items.push(token);
2057 self.start_token();
2058 Ok(())
2059 }
2060
2061 fn prev_non_whitespace(&self) -> Option<&Token> {
2062 self.items
2063 .iter()
2064 .rev()
2065 .find(|t| t.token_type != TokenType::Whitespace)
2066 }
2067
2068 fn prev_is_reference_producing(&self) -> bool {
2069 match self.prev_non_whitespace() {
2070 Some(prev) => match prev.token_type {
2071 TokenType::OpPostfix => true,
2072 TokenType::Paren | TokenType::Func | TokenType::Array
2073 if prev.subtype == TokenSubType::Close =>
2074 {
2075 true
2076 }
2077 TokenType::Operand if prev.subtype == TokenSubType::Range => {
2078 is_reference_operand_value(&prev.value)
2079 }
2080 _ => false,
2081 },
2082 None => false,
2083 }
2084 }
2085
2086 fn should_emit_colon_infix(&self) -> bool {
2087 if self.has_token() {
2088 let value = &self.formula[self.token_start..self.token_end];
2089 if value.ends_with('!') {
2090 return false;
2091 }
2092 return reference_value_contains_range_colon(value)
2093 || value.contains('[')
2094 || (value.contains('!')
2095 && next_reference_has_sheet_qualifier(&self.formula, self.offset + 1));
2096 }
2097 self.prev_is_reference_producing()
2098 }
2099
2100 fn emit_infix_operator(&mut self, start: usize, end: usize) {
2101 self.save_token();
2102 self.start_token();
2103 self.items.push(Token::from_slice(
2104 &self.formula,
2105 TokenType::OpInfix,
2106 TokenSubType::None,
2107 start,
2108 end,
2109 ));
2110 self.offset = end;
2111 self.start_token();
2112 }
2113
2114 fn parse_operator(&mut self) -> Result<(), TokenizerError> {
2116 self.save_token();
2117
2118 if self.offset + 1 < self.formula.len() {
2120 let two_char = &self.formula.as_bytes()[self.offset..self.offset + 2];
2121 if two_char == b">=" || two_char == b"<=" || two_char == b"<>" {
2122 self.items.push(Token::from_slice(
2123 &self.formula,
2124 TokenType::OpInfix,
2125 TokenSubType::None,
2126 self.offset,
2127 self.offset + 2,
2128 ));
2129 self.offset += 2;
2130 self.start_token();
2131 return Ok(());
2132 }
2133 }
2134
2135 let curr_byte = self.formula.as_bytes()[self.offset];
2136 let token_type = match curr_byte {
2137 b'@' => TokenType::OpPrefix,
2138 b'%' => TokenType::OpPostfix,
2139 b'+' | b'-' => {
2140 if self.items.is_empty() {
2142 TokenType::OpPrefix
2143 } else {
2144 let prev = self
2145 .items
2146 .iter()
2147 .rev()
2148 .find(|t| t.token_type != TokenType::Whitespace);
2149 if let Some(p) = prev {
2150 if p.subtype == TokenSubType::Close
2151 || p.token_type == TokenType::OpPostfix
2152 || p.token_type == TokenType::Operand
2153 {
2154 TokenType::OpInfix
2155 } else {
2156 TokenType::OpPrefix
2157 }
2158 } else {
2159 TokenType::OpPrefix
2160 }
2161 }
2162 }
2163 _ => TokenType::OpInfix,
2164 };
2165
2166 self.items.push(Token::from_slice(
2167 &self.formula,
2168 token_type,
2169 TokenSubType::None,
2170 self.offset,
2171 self.offset + 1,
2172 ));
2173 self.offset += 1;
2174 self.start_token();
2175 Ok(())
2176 }
2177
2178 fn parse_opener(&mut self) -> Result<(), TokenizerError> {
2180 let curr_byte = self.formula.as_bytes()[self.offset];
2181 assert!(curr_byte == b'(' || curr_byte == b'{');
2182
2183 let token = if curr_byte == b'{' {
2184 self.save_token();
2185 Token::make_subexp_from_slice(&self.formula, false, self.offset, self.offset + 1)
2186 } else if self.has_token() {
2187 let token = Token::make_subexp_from_slice(
2189 &self.formula,
2190 true,
2191 self.token_start,
2192 self.offset + 1,
2193 );
2194 self.token_start = self.offset + 1;
2195 self.token_end = self.offset + 1;
2196 token
2197 } else {
2198 Token::make_subexp_from_slice(&self.formula, false, self.offset, self.offset + 1)
2199 };
2200
2201 self.items.push(token.clone());
2202 self.token_stack.push(token);
2203 self.offset += 1;
2204 self.start_token();
2205 Ok(())
2206 }
2207
2208 fn parse_closer(&mut self) -> Result<(), TokenizerError> {
2210 self.save_token();
2211
2212 let curr_byte = self.formula.as_bytes()[self.offset];
2213 assert!(curr_byte == b')' || curr_byte == b'}');
2214
2215 if let Some(open_token) = self.token_stack.pop() {
2216 let closer = open_token.get_closer()?;
2217 if (curr_byte == b'}' && closer.value != "}")
2218 || (curr_byte == b')' && closer.value != ")")
2219 {
2220 return Err(TokenizerError {
2221 message: "Mismatched ( and { pair".to_string(),
2222 pos: self.offset,
2223 });
2224 }
2225
2226 self.items.push(Token::from_slice(
2227 &self.formula,
2228 closer.token_type,
2229 TokenSubType::Close,
2230 self.offset,
2231 self.offset + 1,
2232 ));
2233 } else {
2234 return Err(TokenizerError {
2235 message: format!("No matching opener for closer at position {}", self.offset),
2236 pos: self.offset,
2237 });
2238 }
2239
2240 self.offset += 1;
2241 self.start_token();
2242 Ok(())
2243 }
2244
2245 fn parse_separator(&mut self) -> Result<(), TokenizerError> {
2247 self.save_token();
2248
2249 let curr_byte = self.formula.as_bytes()[self.offset];
2250 assert!(curr_byte == b';' || curr_byte == b',');
2251
2252 let top_token = self.token_stack.last();
2253 let in_function_or_array = matches!(
2254 top_token.map(|t| t.token_type),
2255 Some(TokenType::Func | TokenType::Array)
2256 );
2257 let in_array = matches!(top_token.map(|t| t.token_type), Some(TokenType::Array));
2258
2259 let (token_type, subtype) = match curr_byte {
2260 b',' => {
2261 if in_function_or_array {
2262 (TokenType::Sep, TokenSubType::Arg)
2263 } else {
2264 (TokenType::OpInfix, TokenSubType::None)
2265 }
2266 }
2267 b';' => {
2268 if in_array {
2269 (TokenType::Sep, TokenSubType::Row)
2271 } else if self.dialect == FormulaDialect::OpenFormula && in_function_or_array {
2272 (TokenType::Sep, TokenSubType::Arg)
2274 } else if self.dialect == FormulaDialect::OpenFormula {
2275 (TokenType::OpInfix, TokenSubType::None)
2276 } else {
2277 (TokenType::Sep, TokenSubType::Row)
2278 }
2279 }
2280 _ => (TokenType::OpInfix, TokenSubType::None),
2281 };
2282
2283 self.items.push(Token::from_slice(
2284 &self.formula,
2285 token_type,
2286 subtype,
2287 self.offset,
2288 self.offset + 1,
2289 ));
2290
2291 self.offset += 1;
2292 self.start_token();
2293 Ok(())
2294 }
2295
2296 pub fn render(&self) -> String {
2298 if self.items.is_empty() {
2299 "".to_string()
2300 } else if self.items[0].token_type == TokenType::Literal {
2301 self.items[0].value.clone()
2302 } else {
2303 let concatenated: String = self.items.iter().map(|t| t.value.clone()).collect();
2304 format!("={concatenated}")
2305 }
2306 }
2307
2308 pub fn dialect(&self) -> FormulaDialect {
2310 self.dialect
2311 }
2312}
2313
2314impl TryFrom<&str> for Tokenizer {
2315 type Error = TokenizerError;
2316
2317 fn try_from(value: &str) -> Result<Self, Self::Error> {
2318 Tokenizer::new(value)
2319 }
2320}
2321
2322impl TryFrom<String> for Tokenizer {
2323 type Error = TokenizerError;
2324
2325 fn try_from(value: String) -> Result<Self, Self::Error> {
2326 Tokenizer::new(&value)
2327 }
2328}