sqruff_lib_core/
dialects.rs

1pub mod common;
2pub mod init;
3pub mod syntax;
4
5use std::borrow::Cow;
6use std::collections::hash_map::Entry;
7use std::fmt::Debug;
8
9use ahash::{AHashMap, AHashSet};
10
11use crate::dialects::init::DialectKind;
12use crate::dialects::syntax::SyntaxKind;
13use crate::helpers::{ToMatchable, capitalize};
14use crate::parser::lexer::{Lexer, Matcher};
15use crate::parser::matchable::Matchable;
16use crate::parser::parsers::StringParser;
17use crate::parser::types::DialectElementType;
18
19#[derive(Debug, Clone, Default)]
20pub struct Dialect {
21    pub name: DialectKind,
22    lexer_matchers: Option<Vec<Matcher>>,
23    library: AHashMap<Cow<'static, str>, DialectElementType>,
24    sets: AHashMap<&'static str, AHashSet<&'static str>>,
25    pub bracket_collections: AHashMap<&'static str, AHashSet<BracketPair>>,
26    lexer: Option<Lexer>,
27}
28
29impl PartialEq for Dialect {
30    fn eq(&self, other: &Self) -> bool {
31        self.name == other.name
32    }
33}
34
35impl Dialect {
36    pub fn new() -> Self {
37        Dialect {
38            name: DialectKind::Ansi,
39            ..Default::default()
40        }
41    }
42
43    pub fn name(&self) -> DialectKind {
44        self.name
45    }
46
47    pub fn add(
48        &mut self,
49        iter: impl IntoIterator<Item = (Cow<'static, str>, DialectElementType)> + Clone,
50    ) {
51        self.library.extend(iter);
52    }
53
54    pub fn grammar(&self, name: &str) -> Matchable {
55        match self
56            .library
57            .get(name)
58            .unwrap_or_else(|| panic!("not found {name}"))
59        {
60            DialectElementType::Matchable(matchable) => matchable.clone(),
61            DialectElementType::SegmentGenerator(_) => {
62                unreachable!("Attempted to fetch non grammar [{name}] with `Dialect::grammar`.")
63            }
64        }
65    }
66
67    #[track_caller]
68    pub fn replace_grammar(&mut self, name: &'static str, match_grammar: Matchable) {
69        match self
70            .library
71            .get_mut(name)
72            .unwrap_or_else(|| panic!("Failed to get mutable reference for {name}"))
73        {
74            DialectElementType::Matchable(matchable) => {
75                matchable.as_node_matcher().unwrap().match_grammar = match_grammar;
76            }
77            DialectElementType::SegmentGenerator(_) => {
78                unreachable!("Attempted to fetch non grammar [{name}] with `Dialect::grammar`.")
79            }
80        }
81    }
82
83    pub fn lexer_matchers(&self) -> &[Matcher] {
84        match &self.lexer_matchers {
85            Some(lexer_matchers) => lexer_matchers,
86            None => panic!("Lexing struct has not been set for dialect {self:?}"),
87        }
88    }
89
90    pub fn insert_lexer_matchers(&mut self, lexer_patch: Vec<Matcher>, before: &str) {
91        let mut buff = Vec::new();
92        let mut found = false;
93
94        if self.lexer_matchers.is_none() {
95            panic!("Lexer struct must be defined before it can be patched!");
96        }
97
98        for elem in self.lexer_matchers.take().unwrap() {
99            if elem.name() == before {
100                found = true;
101                for patch in lexer_patch.clone() {
102                    buff.push(patch);
103                }
104                buff.push(elem);
105            } else {
106                buff.push(elem);
107            }
108        }
109
110        if !found {
111            panic!("Lexer struct insert before '{before}' failed because tag never found.");
112        }
113
114        self.lexer_matchers = Some(buff);
115    }
116
117    pub fn patch_lexer_matchers(&mut self, lexer_patch: Vec<Matcher>) {
118        let mut buff = Vec::with_capacity(self.lexer_matchers.as_ref().map_or(0, Vec::len));
119        if self.lexer_matchers.is_none() {
120            panic!("Lexer struct must be defined before it can be patched!");
121        }
122
123        let patch_dict: AHashMap<&'static str, Matcher> = lexer_patch
124            .into_iter()
125            .map(|elem| (elem.name(), elem))
126            .collect();
127
128        for elem in self.lexer_matchers.take().unwrap() {
129            if let Some(patch) = patch_dict.get(elem.name()) {
130                buff.push(patch.clone());
131            } else {
132                buff.push(elem);
133            }
134        }
135
136        self.lexer_matchers = Some(buff);
137    }
138
139    pub fn set_lexer_matchers(&mut self, lexer_matchers: Vec<Matcher>) {
140        self.lexer_matchers = lexer_matchers.into();
141    }
142
143    pub fn sets(&self, label: &str) -> AHashSet<&'static str> {
144        match label {
145            "bracket_pairs" | "angle_bracket_pairs" => {
146                panic!("Use `bracket_sets` to retrieve {} set.", label);
147            }
148            _ => (),
149        }
150
151        self.sets.get(label).cloned().unwrap_or_default()
152    }
153
154    pub fn sets_mut(&mut self, label: &'static str) -> &mut AHashSet<&'static str> {
155        assert!(
156            label != "bracket_pairs" && label != "angle_bracket_pairs",
157            "Use `bracket_sets` to retrieve {} set.",
158            label
159        );
160
161        match self.sets.entry(label) {
162            Entry::Occupied(entry) => entry.into_mut(),
163            Entry::Vacant(entry) => entry.insert(<_>::default()),
164        }
165    }
166
167    pub fn update_keywords_set_from_multiline_string(
168        &mut self,
169        set_label: &'static str,
170        values: &'static str,
171    ) {
172        let keywords = values.lines().map(str::trim);
173        self.sets_mut(set_label).extend(keywords);
174    }
175
176    pub fn add_keyword_to_set(&mut self, set_label: &'static str, value: &'static str) {
177        self.sets_mut(set_label).insert(value);
178    }
179
180    pub fn bracket_sets(&self, label: &str) -> AHashSet<BracketPair> {
181        assert!(
182            label == "bracket_pairs" || label == "angle_bracket_pairs",
183            "Invalid bracket set. Consider using another identifier instead."
184        );
185
186        self.bracket_collections
187            .get(label)
188            .cloned()
189            .unwrap_or_default()
190    }
191
192    pub fn bracket_sets_mut(&mut self, label: &'static str) -> &mut AHashSet<BracketPair> {
193        assert!(
194            label == "bracket_pairs" || label == "angle_bracket_pairs",
195            "Invalid bracket set. Consider using another identifier instead."
196        );
197
198        self.bracket_collections.entry(label).or_default()
199    }
200
201    pub fn update_bracket_sets(&mut self, label: &'static str, pairs: Vec<BracketPair>) {
202        let set = self.bracket_sets_mut(label);
203        for pair in pairs {
204            set.insert(pair);
205        }
206    }
207
208    pub fn r#ref(&self, name: &str) -> Matchable {
209        match self.library.get(name) {
210            Some(DialectElementType::Matchable(matchable)) => matchable.clone(),
211            Some(DialectElementType::SegmentGenerator(_)) => {
212                panic!("Unexpected SegmentGenerator while fetching '{}'", name);
213            }
214            None => {
215                if let Some(keyword) = name.strip_suffix("KeywordSegment") {
216                    let keyword_tip = "\
217                        \n\nThe syntax in the query is not (yet?) supported. Try to \
218                        narrow down your query to a minimal, reproducible case and \
219                        raise an issue on GitHub.\n\n\
220                        Or, even better, see this guide on how to help contribute \
221                        keyword and/or dialect updates:\n\
222                        https://github.com/quarylabs/sqruff";
223                    panic!(
224                        "Grammar refers to the '{keyword}' keyword which was not found in the \
225                         dialect.{keyword_tip}",
226                    );
227                } else {
228                    panic!("Grammar refers to '{name}' which was not found in the dialect.",);
229                }
230            }
231        }
232    }
233
234    pub fn expand(&mut self) {
235        // Temporarily take ownership of 'library' from 'self' to avoid borrow checker
236        // errors during mutation.
237        let mut library = std::mem::take(&mut self.library);
238        for element in library.values_mut() {
239            if let DialectElementType::SegmentGenerator(generator) = element {
240                *element = DialectElementType::Matchable(generator.expand(self));
241            }
242        }
243        self.library = library;
244
245        for keyword_set in ["unreserved_keywords", "reserved_keywords"] {
246            if let Some(keywords) = self.sets.get(keyword_set) {
247                for kw in keywords {
248                    let n = format!("{}KeywordSegment", capitalize(kw));
249                    if !self.library.contains_key(n.as_str()) {
250                        let parser = StringParser::new(&kw.to_lowercase(), SyntaxKind::Keyword);
251
252                        self.library.insert(
253                            n.into(),
254                            DialectElementType::Matchable(parser.to_matchable()),
255                        );
256                    }
257                }
258            }
259        }
260
261        self.lexer = Lexer::new(self.lexer_matchers()).into();
262    }
263
264    pub fn lexer(&self) -> &Lexer {
265        self.lexer.as_ref().unwrap()
266    }
267}
268
269pub type BracketPair = (&'static str, &'static str, &'static str, bool);