sqruff_lib_core/parser/grammar/
delimited.rs1use std::ops::{Deref, DerefMut};
2
3use ahash::AHashSet;
4
5use super::anyof::{AnyNumberOf, one_of};
6use super::base::Ref;
7use crate::dialects::syntax::SyntaxSet;
8use crate::errors::SQLParseError;
9use crate::helpers::ToMatchable;
10use crate::parser::context::ParseContext;
11use crate::parser::grammar::noncode::NonCodeMatcher;
12use crate::parser::match_algorithms::{longest_match, skip_start_index_forward_to_code};
13use crate::parser::match_result::MatchResult;
14use crate::parser::matchable::{
15 Matchable, MatchableCacheKey, MatchableTrait, next_matchable_cache_key,
16};
17use crate::parser::segments::base::ErasedSegment;
18
19#[derive(Clone, Debug)]
24pub struct Delimited {
25 pub base: AnyNumberOf,
26 pub allow_trailing: bool,
27 pub(crate) delimiter: Matchable,
28 pub min_delimiters: usize,
29 optional: bool,
30 cache_key: MatchableCacheKey,
31}
32
33impl Delimited {
34 pub fn new(elements: Vec<Matchable>) -> Self {
35 Self {
36 base: one_of(elements),
37 allow_trailing: false,
38 delimiter: Ref::new("CommaSegment").to_matchable(),
39 min_delimiters: 0,
40 optional: false,
41 cache_key: next_matchable_cache_key(),
42 }
43 }
44
45 pub fn allow_trailing(&mut self) {
46 self.allow_trailing = true;
47 }
48
49 pub fn delimiter(&mut self, delimiter: impl ToMatchable) {
50 self.delimiter = delimiter.to_matchable();
51 }
52}
53
54impl PartialEq for Delimited {
55 fn eq(&self, other: &Self) -> bool {
56 self.base == other.base && self.allow_trailing == other.allow_trailing
57 }
59}
60
61impl MatchableTrait for Delimited {
62 fn elements(&self) -> &[Matchable] {
63 &self.elements
64 }
65
66 fn is_optional(&self) -> bool {
67 self.optional || self.base.is_optional()
68 }
69
70 fn simple(
71 &self,
72 parse_context: &ParseContext,
73 crumbs: Option<Vec<&str>>,
74 ) -> Option<(AHashSet<String>, SyntaxSet)> {
75 super::anyof::simple(&self.elements, parse_context, crumbs)
76 }
77
78 fn match_segments(
84 &self,
85 segments: &[ErasedSegment],
86 idx: u32,
87 parse_context: &mut ParseContext,
88 ) -> Result<MatchResult, SQLParseError> {
89 let mut delimiters = 0;
90 let mut seeking_delimiter = false;
91 let max_idx = segments.len() as u32;
92 let mut working_idx = idx;
93 let mut working_match = MatchResult::empty_at(idx);
94 let mut delimiter_match = None;
95
96 let delimiter_matcher = self.delimiter.clone();
97
98 let mut terminator_matchers = self.terminators.clone();
99 terminator_matchers.extend(
100 parse_context
101 .terminators
102 .iter()
103 .filter(|&t| &delimiter_matcher != t)
104 .cloned(),
105 );
106
107 let delimiter_matchers = &[self.delimiter.clone()];
108
109 if !self.allow_gaps {
110 terminator_matchers.push(NonCodeMatcher.to_matchable());
111 }
112
113 loop {
114 if self.allow_gaps && working_idx > idx {
115 working_idx =
116 skip_start_index_forward_to_code(segments, working_idx, segments.len() as u32);
117 }
118
119 if working_idx >= max_idx {
120 break;
121 }
122
123 let (match_result, _) = parse_context.deeper_match(false, &[], |this| {
124 longest_match(segments, &terminator_matchers, working_idx, this)
125 })?;
126
127 if match_result.has_match() {
128 break;
129 }
130
131 let mut push_terminators: &[_] = &[];
132 if !seeking_delimiter {
133 push_terminators = delimiter_matchers;
134 }
135
136 let (match_result, _) =
137 parse_context.deeper_match(false, push_terminators, |this| {
138 longest_match(
139 segments,
140 if seeking_delimiter {
141 delimiter_matchers
142 } else {
143 &self.elements
144 },
145 working_idx,
146 this,
147 )
148 })?;
149
150 if !match_result.has_match() {
151 break;
152 }
153
154 working_idx = match_result.span.end;
155
156 if seeking_delimiter {
157 delimiter_match = match_result.into();
158 } else {
159 if let Some(delimiter_match) = &delimiter_match {
160 delimiters += 1;
161 working_match = working_match.append(delimiter_match);
162 }
163 working_match = working_match.append(match_result);
164 }
165
166 seeking_delimiter = !seeking_delimiter;
167 }
168
169 if let Some(delimiter_match) =
170 delimiter_match.filter(|_delimiter_match| self.allow_trailing && !seeking_delimiter)
171 {
172 delimiters += 1;
173 working_match = working_match.append(delimiter_match);
174 }
175
176 if delimiters < self.min_delimiters {
177 return Ok(MatchResult::empty_at(idx));
178 }
179
180 Ok(working_match)
181 }
182
183 fn cache_key(&self) -> MatchableCacheKey {
184 self.cache_key
185 }
186}
187
188impl Deref for Delimited {
189 type Target = AnyNumberOf;
190
191 fn deref(&self) -> &Self::Target {
192 &self.base
193 }
194}
195
196impl DerefMut for Delimited {
197 fn deref_mut(&mut self) -> &mut Self::Target {
198 &mut self.base
199 }
200}