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