mago_parser/
error.rs

1use mago_reporting::Annotation;
2use mago_reporting::Issue;
3use serde::Deserialize;
4use serde::Serialize;
5
6use mago_ast::ast::*;
7use mago_lexer::error::SyntaxError;
8use mago_span::HasSpan;
9use mago_span::Position;
10use mago_span::Span;
11use mago_token::TokenKind;
12
13#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
14pub enum ParseError {
15    SyntaxError(SyntaxError),
16    UnexpectedEndOfFile(Vec<TokenKind>, Position),
17    UnexpectedToken(Vec<TokenKind>, TokenKind, Span),
18    UnclosedLiteralString(LiteralStringKind, Span),
19}
20
21impl HasSpan for ParseError {
22    fn span(&self) -> Span {
23        match &self {
24            ParseError::SyntaxError(syntax_error) => syntax_error.span(),
25            ParseError::UnexpectedEndOfFile(_, position) => Span::new(*position, *position),
26            ParseError::UnexpectedToken(_, _, span) => *span,
27            ParseError::UnclosedLiteralString(_, span) => *span,
28        }
29    }
30}
31
32impl std::fmt::Display for ParseError {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        let message = match self {
35            ParseError::SyntaxError(e) => {
36                return write!(f, "{}", e);
37            }
38            ParseError::UnexpectedEndOfFile(expected, _) => {
39                let expected = expected.iter().map(|kind| kind.to_string()).collect::<Vec<_>>().join("`, `");
40
41                if expected.is_empty() {
42                    "Unexpected end of file".to_string()
43                } else if expected.len() == 1 {
44                    format!("Expected `{}` before end of file", expected)
45                } else {
46                    format!("Expected one of `{}` before end of file", expected)
47                }
48            }
49            ParseError::UnexpectedToken(expected, found, _) => {
50                let expected = expected.iter().map(|kind| kind.to_string()).collect::<Vec<_>>().join("`, `");
51
52                let found = found.to_string();
53
54                if expected.is_empty() {
55                    format!("Unexpected token `{}`", found)
56                } else if expected.len() == 1 {
57                    format!("Expected `{}`, found `{}`", expected, found)
58                } else {
59                    format!("Expected one of `{}`, found `{}`", expected, found)
60                }
61            }
62            ParseError::UnclosedLiteralString(kind, _) => match kind {
63                LiteralStringKind::SingleQuoted => "Unclosed single-quoted string".to_string(),
64                LiteralStringKind::DoubleQuoted => "Unclosed double-quoted string".to_string(),
65            },
66        };
67
68        write!(f, "{}", message)
69    }
70}
71
72impl std::error::Error for ParseError {
73    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74        match self {
75            ParseError::SyntaxError(e) => Some(e),
76            _ => None,
77        }
78    }
79}
80
81impl From<SyntaxError> for ParseError {
82    fn from(error: SyntaxError) -> Self {
83        ParseError::SyntaxError(error)
84    }
85}
86
87impl From<&ParseError> for Issue {
88    fn from(error: &ParseError) -> Self {
89        let span = error.span();
90
91        Issue::error(error.to_string()).with_annotation(Annotation::primary(span).with_message("Invalid syntax."))
92    }
93}