Skip to main content

sqruff_lib_core/parser/
match_result.rs

1use 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}