Skip to main content

rdf_parsers/
parser.rs

1#[allow(unused)]
2#[derive(Debug)]
3pub struct FatToken<T: TokenTrait> {
4    pub kind: T,
5    text: String,
6    pub range: Range<usize>,
7    old_kind: Option<crate::Fingerprint>,
8    /// Bracket nesting depth from the previous parse at which this token was
9    /// parsed.  `None` for new (inserted) tokens.
10    old_depth: Option<u8>,
11}
12impl<T: TokenTrait> FatToken<T> {
13    pub fn new(kind: T, range: Range<usize>, text: String) -> Self {
14        FatToken {
15            kind,
16            range,
17            text,
18            old_kind: None,
19            old_depth: None,
20        }
21    }
22
23    pub fn text(&self) -> &str {
24        &self.text
25    }
26
27    pub fn old_kind(&self) -> Option<crate::Fingerprint> {
28        self.old_kind
29    }
30
31    pub fn set_old_kind(&mut self, kind: Option<crate::Fingerprint>) {
32        self.old_kind = kind;
33    }
34
35    pub fn old_depth(&self) -> Option<u8> {
36        self.old_depth
37    }
38
39    pub fn set_old_depth(&mut self, depth: Option<u8>) {
40        self.old_depth = depth;
41    }
42
43    pub fn to_prev_token(&self, current_depth: u8) -> crate::PrevToken {
44        crate::PrevToken {
45            text: self.text.clone(),
46            fingerprint: self.old_kind,
47            depth: current_depth,
48        }
49    }
50
51    /// Extend this token's span and text to cover up to `end`.
52    /// Used to merge consecutive lexer error tokens into one.
53    pub fn extend_to(&mut self, end: usize, text: String) {
54        self.range = self.range.start..end;
55        self.text = text;
56    }
57}
58
59/// Collapse rule nodes that consumed no tokens into a single `Expected(RuleName)` error.
60///
61/// When a grammar rule fires but consumes no tokens and has errors, and at least one
62/// ancestor rule has already consumed something, the whole rule is replaced by a
63/// single `Expected(rule_kind)` error.  Collapsing is allowed to cascade outward:
64/// each successive `out.truncate` erases the inner collapse so only the outermost
65/// still-empty rule survives.  This naturally surfaces the most specific grammar
66/// rule that the user should care about — e.g. `Expected(Verb)` instead of
67/// `Expected(Alit)` when the verb is absent, or `Expected(PredicateObjectList)`
68/// when both verb and object are absent.
69fn coalesce_empty_rules<T: crate::TokenTrait>(steps: Vec<Step<T>>) -> Vec<Step<T>> {
70    let mut out: Vec<Step<T>> = Vec::with_capacity(steps.len());
71    // Stack entry: (kind, start_idx_in_out, has_bump, has_error)
72    let mut stack: Vec<(T, usize, bool, bool)> = Vec::new();
73
74    for step in steps {
75        match step {
76            Step::Start(ref kind) => {
77                stack.push((kind.clone(), out.len(), false, false));
78                out.push(step);
79            }
80            Step::Bump(_) | Step::Delete => {
81                for entry in &mut stack {
82                    entry.2 = true;
83                }
84                out.push(step);
85            }
86            Step::Error(_) => {
87                for entry in &mut stack {
88                    entry.3 = true;
89                }
90                out.push(step);
91            }
92            Step::End => {
93                let (kind, start_pos, has_bump, has_error) = stack.pop().expect("unmatched End");
94                let any_ancestor_has_bump = stack.iter().any(|e| e.2);
95
96                if !has_bump && has_error && any_ancestor_has_bump {
97                    // Rule consumed no tokens: collapse into a single Expected(rule_kind)
98                    // error.  The truncate erases any inner collapses, so this rule's name
99                    // takes precedence over more specific (but noisier) inner names.
100                    out.truncate(start_pos);
101                    out.push(Step::Start(kind.clone()));
102                    out.push(Step::Error(Error::Expected(kind.into())));
103                    out.push(Step::End);
104                } else {
105                    out.push(Step::End);
106                }
107            }
108        }
109    }
110    out
111}
112
113pub struct Parse {
114    pub green_node: GreenNode,
115    pub errors: List<String>,
116    pub suggestions: HashSet<(String, Range<usize>)>,
117}
118impl Parse {
119    pub fn expected_at<L>(
120        &self,
121        byte_offset: usize,
122        first_tokens: fn(L::Kind) -> &'static [L::Kind],
123    ) -> Vec<L::Kind>
124    where
125        L: rowan::Language,
126        L::Kind: Eq + std::hash::Hash + Copy,
127    {
128        use rowan::{TextSize, TokenAtOffset};
129        let root = self.syntax::<L>();
130        let offset = TextSize::new(byte_offset as u32);
131
132        let (current_kind, start_node) = match root.token_at_offset(offset) {
133            TokenAtOffset::None => (None, None),
134            TokenAtOffset::Single(t) => (Some(t.kind()), t.parent()),
135            TokenAtOffset::Between(_, right) => (Some(right.kind()), right.parent()),
136        };
137
138        let mut result = HashSet::new();
139        let mut node = start_node;
140        while let Some(n) = node {
141            result.extend(first_tokens(n.kind()).iter().copied());
142            node = n.parent();
143        }
144
145        if let Some(k) = current_kind {
146            result.remove(&k);
147        }
148
149        result.into_iter().collect()
150    }
151
152    pub fn from_steps<T: crate::TokenTrait>(
153        tokens: &mut [FatToken<T>],
154        steps: List<Step<T>>,
155    ) -> Self {
156        let mut at = 0;
157        // (token_index, fingerprint, bracket_depth_at_bump)
158        let mut fingerprint_assignments: Vec<(usize, crate::Fingerprint, u8)> = Vec::new();
159        let skip_white_with_builder =
160            |builder: &mut GreenNodeBuilder<'_>,
161             at: &mut usize,
162             error_msgs: &mut Vec<String>| {
163                while let Some(t) = tokens.get(*at)
164                    && t.kind.skips()
165                {
166                    if t.kind == T::ERROR {
167                        error_msgs.push(format!("InvalidToken({:?})", t.text));
168                    }
169                    builder.token(t.kind.clone().into(), &t.text);
170                    *at += 1;
171                }
172            };
173
174        let mut error_msgs: Vec<String> = Vec::new();
175        let mut builder = GreenNodeBuilder::new();
176        builder.start_node(T::ROOT.into());
177        let step_count = steps.len();
178        let steps_vec: Vec<_> = {
179            let mut v = Vec::with_capacity(step_count);
180            v.extend(steps.iter().cloned());
181            // Drop the list iteratively to avoid stack overflow on long lists.
182            crate::list::drop_list(steps);
183            v
184        };
185        let steps = coalesce_empty_rules(steps_vec.into_iter().rev().collect());
186        // Running bracket nesting depth: incremented on openers, decremented on closers.
187        let mut current_depth: i32 = 0;
188        for step in steps.into_iter() {
189            match step {
190                Step::Start(kind) => {
191                    skip_white_with_builder(&mut builder, &mut at, &mut error_msgs);
192                    builder.start_node(kind.into());
193                }
194                Step::End => builder.finish_node(),
195                Step::Error(e) => {
196                    // Skip whitespace so the zero-width error node is positioned at
197                    // the first non-whitespace byte where the missing token was expected,
198                    // not at the end of the previously-consumed token.
199                    skip_white_with_builder(&mut builder, &mut at, &mut error_msgs);
200                    builder.start_node(T::ERROR.into());
201                    // Convert the rowan::SyntaxKind back to the language-specific T
202                    // for a human-readable debug name (e.g. "Expected(Verb)" rather
203                    // than "Expected(SyntaxKind(42))").
204                    let Error::Expected(raw_kind) = e;
205                    let lang_kind: T = raw_kind.into();
206                    error_msgs.push(format!("Expected({:?})", lang_kind));
207                    builder.finish_node();
208                }
209                Step::Bump(fp) => {
210                    skip_white_with_builder(&mut builder, &mut at, &mut error_msgs);
211                    if let Some(i) = tokens.get(at) {
212                        // Record depth at the token's position (before adjusting for
213                        // this token's own bracket_delta, so openers record the outer
214                        // depth and closers record the inner depth).
215                        let depth_at_token = current_depth.clamp(0, 255) as u8;
216                        fingerprint_assignments.push((at, fp, depth_at_token));
217                        current_depth += i.kind.bracket_delta() as i32;
218                        builder.token(i.kind.clone().into(), &i.text);
219                        at += 1;
220                    }
221                }
222                Step::Delete => {
223                    skip_white_with_builder(&mut builder, &mut at, &mut error_msgs);
224                    if let Some(i) = tokens.get(at) {
225                        builder.start_node(T::ERROR.into());
226                        builder.token(i.kind.clone().into(), &i.text);
227                        builder.finish_node();
228                        error_msgs.push(format!("Unexpected({:?})", i.kind));
229                        // Do NOT adjust current_depth — the deleted token is
230                        // being discarded, not consumed into the grammar.
231                        at += 1;
232                    }
233                }
234            }
235        }
236        skip_white_with_builder(&mut builder, &mut at, &mut error_msgs);
237
238        // Wrap any remaining unconsumed non-whitespace tokens in Error nodes.
239        // This happens when the A* search timed out and returned a partial
240        // parse that didn't consume the full input.
241        while at < tokens.len() {
242            let tok = &tokens[at];
243            if tok.kind.skips() {
244                builder.token(tok.kind.clone().into(), &tok.text);
245                at += 1;
246            } else {
247                builder.start_node(T::ERROR.into());
248                builder.token(tok.kind.clone().into(), &tok.text);
249                builder.finish_node();
250                error_msgs.push(format!("Unparsed({:?})", tok.kind));
251                at += 1;
252            }
253        }
254
255        for (idx, fp, depth) in fingerprint_assignments {
256            if let Some(tok) = tokens.get_mut(idx) {
257                tok.set_old_kind(Some(fp));
258                tok.set_old_depth(Some(depth));
259            }
260        }
261
262        builder.finish_node();
263
264        // Build errors in forward document order: error_msgs was collected left-to-right
265        // (first error in document first), so fold in reverse to build a prepend-only List
266        // whose head is the first error.
267        let errors = error_msgs
268            .into_iter()
269            .rfold(List::default(), |acc, msg| acc.prepend(msg));
270
271        Parse {
272            green_node: builder.finish(),
273            errors,
274            suggestions: HashSet::new(),
275        }
276    }
277}
278
279impl Parse {
280    pub fn syntax<L: Language>(&self) -> rowan::SyntaxNode<L> {
281        rowan::SyntaxNode::new_root(self.green_node.clone())
282    }
283}
284
285/// Returns the effective byte span of an error node.
286///
287/// Error nodes in the CST are zero-width: they sit at the byte position of the
288/// missing token but span no characters themselves.  The *effective* span
289/// widens that point in both directions to include skippable tokens (whitespace /
290/// trivia) that immediately surround it — the "dead zone" between the adjacent
291/// real tokens.  This gives callers a non-empty range suitable for highlighting.
292///
293/// Specifically the span is `leading_gap_start..trailing_gap_end`, where:
294/// - `leading_gap_start` is the end of the last non-skippable token *before* the
295///   error (or `0` if the error is at the start of the file).
296/// - `trailing_gap_end` is the start of the first non-skippable token at or
297///   after the error position (extended through any immediately following
298///   skippable tokens).
299///
300/// # Example
301///
302/// For input `"  <b> <c> }"` with a missing `{`, the error node sits at byte 2
303/// (after the two leading spaces are consumed as trivia by the `Start` step
304/// preceding the error).  The effective span is `0..2`, covering those two bytes.
305///
306/// For input `"<a> <b> <c>"` missing a trailing `.`, where the error appears at
307/// the end (byte 11) with no trailing whitespace, the span is `11..11` (zero-width).
308pub fn effective_error_span<L>(error_node: &rowan::SyntaxNode<L>) -> Range<usize>
309where
310    L: Language,
311    L::Kind: TokenTrait,
312{
313    use rowan::NodeOrToken;
314
315    let text_range = error_node.text_range();
316    let error_start: usize = text_range.start().into();
317    let error_end: usize = text_range.end().into();
318
319    // Non-zero-width Error nodes (e.g. from Step::Delete or Unparsed) contain
320    // actual tokens — use their text range directly.
321    if error_start != error_end {
322        return error_start..error_end;
323    }
324
325    // Zero-width Error nodes (from Step::Error insertions): widen to the
326    // surrounding whitespace gap for a visible highlight.
327    let root = error_node
328        .ancestors()
329        .last()
330        .unwrap_or_else(|| error_node.clone());
331
332    let mut span_start = 0usize;
333    let mut span_end = error_start;
334    let mut past_error = false;
335
336    for elem in root.descendants_with_tokens() {
337        let NodeOrToken::Token(tok) = elem else {
338            continue;
339        };
340        let tok_start: usize = tok.text_range().start().into();
341
342        if !past_error {
343            if tok_start >= error_start {
344                // Transition to trailing side — handle this token below without
345                // skipping it so span_end is computed from the right position.
346                past_error = true;
347            } else {
348                if !tok.kind().skips() {
349                    span_start = tok.text_range().end().into();
350                }
351                continue;
352            }
353        }
354
355        // Trailing side: stop at the first non-skippable token and record its
356        // start as span_end; extend through consecutive skippable tokens.
357        if !tok.kind().skips() {
358            span_end = tok_start;
359            break;
360        }
361        span_end = tok.text_range().end().into();
362    }
363
364    span_start..span_end
365}
366
367use std::{collections::HashSet, ops::Range};
368
369use rowan::{GreenNode, GreenNodeBuilder, Language};
370
371use crate::{TokenTrait, list::List};
372
373#[derive(Clone, Debug, PartialEq, Eq)]
374pub enum Error {
375    Expected(rowan::SyntaxKind),
376}
377
378#[derive(Clone, Debug, PartialEq, Eq)]
379pub enum Step<T> {
380    Start(T),
381    Error(Error),
382    End,
383    Bump(crate::Fingerprint),
384    /// Delete (skip) the current token. The token still appears in the CST
385    /// wrapped in an Error node, but no grammar rule consumes it.
386    Delete,
387}
388impl<T: TokenTrait> Step<T> {
389    pub fn start(kind: T) -> Self {
390        Step::Start(kind)
391    }
392    pub fn error(error: Error) -> Self {
393        Self::Error(error)
394    }
395
396    pub fn end() -> Self {
397        Step::End
398    }
399}