lwb_parser/parser/peg/
parse_error.rs

1use crate::parser::peg::parser_core_expression::ExpressionContext;
2use crate::sources::character_class::CharacterClass;
3use crate::sources::span::Span;
4use itertools::Itertools;
5use miette::{Diagnostic, LabeledSpan, Severity, SourceCode};
6use std::cmp::Ordering;
7use std::fmt::{Display, Formatter};
8use thiserror::Error;
9
10/// A parsing error represents a single error that occurred during parsing.
11/// The parsing error occurs at a certain position in a file, represented by the span.
12/// The parsing error consists of multiple `ParseErrorSub`, which each represent a single thing that went wrong at this position.
13#[derive(Debug, Clone, Error)]
14#[error("A parse error occured!")]
15pub struct PEGParseError {
16    pub span: Span,
17    pub expected: Vec<Expect>,
18    pub fail_left_rec: bool,
19    pub fail_loop: bool,
20    /// first the name of the sort that caused the error, then the error message
21    pub msgs: Vec<(String, String)>,
22}
23
24// add error bound so IDEs don't complain. Error is always derived by thiserror.
25impl Diagnostic for PEGParseError
26where
27    PEGParseError: std::error::Error,
28{
29    /// Diagnostic severity. This may be used by [ReportHandler]s to change the
30    /// display format of this diagnostic.
31    ///
32    /// If `None`, reporters should treat this as [Severity::Error]
33    fn severity(&self) -> Option<Severity> {
34        Some(Severity::Error)
35    }
36
37    /// Source code to apply this Diagnostic's [Diagnostic::labels] to.
38    fn source_code(&self) -> Option<&dyn SourceCode> {
39        Some(&self.span)
40    }
41
42    /// Labels to apply to this Diagnostic's [Diagnostic::source_code]
43    fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
44        // immediately return when the grammar gave a custom error
45        if let Some(i) = self
46            .expected
47            .iter()
48            .find(|i| matches!(i, Expect::Custom(_)))
49        {
50            let label = LabeledSpan::new_with_span(Some(i.to_string()), self.span.clone());
51
52            return Some(Box::new(vec![label].into_iter()));
53        }
54
55        let expect_str = self.expected.iter().map(|exp| exp.to_string()).join(", ");
56        let mut labels = vec![];
57
58        //Leftrec label
59        if self.fail_left_rec {
60            labels.push(LabeledSpan::new_with_span(Some("Encountered left recursion here. This is a problem with the grammar, and may hide other errors.".to_string()), self.span.clone()));
61        }
62
63        //Loop label
64        if self.fail_loop {
65            labels.push(LabeledSpan::new_with_span(Some("Encountered an infinite loop here. This is a problem with the grammar, and may hide other errors.".to_string()), self.span.clone()));
66        }
67
68        //Expected label
69        match self.expected.len() {
70            0 => {}
71            1 => labels.push(LabeledSpan::new_with_span(
72                Some(format!("Expected {} here", expect_str)),
73                self.span.clone(),
74            )),
75            _ => labels.push(LabeledSpan::new_with_span(
76                Some(format!("Expected one of {} here", expect_str)),
77                self.span.clone(),
78            )),
79        }
80
81        Some(Box::new(labels.into_iter()))
82    }
83
84    fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
85        let mut helps = Vec::new();
86        for (sort, msg) in &self.msgs {
87            helps.push(format!("if a {sort} would be parsed here then {msg}"));
88        }
89
90        if helps.is_empty() {
91            None
92        } else {
93            Some(Box::new(helps.join("\n")))
94        }
95    }
96}
97
98impl PEGParseError {
99    pub fn expect(span: Span, expect: Expect, sort_context: &ExpressionContext) -> Self {
100        PEGParseError {
101            span,
102            expected: vec![expect],
103            fail_left_rec: false,
104            fail_loop: false,
105            msgs: if let Some(name) = sort_context.name {
106                sort_context
107                    .error
108                    .iter()
109                    .map(|i| (name.to_string(), i.to_string()))
110                    .collect()
111            } else {
112                vec![]
113            },
114        }
115    }
116
117    pub fn fail_left_recursion(span: Span) -> Self {
118        PEGParseError {
119            span,
120            expected: vec![],
121            fail_left_rec: true,
122            fail_loop: false,
123            msgs: vec![],
124        }
125    }
126
127    pub fn fail_loop(span: Span) -> Self {
128        PEGParseError {
129            span,
130            expected: vec![],
131            fail_left_rec: false,
132            fail_loop: true,
133            msgs: vec![],
134        }
135    }
136}
137
138/// Represents a single thing that went wrong at this position.
139#[derive(Debug, Clone, Hash, Eq, PartialEq)]
140pub enum Expect {
141    /// Expect a character from a certain char class to be there, but it was not.
142    ExpectCharClass(CharacterClass),
143
144    /// Expect a certain string (keyword) to be there, but it was not.
145    ExpectString(String),
146
147    /// Expect a certain sort
148    ExpectSort(String),
149
150    /// This happens when not the entire input was parsed, but also no errors occurred during parsing.
151    NotEntireInput(),
152
153    /// This happens when a constructor has an error annotation
154    Custom(String),
155}
156
157impl Display for Expect {
158    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
159        match self {
160            Expect::ExpectCharClass(cc) => {
161                write!(f, "{}", cc)
162            }
163            Expect::ExpectString(s) => {
164                write!(f, "\'{}\'", s)
165            }
166            Expect::ExpectSort(s) => {
167                write!(f, "{}", s)
168            }
169            Expect::NotEntireInput() => {
170                write!(f, "more input")
171            }
172            Expect::Custom(e) => {
173                write!(f, "{e}")
174            }
175        }
176    }
177}
178
179impl PEGParseError {
180    /// Combine multiple parse errors. When one has precedence over
181    /// another, the highest precedence error is kept and the other
182    /// is discarded.
183    ///
184    /// Highest precedence is defined as furthest starting position for now. This might be changed later.
185    pub fn combine(mut self, mut other: PEGParseError) -> PEGParseError {
186        assert_eq!(self.span.source.name(), other.span.source.name());
187
188        //Compare the starting positions of the span
189        match self.span.position.cmp(&other.span.position) {
190            Ordering::Less => other,
191            Ordering::Greater => self,
192            Ordering::Equal => {
193                //The span is extended such that the longest one is kept.
194                self.span.length = self.span.length.max(other.span.length);
195                //Merge the expected tokens
196                self.expected.append(&mut other.expected);
197                //Left recursion
198                self.fail_left_rec |= other.fail_left_rec;
199
200                self.msgs.extend(other.msgs);
201
202                self
203            }
204        }
205    }
206
207    /// A helper that combines optional parse errors, and returns an optional parse error if either exists.
208    /// If both exist, use `ParseError::combine` to combine the errors.
209    pub fn combine_option_parse_error(
210        a: Option<PEGParseError>,
211        b: Option<PEGParseError>,
212    ) -> Option<PEGParseError> {
213        match (a, b) {
214            (None, None) => None,
215            (None, Some(e)) => Some(e),
216            (Some(e), None) => Some(e),
217            (Some(e1), Some(e2)) => Some(e1.combine(e2)),
218        }
219    }
220}