boa/syntax/parser/
error.rs

1//! Error and result implementation for the parser.
2
3use crate::syntax::ast::{position::Position, Node};
4use crate::syntax::lexer::{Error as LexError, Token, TokenKind};
5use std::fmt;
6
7/// Result of a parsing operation.
8pub type ParseResult = Result<Node, ParseError>;
9
10pub(crate) trait ErrorContext {
11    fn context(self, context: &'static str) -> Self;
12}
13
14impl<T> ErrorContext for Result<T, ParseError> {
15    fn context(self, context: &'static str) -> Self {
16        self.map_err(|e| e.context(context))
17    }
18}
19
20impl From<LexError> for ParseError {
21    fn from(e: LexError) -> ParseError {
22        ParseError::lex(e)
23    }
24}
25
26/// `ParseError` is an enum which represents errors encounted during parsing an expression
27#[derive(Debug)]
28pub enum ParseError {
29    /// When it expected a certain kind of token, but got another as part of something
30    Expected {
31        expected: Box<[TokenKind]>,
32        found: Token,
33        context: &'static str,
34    },
35    /// When a token is unexpected
36    Unexpected {
37        found: Token,
38        message: Option<&'static str>,
39    },
40    /// When there is an abrupt end to the parsing
41    AbruptEnd,
42    /// A lexing error.
43    Lex { err: LexError },
44    /// Catch all General Error
45    General {
46        message: &'static str,
47        position: Position,
48    },
49    /// Unimplemented syntax error
50    Unimplemented {
51        message: &'static str,
52        position: Position,
53    },
54}
55
56impl ParseError {
57    /// Changes the context of the error, if any.
58    fn context(self, new_context: &'static str) -> Self {
59        match self {
60            Self::Expected {
61                expected, found, ..
62            } => Self::expected(expected, found, new_context),
63            e => e,
64        }
65    }
66
67    /// Creates an `Expected` parsing error.
68    pub(super) fn expected<E>(expected: E, found: Token, context: &'static str) -> Self
69    where
70        E: Into<Box<[TokenKind]>>,
71    {
72        Self::Expected {
73            expected: expected.into(),
74            found,
75            context,
76        }
77    }
78
79    /// Creates an `Expected` parsing error.
80    pub(super) fn unexpected<C>(found: Token, message: C) -> Self
81    where
82        C: Into<Option<&'static str>>,
83    {
84        Self::Unexpected {
85            found,
86            message: message.into(),
87        }
88    }
89
90    /// Creates a "general" parsing error.
91    pub(super) fn general(message: &'static str, position: Position) -> Self {
92        Self::General { message, position }
93    }
94
95    /// Creates a parsing error from a lexing error.
96    pub(super) fn lex(e: LexError) -> Self {
97        Self::Lex { err: e }
98    }
99
100    /// Creates a new `Unimplemented` parsing error.
101    #[allow(dead_code)]
102    pub(super) fn unimplemented(message: &'static str, position: Position) -> Self {
103        Self::Unimplemented { message, position }
104    }
105}
106
107impl fmt::Display for ParseError {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        match self {
110            Self::Expected {
111                expected,
112                found,
113                context,
114            } => write!(
115                f,
116                "expected {}, got '{}' in {} at line {}, col {}",
117                if expected.len() == 1 {
118                    format!(
119                        "token '{}'",
120                        expected.first().map(TokenKind::to_string).unwrap()
121                    )
122                } else {
123                    format!(
124                        "one of {}",
125                        expected
126                            .iter()
127                            .enumerate()
128                            .map(|(i, t)| {
129                                format!(
130                                    "{}'{}'",
131                                    if i == 0 {
132                                        ""
133                                    } else if i == expected.len() - 1 {
134                                        " or "
135                                    } else {
136                                        ", "
137                                    },
138                                    t
139                                )
140                            })
141                            .collect::<String>()
142                    )
143                },
144                found,
145                context,
146                found.span().start().line_number(),
147                found.span().start().column_number()
148            ),
149            Self::Unexpected { found, message } => write!(
150                f,
151                "unexpected token '{}'{} at line {}, col {}",
152                found,
153                if let Some(m) = message {
154                    format!(", {}", m)
155                } else {
156                    String::new()
157                },
158                found.span().start().line_number(),
159                found.span().start().column_number()
160            ),
161            Self::AbruptEnd => f.write_str("abrupt end"),
162            Self::General { message, position } => write!(
163                f,
164                "{} at line {}, col {}",
165                message,
166                position.line_number(),
167                position.column_number()
168            ),
169            Self::Lex { err } => fmt::Display::fmt(err, f),
170            Self::Unimplemented { message, position } => write!(
171                f,
172                "{} not yet implemented at line {}, col {}",
173                message,
174                position.line_number(),
175                position.column_number()
176            ),
177        }
178    }
179}