Skip to main content

eure_parol/
error.rs

1//! Parse error types for Eure.
2use std::fmt;
3
4use eure_tree::tree::InputSpan;
5use parol_runtime::{LexerError, ParolError, ParserError, SyntaxError};
6
7/// A parse error with extracted span and message information.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct EureParseError {
10    pub entries: Vec<ParseErrorEntry>,
11}
12
13impl EureParseError {
14    pub fn new_entry(span: Option<InputSpan>, message: String, kind: ParseErrorKind) -> Self {
15        Self {
16            entries: vec![ParseErrorEntry {
17                span,
18                message,
19                kind,
20                source: vec![],
21            }],
22        }
23    }
24}
25
26impl fmt::Display for EureParseError {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        for (i, entry) in self.entries.iter().enumerate() {
29            if i > 0 {
30                writeln!(f)?;
31            }
32            write!(f, "{}", entry)?;
33        }
34        Ok(())
35    }
36}
37
38impl std::error::Error for EureParseError {}
39
40impl fmt::Display for ParseErrorEntry {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        write!(f, "{}", self.message)?;
43        if let Some(span) = &self.span {
44            write!(f, " at {}..{}", span.start, span.end)?;
45        }
46        Ok(())
47    }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct ParseErrorEntry {
52    /// The span in the input where the error occurred, if available.
53    pub span: Option<InputSpan>,
54    /// The error message.
55    pub message: String,
56    /// The kind of parse error.
57    pub kind: ParseErrorKind,
58    pub source: Vec<ParseErrorEntry>,
59}
60
61/// The kind of parse error.
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub enum ParseErrorKind {
64    /// A syntax error (unexpected token, etc.)
65    SyntaxError {
66        unexpected_tokens: Vec<UnexpectedToken>,
67        expected_tokens: Vec<String>,
68    },
69    UnprocessedInput,
70    /// A lexer error (invalid character, etc.)
71    LexerError,
72    /// Other error types
73    Other,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct UnexpectedToken {
78    pub name: String,
79    pub token_type: String,
80    pub token: InputSpan,
81}
82
83impl From<parol_runtime::UnexpectedToken> for UnexpectedToken {
84    fn from(
85        parol_runtime::UnexpectedToken {
86            name,
87            token_type,
88            token,
89        }: parol_runtime::UnexpectedToken,
90    ) -> Self {
91        Self {
92            name,
93            token_type,
94            token: InputSpan {
95                start: token.start,
96                end: token.end,
97            },
98        }
99    }
100}
101
102impl From<ParolError> for EureParseError {
103    fn from(error: ParolError) -> Self {
104        match error {
105            ParolError::ParserError(parser_error) => parser_error.into(),
106            ParolError::LexerError(lexer_error) => lexer_error.into(),
107            ParolError::UserError(error) => error.into(),
108        }
109    }
110}
111
112impl From<ParserError> for EureParseError {
113    fn from(error: ParserError) -> Self {
114        match error {
115            ParserError::TreeError { source } => source.into(),
116            ParserError::DataError(error) => {
117                EureParseError::new_entry(None, error.to_string(), ParseErrorKind::Other)
118            }
119            ParserError::PredictionError { cause } => {
120                EureParseError::new_entry(None, cause, ParseErrorKind::Other)
121            }
122            ParserError::SyntaxErrors { entries } => EureParseError {
123                entries: entries.into_iter().map(ParseErrorEntry::from).collect(),
124            },
125            ParserError::UnprocessedInput {
126                input: _,
127                last_token,
128            } => EureParseError::new_entry(
129                Some(InputSpan {
130                    start: last_token.start,
131                    end: last_token.end,
132                }),
133                "Unprocessed input".to_string(),
134                ParseErrorKind::UnprocessedInput,
135            ),
136            ParserError::Unsupported {
137                context,
138                error_location,
139            } => EureParseError::new_entry(
140                Some(InputSpan {
141                    start: error_location.start,
142                    end: error_location.end,
143                }),
144                format!("Unsupported: {context}"),
145                ParseErrorKind::Other,
146            ),
147            ParserError::TooManyErrors { count } => EureParseError::new_entry(
148                None,
149                format!("Too many errors: {count}"),
150                ParseErrorKind::Other,
151            ),
152            ParserError::RecoveryFailed => EureParseError::new_entry(
153                None,
154                "Recovery failed".to_string(),
155                ParseErrorKind::Other,
156            ),
157            ParserError::InternalError(error) => EureParseError::new_entry(
158                None,
159                format!("Internal error: {error}"),
160                ParseErrorKind::Other,
161            ),
162        }
163    }
164}
165
166impl From<parol_runtime::syntree::Error> for EureParseError {
167    fn from(error: parol_runtime::syntree::Error) -> Self {
168        EureParseError::new_entry(None, error.to_string(), ParseErrorKind::Other)
169    }
170}
171
172impl From<parol_runtime::SyntaxError> for ParseErrorEntry {
173    fn from(error: parol_runtime::SyntaxError) -> Self {
174        let SyntaxError {
175            cause,
176            input: _,
177            error_location,
178            unexpected_tokens,
179            expected_tokens,
180            source,
181        } = error;
182        ParseErrorEntry {
183            span: Some(InputSpan {
184                start: error_location.start,
185                end: error_location.end,
186            }),
187            message: cause,
188            kind: ParseErrorKind::SyntaxError {
189                unexpected_tokens: unexpected_tokens
190                    .into_iter()
191                    .map(UnexpectedToken::from)
192                    .collect(),
193                expected_tokens: expected_tokens.iter().cloned().collect(),
194            },
195            source: source
196                .map(|source| EureParseError::from(*source).entries)
197                .unwrap_or_default(),
198        }
199    }
200}
201
202impl From<LexerError> for EureParseError {
203    fn from(error: LexerError) -> Self {
204        match error {
205            LexerError::TokenBufferEmptyError => EureParseError::new_entry(
206                None,
207                "Token buffer empty".to_string(),
208                ParseErrorKind::LexerError,
209            ),
210            LexerError::InternalError(error) => EureParseError::new_entry(
211                None,
212                format!("Internal error: {error}"),
213                ParseErrorKind::Other,
214            ),
215            LexerError::LookaheadExceedsMaximum => EureParseError::new_entry(
216                None,
217                "Lookahead exceeds maximum".to_string(),
218                ParseErrorKind::LexerError,
219            ),
220            LexerError::LookaheadExceedsTokenBufferLength => EureParseError::new_entry(
221                None,
222                "Lookahead exceeds token buffer length".to_string(),
223                ParseErrorKind::LexerError,
224            ),
225            LexerError::ScannerStackEmptyError => EureParseError::new_entry(
226                None,
227                "Scanner stack empty".to_string(),
228                ParseErrorKind::LexerError,
229            ),
230            LexerError::RecoveryError(error) => EureParseError::new_entry(
231                None,
232                format!("Recovery error: {error}"),
233                ParseErrorKind::LexerError,
234            ),
235        }
236    }
237}
238
239impl From<anyhow::Error> for EureParseError {
240    fn from(error: anyhow::Error) -> Self {
241        EureParseError::new_entry(None, error.to_string(), ParseErrorKind::Other)
242    }
243}