ron_reboot/utf8_parser/
error.rs

1//! An error type, [`ErrorTree`], designed to retain much more useful
2//! information about parse failures than the built-in nom error types.
3
4use std::{
5    error::Error,
6    fmt::{self, Debug, Display, Formatter},
7};
8
9use crate::{location::Location, utf8_parser::Input, util::write_pretty_list};
10
11pub type InputParseError<'a> = ErrorTree<Input<'a>>;
12
13#[derive(Debug)]
14pub struct _PrivateConstructor {
15    private: (),
16}
17
18#[derive(Debug)]
19pub enum InputParseErr<'a> {
20    /// The utf8_parser had an error (recoverable)
21    Recoverable(InputParseError<'a>),
22    /// The utf8_parser had an unrecoverable error: we got to the right
23    /// branch and we know other branches won't work, so backtrack
24    /// as fast as possible
25    Fatal(InputParseError<'a>),
26}
27
28impl<'a> InputParseErr<'a> {
29    pub fn recoverable(e: InputParseError<'a>) -> Self {
30        InputParseErr::Recoverable(e)
31    }
32
33    pub fn fatal(e: InputParseError<'a>) -> Self {
34        InputParseErr::Fatal(e)
35    }
36
37    pub fn is_recoverable(&self) -> bool {
38        match self {
39            InputParseErr::Recoverable(_) => true,
40            InputParseErr::Fatal(_) => false,
41        }
42    }
43}
44
45impl<'a> Display for InputParseErr<'a> {
46    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
47        match self {
48            InputParseErr::Recoverable(e) => write!(f, "{}", e),
49            InputParseErr::Fatal(e) => write!(f, "{}", e),
50        }
51    }
52}
53
54#[non_exhaustive]
55#[derive(Debug, Copy, Clone, PartialEq, Eq)]
56pub enum Expectation {
57    /// A string tag was expected.
58    Tag(&'static str),
59
60    /// A specific character was expected.
61    Char(char),
62
63    /// One of the chars in the str
64    OneOfChars(&'static str),
65
66    /// One of the chars in the str
67    OneOfTags(&'static [&'static str]),
68
69    /// One of the classes in the array
70    OneOfExpectations(&'static [Self]),
71
72    /// An ASCII letter (`[a-zA-Z]`) was expected.
73    Alpha,
74
75    /// A decimal digit (`[0-9]`) was expected.
76    Digit,
77
78    /// A decimal digit (`[1-9]`) was expected.
79    DigitFirst,
80
81    /// A hexadecimal digit (`[0-9a-fA-F]`) was expected.
82    HexDigit,
83
84    /// A hexadecimal digit (`[0-9a-fA-F]`) was expected.
85    UnicodeHexSequence { got: u32 },
86
87    /// An octal digit (`[0-7]`) was expected.
88    OctDigit,
89
90    /// An alphanumeric character (`[0-9a-zA-Z]`) was expected.
91    AlphaNumeric,
92
93    /// A space or tab was expected.
94    Space,
95
96    /// The ned of a raw string was expected.
97    RawStringEnd,
98
99    /// The closing */ of a block comment.
100    BlockCommentEnd,
101
102    /// A space, tab, newline, or carriage return was expected.
103    Multispace,
104
105    /// `"\r\n"` was expected.
106    CrLf,
107
108    /// Eof was expected.
109    Eof,
110
111    /// Expected something; ie, not Eof.
112    Something,
113}
114
115impl Display for Expectation {
116    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
117        match *self {
118            Expectation::Tag(tag) => write!(f, "{:?}", tag),
119            Expectation::Char(c) => write!(f, "{:?}", c),
120            Expectation::OneOfChars(one_of) => {
121                write_pretty_list(f, one_of.chars(), |f, c| write!(f, "{:?}", c))
122            }
123            Expectation::OneOfTags(one_of) => {
124                write_pretty_list(f, one_of.iter(), |f, c| write!(f, "{:?}", c))
125            }
126            Expectation::OneOfExpectations(one_of) => {
127                write_pretty_list(f, one_of.iter(), |f, c| write!(f, "{}", c))
128            }
129            Expectation::Alpha => write!(f, "an ascii letter"),
130            Expectation::Digit => write!(f, "an ascii digit"),
131            Expectation::DigitFirst => write!(f, "a non-zero ascii digit [1-9]"),
132            Expectation::HexDigit => write!(f, "a hexadecimal digit"),
133            Expectation::OctDigit => write!(f, "an octal digit"),
134            Expectation::AlphaNumeric => write!(f, "an ascii alphanumeric character"),
135            Expectation::Space => write!(f, "a space or tab"),
136            Expectation::Multispace => write!(f, "whitespace"),
137            Expectation::BlockCommentEnd => write!(f, "end of block comment (`*/`)"),
138            Expectation::Eof => write!(f, "eof"),
139            Expectation::CrLf => write!(f, "CRLF"),
140            Expectation::Something => write!(f, "not eof"),
141            Expectation::UnicodeHexSequence { got } => {
142                write!(f, "a valid unicode hex sequence (got 0x{:X})", got)
143            }
144            Expectation::RawStringEnd => write!(f, "closing raw string sequence"),
145        }
146    }
147}
148
149/// These are the different specific things that can go wrong at a particular
150/// location during a nom parse. Many of these are collected into an
151/// [`ErrorTree`].
152#[derive(Debug)]
153pub enum BaseErrorKind {
154    /// Something specific was expected, such as a specific
155    /// [character][Expectation::Char] or any [digit](Expectation::Digit).
156    /// See [`Expectation`] for details.
157    Expected(Expectation),
158
159    External(Box<dyn Error + Send + Sync + 'static>),
160}
161
162impl Display for BaseErrorKind {
163    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
164        match *self {
165            BaseErrorKind::Expected(expectation) => write!(f, "expected {}", expectation),
166            BaseErrorKind::External(ref err) => {
167                writeln!(f, "external error:")?;
168                write!(f, "{}", indent(err))
169            }
170        }
171    }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175pub enum StackContext {
176    /// The [`context`][crate::parser_ext::ParserExt::context] combinator
177    /// attached a message as context for a subparser error.
178    Context(&'static str),
179}
180
181impl Display for StackContext {
182    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
183        match *self {
184            StackContext::Context(ctx) => write!(f, "could not match {:?}", ctx),
185        }
186    }
187}
188
189#[derive(Debug)]
190pub enum ErrorTree<I> {
191    /// A specific error event at a specific location. Often this will indicate
192    /// that something like a tag or character was expected at that location.
193    Base {
194        /// The location of this error in the input
195        location: I,
196
197        /// The specific error that occurred
198        kind: BaseErrorKind,
199    },
200
201    /// A stack indicates a chain of error contexts was provided. The stack
202    /// should be read "backwards"; that is, errors *earlier* in the `Vec`
203    /// occurred "sooner" (deeper in the call stack).
204    Stack {
205        /// The original error
206        base: Box<Self>,
207
208        /// Whether it was indicated that the "final" useful context has been pushed onto the stack
209        finalized: bool,
210
211        /// The stack of contexts attached to that error
212        contexts: Vec<(I, StackContext)>,
213    },
214
215    /// A series of parsers were tried at the same location (for instance, via
216    /// the `alt2` combinator) and all of them failed.
217    Alt(Vec<Self>),
218    // TODO: in a future version of nom-supreme, elaborate on the specific
219    // type combinations here. For instance:
220    // - Alt can only contain Stack or Base
221    // - Stack has a single Base or Alt, followed by a series of contexts
222    //   (Context or Kind)
223}
224
225impl<I> ErrorTree<I> {
226    pub(crate) fn max_location(&self) -> &I
227    where
228        I: Ord,
229    {
230        match self {
231            ErrorTree::Base { location, .. } => location,
232            ErrorTree::Stack { base, .. } => base.max_location(),
233            ErrorTree::Alt(v) => v.iter().map(ErrorTree::max_location).max().unwrap(),
234        }
235    }
236
237    pub(crate) fn expected(location: I, expectation: Expectation) -> Self {
238        ErrorTree::Base {
239            location,
240            kind: BaseErrorKind::Expected(expectation),
241        }
242    }
243
244    pub(crate) fn alt(first: Self, second: Self) -> Self {
245        match (first, second) {
246            (ErrorTree::Alt(mut alt), ErrorTree::Alt(alt2)) => {
247                alt.extend(alt2);
248                ErrorTree::Alt(alt)
249            }
250            (ErrorTree::Alt(mut alt), x) | (x, ErrorTree::Alt(mut alt)) => {
251                // TODO: should we preserve order?
252                alt.push(x);
253                ErrorTree::Alt(alt)
254            }
255            (first, second) => ErrorTree::Alt(vec![first, second]),
256        }
257    }
258
259    fn map_locations_ref<T>(self, convert_location: &mut impl FnMut(I) -> T) -> ErrorTree<T> {
260        match self {
261            ErrorTree::Base { location, kind } => ErrorTree::Base {
262                location: convert_location(location),
263                kind,
264            },
265            ErrorTree::Stack {
266                base,
267                contexts,
268                finalized,
269            } => ErrorTree::Stack {
270                base: Box::new(base.map_locations_ref(convert_location)),
271                contexts: contexts
272                    .into_iter()
273                    .map(|(location, context)| (convert_location(location), context))
274                    .collect(),
275                finalized,
276            },
277            ErrorTree::Alt(siblings) => ErrorTree::Alt(
278                siblings
279                    .into_iter()
280                    .map(|err| err.map_locations_ref(convert_location))
281                    .collect(),
282            ),
283        }
284    }
285
286    pub(crate) fn map_locations<T>(self, mut convert_location: impl FnMut(I) -> T) -> ErrorTree<T> {
287        self.map_locations_ref(&mut convert_location)
288    }
289
290    pub(crate) fn calc_locations(self) -> ErrorTree<Location>
291    where
292        I: Into<Location>,
293    {
294        self.map_locations(|i| i.into())
295    }
296}
297
298impl<I: Display> Display for ErrorTree<I> {
299    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
300        match self {
301            ErrorTree::Base { location, kind } => write!(f, "{} at {:#}", kind, location),
302            ErrorTree::Stack {
303                contexts,
304                base,
305                finalized: _,
306            } => {
307                contexts.iter().rev().try_for_each(|(location, context)| {
308                    writeln!(f, "{} at {:#} because", context, location)
309                })?;
310
311                write!(f, "{}", indent(base))
312            }
313            ErrorTree::Alt(siblings) => {
314                writeln!(f, "none of these matched:")?;
315                write!(
316                    f,
317                    "{}",
318                    indent(
319                        siblings
320                            .iter()
321                            .map(ToString::to_string)
322                            .collect::<Vec<_>>()
323                            .join(" or\n")
324                    )
325                )
326            }
327        }
328    }
329}
330
331impl<I: Display + Debug> Error for ErrorTree<I> {}
332
333impl<I> ErrorTree<I> {
334    /// Similar to append: Create a new error with some added context
335    pub fn add_context(location: I, ctx: &'static str, final_context: bool, other: Self) -> Self {
336        let context = (location, StackContext::Context(ctx));
337
338        match other {
339            // This is already a stack, so push on to it
340            ErrorTree::Stack {
341                mut contexts,
342                base,
343                finalized: false,
344            } => {
345                contexts.push(context);
346                ErrorTree::Stack {
347                    base,
348                    contexts,
349                    finalized: final_context,
350                }
351            }
352
353            ErrorTree::Stack {
354                finalized: true, ..
355            } => other,
356
357            // This isn't a stack, create a new stack
358            base => ErrorTree::Stack {
359                base: Box::new(base),
360                contexts: vec![context],
361                finalized: final_context,
362            },
363        }
364    }
365}
366
367impl From<ErrorTree<Location>> for crate::error::Error {
368    fn from(e: ErrorTree<Location>) -> Self {
369        let max_location = *e.max_location();
370        let max_location: Location = max_location.into();
371
372        Self {
373            kind: crate::error::ErrorKind::ParseError(e.to_string()),
374            context: None,
375        }
376        .context_loc(
377            max_location,
378            Location {
379                line: max_location.line,
380                column: max_location.column + 1,
381            },
382        )
383    }
384}
385
386impl From<InputParseError<'_>> for crate::error::Error {
387    fn from(e: InputParseError) -> Self {
388        e.calc_locations().into()
389    }
390}
391
392pub struct Indented(String);
393
394pub fn indent(display: impl Display) -> Indented {
395    Indented(display.to_string())
396}
397
398impl Display for Indented {
399    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
400        let mut s = self.0.as_str();
401        let mut need_indent = true;
402        loop {
403            match need_indent {
404                // We don't need an indent. Scan for the end of the line
405                false => match s.as_bytes().iter().position(|&b| b == b'\n') {
406                    // No end of line in the input; write the entire string
407                    None => break f.write_str(s),
408
409                    // We can see the end of the line. Write up to and including
410                    // that newline, then request an indent
411                    Some(len) => {
412                        let (head, tail) = s.split_at(len + 1);
413                        f.write_str(head)?;
414                        need_indent = true;
415                        s = tail;
416                    }
417                },
418                // We need an indent. Scan for the beginning of the next
419                // non-empty line.
420                true => match s.as_bytes().iter().position(|&b| b != b'\n') {
421                    // No non-empty lines in input, write the entire string
422                    None => break f.write_str(s),
423
424                    // We can see the next non-empty line. Write up to the
425                    // beginning of that line, then insert an indent, then
426                    // continue.
427                    Some(len) => {
428                        let (head, tail) = s.split_at(len);
429                        f.write_str(head)?;
430                        f.write_str("    ")?;
431                        need_indent = false;
432                        s = tail;
433                    }
434                },
435            }
436        }
437    }
438}