sqruff_lib_core/parser/
match_result.rs1use std::borrow::Cow;
2use std::cmp::Ordering;
3
4use nohash_hasher::IntMap;
5
6use super::segments::{ErasedSegment, SegmentBuilder, Tables};
7use crate::dialects::init::DialectKind;
8use crate::dialects::syntax::SyntaxKind;
9use crate::parser::markers::PositionMarker;
10
11fn get_point_pos_at_idx(segments: &[ErasedSegment], idx: u32) -> PositionMarker {
12 let idx = idx as usize;
13 if idx < segments.len() {
14 segments[idx]
15 .get_position_marker()
16 .unwrap()
17 .start_point_marker()
18 } else {
19 segments[idx - 1]
20 .get_position_marker()
21 .unwrap()
22 .end_point_marker()
23 }
24}
25
26#[derive(Debug, Clone)]
27pub enum Matched {
28 SyntaxKind(SyntaxKind),
29 Newtype(SyntaxKind),
30}
31
32#[derive(Default, Debug, Clone)]
33pub struct MatchResult {
34 pub span: Span,
35 pub matched: Option<Matched>,
36 pub insert_segments: Vec<(u32, SyntaxKind)>,
37 pub child_matches: Vec<MatchResult>,
38}
39
40impl MatchResult {
41 pub fn from_span(start: u32, end: u32) -> Self {
42 Self {
43 span: Span { start, end },
44 ..Default::default()
45 }
46 }
47
48 pub fn empty_at(idx: u32) -> Self {
49 Self::from_span(idx, idx)
50 }
51
52 pub fn len(&self) -> u32 {
53 self.span.end - self.span.start
54 }
55
56 pub fn is_empty(&self) -> bool {
57 !self.has_match()
58 }
59
60 #[allow(clippy::len_zero)]
61 pub fn has_match(&self) -> bool {
62 self.len() > 0 || !self.insert_segments.is_empty()
63 }
64
65 pub fn is_better_than(&self, other: &MatchResult) -> bool {
66 self.len() > other.len()
67 }
68
69 pub(crate) fn append<'a>(self, other: impl Into<Cow<'a, MatchResult>>) -> Self {
70 let other = other.into();
71 let mut insert_segments = Vec::new();
72
73 if self.is_empty() {
74 return other.into_owned();
75 }
76
77 if other.is_empty() {
78 return self;
79 }
80
81 let new_span = Span {
82 start: self.span.start,
83 end: other.span.end,
84 };
85 let mut child_matches = Vec::new();
86 for mut matched in [self, other.into_owned()] {
87 if matched.matched.is_some() {
88 child_matches.push(matched);
89 } else {
90 insert_segments.append(&mut matched.insert_segments);
91 child_matches.append(&mut matched.child_matches);
92 }
93 }
94
95 MatchResult {
96 span: new_span,
97 insert_segments,
98 child_matches,
99 ..Default::default()
100 }
101 }
102
103 pub(crate) fn wrap(self, outer_matched: Matched) -> Self {
104 if self.is_empty() {
105 return self;
106 }
107
108 let mut insert_segments = Vec::new();
109 let span = self.span;
110 let child_matches = if self.matched.is_some() {
111 vec![self]
112 } else {
113 insert_segments = self.insert_segments;
114 self.child_matches
115 };
116
117 Self {
118 span,
119 matched: Some(outer_matched),
120 insert_segments,
121 child_matches,
122 }
123 }
124
125 pub fn apply(
126 self,
127 tables: &Tables,
128 dialect: DialectKind,
129 segments: &[ErasedSegment],
130 ) -> Vec<ErasedSegment> {
131 enum Trigger {
132 MatchResult(MatchResult),
133 Meta(SyntaxKind),
134 }
135
136 let mut result_segments = Vec::new();
137 let mut trigger_locs: IntMap<u32, Vec<Trigger>> = IntMap::with_capacity_and_hasher(
138 self.insert_segments.len() + self.child_matches.len(),
139 Default::default(),
140 );
141
142 for (pos, insert) in self.insert_segments {
143 trigger_locs
144 .entry(pos)
145 .or_default()
146 .push(Trigger::Meta(insert));
147 }
148
149 for match_result in self.child_matches {
150 trigger_locs
151 .entry(match_result.span.start)
152 .or_default()
153 .push(Trigger::MatchResult(match_result));
154 }
155
156 let mut max_idx = self.span.start;
157 let mut keys: Vec<u32> = Vec::from_iter(trigger_locs.keys().copied());
158 keys.sort();
159
160 for idx in keys {
161 match idx.cmp(&max_idx) {
162 Ordering::Greater => {
163 result_segments.extend_from_slice(&segments[max_idx as usize..idx as usize]);
164 max_idx = idx;
165 }
166 Ordering::Less => {
167 unreachable!("This MatchResult was wrongly constructed")
168 }
169 Ordering::Equal => {}
170 }
171
172 for trigger in trigger_locs.remove(&idx).unwrap() {
173 match trigger {
174 Trigger::MatchResult(trigger) => {
175 max_idx = trigger.span.end;
176 result_segments.append(&mut trigger.apply(tables, dialect, segments));
177 }
178 Trigger::Meta(meta) => {
179 let pos = get_point_pos_at_idx(segments, idx);
180 let meta = SegmentBuilder::token(tables.next_id(), "", meta)
181 .with_position(pos)
182 .finish();
183 result_segments.push(meta);
184 }
185 }
186 }
187 }
188
189 if max_idx < self.span.end {
190 result_segments.extend_from_slice(&segments[max_idx as usize..self.span.end as usize])
191 }
192
193 let Some(matched) = self.matched else {
194 return result_segments;
195 };
196
197 match matched {
198 Matched::SyntaxKind(kind) => {
199 vec![
200 SegmentBuilder::node(tables.next_id(), kind, dialect, result_segments)
201 .position_from_segments()
202 .finish(),
203 ]
204 }
205 Matched::Newtype(kind) => {
206 let old = result_segments.pop().unwrap();
207
208 vec![
209 SegmentBuilder::token(old.id(), old.raw().as_ref(), kind)
210 .with_position(old.get_position_marker().unwrap().clone())
211 .finish(),
212 ]
213 }
214 }
215 }
216}
217
218impl<'a> From<&'a MatchResult> for Cow<'a, MatchResult> {
219 fn from(t: &'a MatchResult) -> Self {
220 Cow::Borrowed(t)
221 }
222}
223
224impl From<MatchResult> for Cow<'_, MatchResult> {
225 fn from(t: MatchResult) -> Self {
226 Cow::Owned(t)
227 }
228}
229
230#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
231pub struct Span {
232 pub start: u32,
233 pub end: u32,
234}