sphinx/lexer/rules/
strmatcher.rs

1use core::str::Chars;
2use std::collections::VecDeque;
3
4use crate::lexer::rules::MatchResult;
5
6// Helper struct to match an exact string
7
8#[derive(Clone, Copy)]
9pub enum MatchCase {
10    Sensitive,
11    AsciiInsensitive,
12    // Insensitive,  // too complicated
13}
14
15#[derive(Clone)]
16pub struct StrMatcher<'a> {
17    target: &'a str,
18    match_case: MatchCase,
19    
20    chars: Chars<'a>,
21    peek: VecDeque<Option<char>>,
22    
23    last_result: MatchResult,
24    count: usize, // track how far have we advanced through the target
25}
26
27impl<'a> StrMatcher<'a> {
28    pub fn new(target: &'a str, match_case: MatchCase) -> Self {
29        StrMatcher {
30            target,
31            match_case,
32            
33            chars: target.chars(),
34            peek: VecDeque::new(),
35            
36            last_result: MatchResult::IncompleteMatch,
37            count: 0,
38        }
39    }
40    pub fn case_sensitive(target: &'a str) -> Self { StrMatcher::new(target, MatchCase::Sensitive) }
41    pub fn ascii_case_insensitive(target: &'a str) -> Self { StrMatcher::new(target, MatchCase::AsciiInsensitive) }
42    
43    
44    pub fn target(&self) -> &'a str { self.target }
45    pub fn match_case(&self) -> MatchCase { self.match_case }
46    
47    pub fn last_match_result(&self) -> MatchResult { self.last_result }
48    pub fn count(&self) -> usize { self.count }
49    
50    pub fn reset(&mut self) {
51        self.last_result = MatchResult::IncompleteMatch;
52        self.chars = self.target.chars();
53        self.peek.clear();
54        self.count = 0;
55    }
56    
57    pub fn reset_target(&mut self, target: &'a str) {
58        self.target = target;
59        self.reset();
60    }
61    
62    fn peek_nth(&mut self, n: usize) -> Option<char> {
63        while self.peek.len() < n + 1 {
64            self.peek.push_back(self.chars.next());
65        }
66        self.peek[n]
67    }
68    
69    fn advance(&mut self) -> Option<char> {
70        self.count += 1;
71        match self.peek.pop_front() {
72            Some(o) => o,
73            None => self.chars.next()
74        }
75    }
76    
77    fn compare_chars(&self, a: &char, b: &char) -> bool {
78        match self.match_case {
79            MatchCase::Sensitive => a == b,
80            MatchCase::AsciiInsensitive => a.eq_ignore_ascii_case(b),
81        }
82    }
83    
84    pub fn peek_match(&mut self, next: char) -> MatchResult {
85        // if the match already failed, don't bother looking at any further input
86        if !self.last_result.is_match() {
87            return MatchResult::NoMatch;
88        }
89        
90        match self.peek_nth(0) {
91            Some(this_ch) if self.compare_chars(&this_ch, &next) => {
92                if self.peek_nth(1).is_none() {
93                    MatchResult::CompleteMatch
94                } else {
95                    MatchResult::IncompleteMatch
96                }
97            },
98            _ => MatchResult::NoMatch,
99        }
100    }
101    
102    pub fn update_match(&mut self, next: char) -> MatchResult {
103        self.last_result = self.peek_match(next);
104        self.advance();
105        
106        self.last_result
107    }
108    
109    pub fn try_match(&mut self, next: char) -> MatchResult {
110        let match_result = self.peek_match(next);
111        if match_result.is_match() {
112            self.last_result = match_result;
113            self.advance();
114        }
115        match_result
116    }
117    
118}