teleparse/syntax/
error.rs

1use itertools::Itertools;
2use teleparse_macros::ToSpan;
3
4use crate::{Lexicon, Span};
5
6use super::FirstSet;
7
8/// Error encountered during parsing
9#[derive(Debug, Clone, ToSpan, PartialEq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize))]
11pub struct Error<L: Lexicon> {
12    pub span: Span,
13    pub data: ErrorKind<L>,
14}
15
16impl<L: Lexicon> Error<L> {
17    pub fn new<S: Into<Span>>(span: S, data: ErrorKind<L>) -> Self {
18        Self {
19            span: span.into(),
20            data,
21        }
22    }
23
24    pub fn message(&self, input: &str) -> String {
25        match &self.data {
26            ErrorKind::Custom(msg) => msg.clone(),
27            ErrorKind::UnexpectedCharacters => {
28                format!("Unexpected: {}", self.span.get(input))
29            },
30            ErrorKind::UnexpectedTokens => {
31                format!("Unexpected token(s): {}", self.span.get(input))
32            },
33            ErrorKind::Expecting(set) => {
34                let set = set.as_terminal_set().to_repr().into_iter().join(", ");
35                format!("Expecting one of {set}")
36            },
37            ErrorKind::UnexpectedEof => "Unexpected end of file".to_string(),
38            ErrorKind::UnexpectedNoAdvanceInLoop => "Unexpected: Parser did not advance in a loop. The grammar is probably not LL(1), and this is a bug since the parser should catch that before parsing.".to_string(),
39        }
40    }
41}
42
43#[derive(Debug, Clone, PartialEq)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize))]
45pub enum ErrorKind<L: Lexicon> {
46    Custom(String),
47    UnexpectedCharacters,
48    UnexpectedTokens,
49    Expecting(FirstSet<L>),
50    UnexpectedEof,
51    UnexpectedNoAdvanceInLoop,
52}
53
54/// Result of parsing an AST node
55pub enum Result<T, L: Lexicon> {
56    /// The AST node was successfully parsed, with the corresponding tokens consumed
57    Success(T),
58    /// The parser panicked while parsing the AST node, but it was able to skip some tokens and
59    /// recover.
60    Recovered(T, Vec<Error<L>>),
61    /// The parser panicked while parsing the AST node, and was unable to recover.
62    /// The parser might have advanced its position in the input.
63    Panic(Vec<Error<L>>),
64}
65
66impl<T, L: Lexicon> From<(T, Vec<Error<L>>)> for Result<T, L> {
67    #[inline]
68    fn from((value, errors): (T, Vec<Error<L>>)) -> Self {
69        if errors.is_empty() {
70            Result::Success(value)
71        } else {
72            Result::Recovered(value, errors)
73        }
74    }
75}
76
77impl<T, L: Lexicon> Result<T, L> {
78    #[inline]
79    pub fn map<T2, F: FnOnce(T) -> T2>(self, f: F) -> Result<T2, L> {
80        match self {
81            Self::Success(obj) => Result::Success(f(obj)),
82            Self::Recovered(obj, errors) => Result::Recovered(f(obj), errors),
83            Self::Panic(errors) => Result::Panic(errors),
84        }
85    }
86}
87
88#[macro_export]
89#[doc(hidden)]
90macro_rules! handle_result {
91    ($errors:ident, $parse:expr) => {{
92        let result = $parse;
93        match result {
94            $crate::syntax::Result::Success(x) => x,
95            $crate::syntax::Result::Recovered(x, e) => {
96                $errors.extend(e);
97                x
98            }
99            $crate::syntax::Result::Panic(e) => {
100                $errors.extend(e);
101                return $crate::syntax::Result::Panic($errors);
102            }
103        }
104    }};
105}