sqruff_lib_core/parser/
grammar.rs

1pub mod anyof;
2pub mod conditional;
3pub mod delimited;
4pub mod noncode;
5pub mod sequence;
6
7use ahash::AHashSet;
8use std::borrow::Cow;
9use std::sync::OnceLock;
10
11use crate::dialects::syntax::SyntaxSet;
12use crate::errors::SQLParseError;
13use crate::helpers::ToMatchable;
14use crate::parser::context::ParseContext;
15use crate::parser::match_algorithms::greedy_match;
16use crate::parser::match_result::MatchResult;
17use crate::parser::matchable::{
18    Matchable, MatchableCacheKey, MatchableTrait, next_matchable_cache_key,
19};
20use crate::parser::segments::ErasedSegment;
21
22#[derive(Clone)]
23pub struct Ref {
24    pub(crate) reference: Cow<'static, str>,
25    pub exclude: Option<Matchable>,
26    terminators: Vec<Matchable>,
27    reset_terminators: bool,
28    pub(crate) allow_gaps: bool,
29    pub(crate) optional: bool,
30    cache_key: MatchableCacheKey,
31    simple_cache: OnceLock<Option<(AHashSet<String>, SyntaxSet)>>,
32}
33
34impl std::fmt::Debug for Ref {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        write!(
37            f,
38            "<Ref: {}{}>",
39            self.reference,
40            if self.is_optional() { " [opt]" } else { "" }
41        )
42    }
43}
44
45impl Ref {
46    // Constructor function
47    pub fn new(reference: impl Into<Cow<'static, str>>) -> Self {
48        Ref {
49            reference: reference.into(),
50            exclude: None,
51            terminators: Vec::new(),
52            reset_terminators: false,
53            allow_gaps: true,
54            optional: false,
55            cache_key: next_matchable_cache_key(),
56            simple_cache: OnceLock::new(),
57        }
58    }
59
60    pub fn exclude(mut self, exclude: impl ToMatchable) -> Self {
61        self.exclude = exclude.to_matchable().into();
62        self
63    }
64
65    pub fn optional(mut self) -> Self {
66        self.optional = true;
67        self
68    }
69
70    // Static method to create a Ref instance for a keyword
71    #[track_caller]
72    pub fn keyword(keyword: impl Into<Cow<'static, str>>) -> Self {
73        let keyword = keyword.into();
74
75        debug_assert!(
76            keyword.chars().all(|c| !c.is_lowercase()),
77            "Keyword references must be uppercase: {keyword}",
78        );
79
80        Ref::new(keyword)
81    }
82}
83
84impl PartialEq for Ref {
85    fn eq(&self, other: &Self) -> bool {
86        self.reference == other.reference
87            && self.reset_terminators == other.reset_terminators
88            && self.allow_gaps == other.allow_gaps
89            && self.optional == other.optional
90    }
91}
92
93impl Eq for Ref {}
94
95impl MatchableTrait for Ref {
96    fn elements(&self) -> &[Matchable] {
97        &[]
98    }
99
100    fn is_optional(&self) -> bool {
101        self.optional
102    }
103
104    fn simple(
105        &self,
106        parse_context: &ParseContext,
107        crumbs: Option<Vec<&str>>,
108    ) -> Option<(AHashSet<String>, SyntaxSet)> {
109        self.simple_cache
110            .get_or_init(|| {
111                if let Some(ref c) = crumbs
112                    && c.contains(&&*self.reference)
113                {
114                    let loop_string = c.join(" -> ");
115                    panic!("Self referential grammar detected: {loop_string}");
116                }
117
118                let mut new_crumbs = crumbs.unwrap_or_default();
119                new_crumbs.push(&self.reference);
120
121                parse_context
122                    .dialect()
123                    .r#ref(&self.reference)
124                    .simple(parse_context, Some(new_crumbs))
125            })
126            .clone()
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 elem = parse_context.dialect().r#ref(&self.reference);
136
137        if let Some(exclude) = &self.exclude {
138            let ctx =
139                parse_context.deeper_match(self.reset_terminators, &self.terminators, |this| {
140                    if exclude
141                        .match_segments(segments, idx, this)
142                        .inspect_err(|e| log::error!("Parser error: {e:?}"))
143                        .is_ok_and(|match_result| match_result.has_match())
144                    {
145                        return Some(MatchResult::empty_at(idx));
146                    }
147
148                    None
149                });
150
151            if let Some(ctx) = ctx {
152                return Ok(ctx);
153            }
154        }
155
156        parse_context.deeper_match(self.reset_terminators, &self.terminators, |this| {
157            elem.match_segments(segments, idx, this)
158        })
159    }
160
161    fn cache_key(&self) -> MatchableCacheKey {
162        self.cache_key
163    }
164}
165
166#[derive(Clone, Debug)]
167pub struct Anything {
168    cache_key: MatchableCacheKey,
169    terminators: Vec<Matchable>,
170}
171
172impl PartialEq for Anything {
173    #[allow(unused_variables)]
174    fn eq(&self, other: &Self) -> bool {
175        unimplemented!()
176    }
177}
178
179impl Default for Anything {
180    fn default() -> Self {
181        Self::new()
182    }
183}
184
185impl Anything {
186    pub fn new() -> Self {
187        Self {
188            cache_key: next_matchable_cache_key(),
189            terminators: Vec::new(),
190        }
191    }
192
193    pub fn terminators(mut self, terminators: Vec<Matchable>) -> Self {
194        self.terminators = terminators;
195        self
196    }
197}
198
199impl MatchableTrait for Anything {
200    fn elements(&self) -> &[Matchable] {
201        &[]
202    }
203
204    fn match_segments(
205        &self,
206        segments: &[ErasedSegment],
207        idx: u32,
208        parse_context: &mut ParseContext,
209    ) -> Result<MatchResult, SQLParseError> {
210        if self.terminators.is_empty() && parse_context.terminators.is_empty() {
211            return Ok(MatchResult::from_span(idx, segments.len() as u32));
212        }
213
214        let mut terminators = self.terminators.clone();
215        terminators.extend_from_slice(&parse_context.terminators);
216
217        greedy_match(segments, idx, parse_context, &terminators, false, true)
218    }
219
220    fn cache_key(&self) -> MatchableCacheKey {
221        self.cache_key
222    }
223}
224
225#[derive(Clone, Debug, PartialEq)]
226pub struct Nothing {}
227
228impl Default for Nothing {
229    fn default() -> Self {
230        Self::new()
231    }
232}
233
234impl Nothing {
235    pub fn new() -> Self {
236        Self {}
237    }
238}
239
240impl MatchableTrait for Nothing {
241    fn elements(&self) -> &[Matchable] {
242        &[]
243    }
244
245    fn match_segments(
246        &self,
247        _segments: &[ErasedSegment],
248        idx: u32,
249        _parse_context: &mut ParseContext,
250    ) -> Result<MatchResult, SQLParseError> {
251        Ok(MatchResult::empty_at(idx))
252    }
253}