mago_syntax/
error.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_reporting::Annotation;
5use mago_reporting::Issue;
6use mago_span::HasPosition;
7use mago_span::HasSpan;
8use mago_span::Position;
9use mago_span::Span;
10
11use crate::ast::LiteralStringKind;
12use crate::token::TokenKind;
13
14#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
15pub enum SyntaxError {
16    UnexpectedToken(u8, Position),
17    UnrecognizedToken(u8, Position),
18    UnexpectedEndOfFile(Position),
19}
20
21#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
22pub enum ParseError {
23    SyntaxError(SyntaxError),
24    UnexpectedEndOfFile(Vec<TokenKind>, Position),
25    UnexpectedToken(Vec<TokenKind>, TokenKind, Span),
26    UnclosedLiteralString(LiteralStringKind, Span),
27}
28
29impl HasSpan for ParseError {
30    fn span(&self) -> Span {
31        match &self {
32            ParseError::SyntaxError(syntax_error) => syntax_error.span(),
33            ParseError::UnexpectedEndOfFile(_, position) => Span::new(*position, *position),
34            ParseError::UnexpectedToken(_, _, span) => *span,
35            ParseError::UnclosedLiteralString(_, span) => *span,
36        }
37    }
38}
39
40impl HasSpan for SyntaxError {
41    fn span(&self) -> Span {
42        let position = match self {
43            Self::UnexpectedToken(_, p) => *p,
44            Self::UnrecognizedToken(_, p) => *p,
45            Self::UnexpectedEndOfFile(p) => *p,
46        };
47
48        Span::new(position, Position { offset: position.offset + 1, ..position })
49    }
50}
51
52impl std::fmt::Display for SyntaxError {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        let message = match self {
55            Self::UnexpectedToken(token, _) => &format!("Unexpected token `{}` (0x{:02X})", *token as char, token),
56            Self::UnrecognizedToken(token, _) => &format!("Unrecognised token `{}` (0x{:02X})", *token as char, token),
57            Self::UnexpectedEndOfFile(_) => "Unexpected end of file",
58        };
59
60        write!(f, "{message}")
61    }
62}
63
64impl std::fmt::Display for ParseError {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        let message = match self {
67            ParseError::SyntaxError(e) => {
68                return write!(f, "{e}");
69            }
70            ParseError::UnexpectedEndOfFile(expected, _) => {
71                let expected = expected.iter().map(|kind| kind.to_string()).collect::<Vec<_>>().join("`, `");
72
73                if expected.is_empty() {
74                    "Unexpected end of file".to_string()
75                } else if expected.len() == 1 {
76                    format!("Expected `{expected}` before end of file")
77                } else {
78                    format!("Expected one of `{expected}` before end of file")
79                }
80            }
81            ParseError::UnexpectedToken(expected, found, _) => {
82                let expected = expected.iter().map(|kind| kind.to_string()).collect::<Vec<_>>().join("`, `");
83
84                let found = found.to_string();
85
86                if expected.is_empty() {
87                    format!("Unexpected token `{found}`")
88                } else if expected.len() == 1 {
89                    format!("Expected `{expected}`, found `{found}`")
90                } else {
91                    format!("Expected one of `{expected}`, found `{found}`")
92                }
93            }
94            ParseError::UnclosedLiteralString(kind, _) => match kind {
95                LiteralStringKind::SingleQuoted => "Unclosed single-quoted string".to_string(),
96                LiteralStringKind::DoubleQuoted => "Unclosed double-quoted string".to_string(),
97            },
98        };
99
100        write!(f, "{message}")
101    }
102}
103
104impl std::error::Error for SyntaxError {}
105
106impl std::error::Error for ParseError {
107    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
108        match self {
109            ParseError::SyntaxError(e) => Some(e),
110            _ => None,
111        }
112    }
113}
114impl From<SyntaxError> for Issue {
115    fn from(error: SyntaxError) -> Issue {
116        let position = error.position();
117        let span = Span::new(position, Position { offset: position.offset + 1, ..position });
118
119        Issue::error(error.to_string()).with_annotation(Annotation::primary(span).with_message("Syntax error."))
120    }
121}
122
123impl From<SyntaxError> for ParseError {
124    fn from(error: SyntaxError) -> Self {
125        ParseError::SyntaxError(error)
126    }
127}
128
129impl From<&ParseError> for Issue {
130    fn from(error: &ParseError) -> Self {
131        let span = error.span();
132
133        Issue::error(error.to_string()).with_annotation(Annotation::primary(span).with_message("Invalid syntax."))
134    }
135}