sqruff_lib_core/parser/grammar/
base.rs

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