sqruff_lib_core/parser/
parsers.rs

1use ahash::AHashSet;
2use fancy_regex::Regex;
3use smol_str::SmolStr;
4
5use super::context::ParseContext;
6use super::match_result::{MatchResult, Matched, Span};
7use super::matchable::{Matchable, MatchableCacheKey, MatchableTrait, next_matchable_cache_key};
8use super::segments::base::ErasedSegment;
9use crate::dialects::syntax::{SyntaxKind, SyntaxSet};
10use crate::errors::SQLParseError;
11
12#[derive(Debug, Clone, PartialEq)]
13pub struct TypedParser {
14    template: SyntaxKind,
15    target_types: SyntaxSet,
16    kind: SyntaxKind,
17    optional: bool,
18    cache_key: MatchableCacheKey,
19}
20
21impl TypedParser {
22    pub fn new(template: SyntaxKind, kind: SyntaxKind) -> Self {
23        let target_types = SyntaxSet::new(&[template]);
24
25        Self {
26            template,
27            kind,
28            target_types,
29            optional: false,
30            cache_key: next_matchable_cache_key(),
31        }
32    }
33
34    pub fn is_first_match(&self, segment: &ErasedSegment) -> bool {
35        self.target_types.contains(segment.get_type())
36    }
37}
38
39impl MatchableTrait for TypedParser {
40    fn elements(&self) -> &[Matchable] {
41        &[]
42    }
43
44    fn simple(
45        &self,
46        parse_context: &ParseContext,
47        crumbs: Option<Vec<&str>>,
48    ) -> Option<(AHashSet<String>, SyntaxSet)> {
49        let _ = (parse_context, crumbs);
50        (AHashSet::new(), self.target_types.clone()).into()
51    }
52
53    fn match_segments(
54        &self,
55        segments: &[ErasedSegment],
56        idx: u32,
57        _parse_context: &mut ParseContext,
58    ) -> Result<MatchResult, SQLParseError> {
59        let segment = &segments[idx as usize];
60        if segment.is_type(self.template) {
61            return Ok(MatchResult {
62                span: Span {
63                    start: idx,
64                    end: idx + 1,
65                },
66                matched: Matched::Newtype(self.kind).into(),
67                insert_segments: Vec::new(),
68                child_matches: Vec::new(),
69            });
70        }
71
72        Ok(MatchResult::empty_at(idx))
73    }
74
75    fn cache_key(&self) -> MatchableCacheKey {
76        self.cache_key
77    }
78}
79
80#[derive(Clone, Debug, PartialEq)]
81pub struct StringParser {
82    template: String,
83    simple: AHashSet<String>,
84    kind: SyntaxKind,
85    optional: bool,
86    cache_key: MatchableCacheKey,
87}
88
89impl StringParser {
90    pub fn new(template: &str, kind: SyntaxKind) -> StringParser {
91        let template_upper = template.to_uppercase();
92        let simple_set = [template_upper.clone()].into();
93
94        StringParser {
95            template: template_upper,
96            simple: simple_set,
97            kind,
98            optional: false,
99            cache_key: next_matchable_cache_key(),
100        }
101    }
102
103    pub fn simple(&self, _parse_cx: &ParseContext) -> (AHashSet<String>, AHashSet<String>) {
104        (self.simple.clone(), AHashSet::new())
105    }
106
107    pub fn is_first_match(&self, segment: &ErasedSegment) -> bool {
108        segment.is_code() && self.template.eq_ignore_ascii_case(segment.raw())
109    }
110}
111
112impl MatchableTrait for StringParser {
113    fn elements(&self) -> &[Matchable] {
114        &[]
115    }
116
117    fn is_optional(&self) -> bool {
118        self.optional
119    }
120
121    fn simple(
122        &self,
123        _parse_context: &ParseContext,
124        _crumbs: Option<Vec<&str>>,
125    ) -> Option<(AHashSet<String>, SyntaxSet)> {
126        (self.simple.clone(), SyntaxSet::EMPTY).into()
127    }
128
129    fn match_segments(
130        &self,
131        segments: &[ErasedSegment],
132        idx: u32,
133        _parse_context: &mut ParseContext,
134    ) -> Result<MatchResult, SQLParseError> {
135        let segment = &segments[idx as usize];
136
137        if segment.is_code() && self.template.eq_ignore_ascii_case(segment.raw()) {
138            return Ok(MatchResult {
139                span: Span {
140                    start: idx,
141                    end: idx + 1,
142                },
143                matched: Matched::Newtype(self.kind).into(),
144                insert_segments: Vec::new(),
145                child_matches: Vec::new(),
146            });
147        }
148
149        Ok(MatchResult::empty_at(idx))
150    }
151
152    fn cache_key(&self) -> MatchableCacheKey {
153        self.cache_key
154    }
155}
156
157#[derive(Debug, Clone)]
158pub struct RegexParser {
159    pub template: Regex,
160    pub anti_template: Option<Regex>,
161    kind: SyntaxKind,
162    cache_key: MatchableCacheKey,
163}
164
165impl PartialEq for RegexParser {
166    fn eq(&self, other: &Self) -> bool {
167        self.template.as_str() == other.template.as_str()
168            && self
169                .anti_template
170                .as_ref()
171                .zip(other.anti_template.as_ref())
172                .is_some_and(|(lhs, rhs)| lhs.as_str() == rhs.as_str())
173            && self.kind == other.kind
174    }
175}
176
177impl RegexParser {
178    pub fn new(template: &str, kind: SyntaxKind) -> Self {
179        let template_pattern = Regex::new(&format!("(?i){}", template)).unwrap();
180
181        Self {
182            template: template_pattern,
183            anti_template: None,
184            kind,
185            cache_key: next_matchable_cache_key(),
186        }
187    }
188
189    pub fn anti_template(mut self, anti_template: &str) -> Self {
190        self.anti_template = Regex::new(&format!("(?i){anti_template}")).unwrap().into();
191        self
192    }
193}
194
195impl MatchableTrait for RegexParser {
196    fn elements(&self) -> &[Matchable] {
197        &[]
198    }
199
200    fn is_optional(&self) -> bool {
201        unimplemented!()
202    }
203
204    fn simple(
205        &self,
206        _parse_context: &ParseContext,
207        _crumbs: Option<Vec<&str>>,
208    ) -> Option<(AHashSet<String>, SyntaxSet)> {
209        // Does this matcher support a uppercase hash matching route?
210        // Regex segment does NOT for now. We might need to later for efficiency.
211        None
212    }
213
214    fn match_segments(
215        &self,
216        segments: &[ErasedSegment],
217        idx: u32,
218        _parse_context: &mut ParseContext,
219    ) -> Result<MatchResult, SQLParseError> {
220        let segment = &segments[idx as usize];
221        let segment_raw_upper =
222            SmolStr::from_iter(segment.raw().chars().map(|ch| ch.to_ascii_uppercase()));
223        if let Some(result) = self.template.find(&segment_raw_upper).ok().flatten() {
224            if result.as_str() == segment_raw_upper
225                && !self.anti_template.as_ref().is_some_and(|anti_template| {
226                    anti_template
227                        .is_match(&segment_raw_upper)
228                        .unwrap_or_default()
229                })
230            {
231                return Ok(MatchResult {
232                    span: Span {
233                        start: idx,
234                        end: idx + 1,
235                    },
236                    matched: Matched::Newtype(self.kind).into(),
237                    insert_segments: Vec::new(),
238                    child_matches: Vec::new(),
239                });
240            }
241        }
242
243        Ok(MatchResult::empty_at(idx))
244    }
245
246    fn cache_key(&self) -> MatchableCacheKey {
247        self.cache_key
248    }
249}
250
251#[derive(Clone, Debug, PartialEq)]
252pub struct MultiStringParser {
253    templates: AHashSet<String>,
254    simple: AHashSet<String>,
255    kind: SyntaxKind,
256    cache: MatchableCacheKey,
257}
258
259impl MultiStringParser {
260    pub fn new(templates: Vec<String>, kind: SyntaxKind) -> Self {
261        let templates = templates
262            .iter()
263            .map(|template| template.to_ascii_uppercase())
264            .collect::<AHashSet<String>>();
265
266        let _simple = templates.clone();
267
268        Self {
269            templates: templates.into_iter().collect(),
270            simple: _simple.into_iter().collect(),
271            kind,
272            cache: next_matchable_cache_key(),
273        }
274    }
275}
276
277impl MatchableTrait for MultiStringParser {
278    fn elements(&self) -> &[Matchable] {
279        &[]
280    }
281
282    fn is_optional(&self) -> bool {
283        todo!()
284    }
285
286    fn simple(
287        &self,
288        _parse_context: &ParseContext,
289        _crumbs: Option<Vec<&str>>,
290    ) -> Option<(AHashSet<String>, SyntaxSet)> {
291        (self.simple.clone(), SyntaxSet::EMPTY).into()
292    }
293
294    fn match_segments(
295        &self,
296        segments: &[ErasedSegment],
297        idx: u32,
298        _parse_context: &mut ParseContext,
299    ) -> Result<MatchResult, SQLParseError> {
300        let segment = &segments[idx as usize];
301
302        if segment.is_code() && self.templates.contains(&segment.raw().to_ascii_uppercase()) {
303            return Ok(MatchResult {
304                span: Span {
305                    start: idx,
306                    end: idx + 1,
307                },
308                matched: Matched::Newtype(self.kind).into(),
309                ..<_>::default()
310            });
311        }
312
313        Ok(MatchResult::empty_at(idx))
314    }
315
316    fn cache_key(&self) -> MatchableCacheKey {
317        self.cache
318    }
319}