Skip to main content

obeli_sk_boa_parser/error/
mod.rs

1//! Error and result implementation for the parser.
2
3#[cfg(test)]
4mod tests;
5
6use crate::lexer::Error as LexError;
7use boa_ast::{Position, Span};
8use std::fmt;
9
10/// Result of a parsing operation.
11pub type ParseResult<T> = Result<T, Error>;
12
13/// Adds context to a parser error.
14pub(crate) trait ErrorContext {
15    /// Sets the context of the error, if possible.
16    fn set_context(self, context: &'static str) -> Self;
17
18    /// Gets the context of the error, if any.
19    fn context(&self) -> Option<&'static str>;
20}
21
22impl<T> ErrorContext for ParseResult<T> {
23    fn set_context(self, context: &'static str) -> Self {
24        self.map_err(|e| e.set_context(context))
25    }
26
27    fn context(&self) -> Option<&'static str> {
28        self.as_ref().err().and_then(ErrorContext::context)
29    }
30}
31
32impl ErrorContext for Error {
33    fn set_context(self, new_context: &'static str) -> Self {
34        match self {
35            Self::Expected {
36                expected,
37                found,
38                span,
39                ..
40            } => Self::expected(expected, found, span, new_context),
41            e => e,
42        }
43    }
44
45    fn context(&self) -> Option<&'static str> {
46        if let Self::Expected { context, .. } = self {
47            Some(context)
48        } else {
49            None
50        }
51    }
52}
53
54impl From<LexError> for Error {
55    #[inline]
56    fn from(e: LexError) -> Self {
57        Self::lex(e)
58    }
59}
60
61/// An enum which represents errors encountered during parsing an expression
62#[derive(Debug)]
63pub enum Error {
64    /// When it expected a certain kind of token, but got another as part of something
65    Expected {
66        /// The token(s) that were expected.
67        expected: Box<[String]>,
68
69        /// The token that was not expected.
70        found: Box<str>,
71
72        /// The parsing context in which the error occurred.
73        context: &'static str,
74
75        /// Position of the source code where the error occurred.
76        span: Span,
77    },
78
79    /// When a token is unexpected
80    Unexpected {
81        /// The error message.
82        message: Box<str>,
83
84        /// The token that was not expected.
85        found: Box<str>,
86
87        /// Position of the source code where the error occurred.
88        span: Span,
89    },
90
91    /// When there is an abrupt end to the parsing
92    AbruptEnd,
93
94    /// A lexing error.
95    Lex {
96        /// The error that occurred during lexing.
97        err: LexError,
98    },
99
100    /// A scope analysis error.
101    ScopeAnalysis {
102        /// The error that occurred during scope analysis.
103        err: &'static str,
104    },
105
106    /// Catch all General Error
107    General {
108        /// The error message.
109        message: Box<str>,
110
111        /// Position of the source code where the error occurred.
112        position: Position,
113    },
114}
115
116impl Error {
117    /// Creates an `Expected` parsing error.
118    pub(crate) fn expected<E, F>(expected: E, found: F, span: Span, context: &'static str) -> Self
119    where
120        E: Into<Box<[String]>>,
121        F: Into<Box<str>>,
122    {
123        let expected = expected.into();
124        debug_assert_ne!(expected.len(), 0);
125
126        Self::Expected {
127            expected,
128            found: found.into(),
129            span,
130            context,
131        }
132    }
133
134    /// Creates an `Unexpected` parsing error.
135    pub(crate) fn unexpected<F, C>(found: F, span: Span, message: C) -> Self
136    where
137        F: Into<Box<str>>,
138        C: Into<Box<str>>,
139    {
140        Self::Unexpected {
141            found: found.into(),
142            span,
143            message: message.into(),
144        }
145    }
146
147    /// Creates a `ScopeAnalysis` parsing error.
148    pub(crate) fn scope_analysis(err: &'static str) -> Self {
149        Self::ScopeAnalysis { err }
150    }
151
152    /// Creates a "general" parsing error.
153    pub(crate) fn general<S, P>(message: S, position: P) -> Self
154    where
155        S: Into<Box<str>>,
156        P: Into<Position>,
157    {
158        Self::General {
159            message: message.into(),
160            position: position.into(),
161        }
162    }
163
164    /// Creates a "general" parsing error with the specific error message for a misplaced function declaration.
165    pub(crate) fn misplaced_function_declaration(position: Position, strict: bool) -> Self {
166        Self::General {
167            message: format!(
168                "{}functions can only be declared at the top level or inside a block.",
169                if strict { "in strict mode code, " } else { "" }
170            )
171            .into(),
172            position,
173        }
174    }
175
176    /// Creates a "general" parsing error with the specific error message for a wrong function declaration with label.
177    pub(crate) fn wrong_labelled_function_declaration(position: Position) -> Self {
178        Self::General {
179            message: "labelled functions can only be declared at the top level or inside a block"
180                .into(),
181            position,
182        }
183    }
184
185    /// Creates a parsing error from a lexing error.
186    pub(crate) const fn lex(e: LexError) -> Self {
187        Self::Lex { err: e }
188    }
189}
190
191impl fmt::Display for Error {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        match self {
194            Self::Expected {
195                expected,
196                found,
197                span,
198                ..
199            } => {
200                write!(f, "expected ")?;
201                match &**expected {
202                    [single] => write!(f, "token '{single}'")?,
203                    expected => {
204                        write!(f, "one of ")?;
205                        for (i, token) in expected.iter().enumerate() {
206                            let prefix = if i == 0 {
207                                ""
208                            } else if i == expected.len() - 1 {
209                                " or "
210                            } else {
211                                ", "
212                            };
213                            write!(f, "{prefix}'{token}'")?;
214                        }
215                    }
216                }
217                if let Some(context) = self.context() {
218                    write!(
219                        f,
220                        ", got '{found}' in {context} at line {}, col {}",
221                        span.start().line_number(),
222                        span.start().column_number()
223                    )
224                } else {
225                    write!(
226                        f,
227                        ", got '{found}' at line {}, col {}",
228                        span.start().line_number(),
229                        span.start().column_number()
230                    )
231                }
232            }
233            Self::Unexpected {
234                found,
235                span,
236                message,
237            } => write!(
238                f,
239                "unexpected token '{found}', {message} at line {}, col {}",
240                span.start().line_number(),
241                span.start().column_number()
242            ),
243            Self::AbruptEnd => f.write_str("abrupt end"),
244            Self::General { message, position } => write!(
245                f,
246                "{message} at line {}, col {}",
247                position.line_number(),
248                position.column_number()
249            ),
250            Self::Lex { err } => err.fmt(f),
251            Self::ScopeAnalysis { err } => write!(f, "invalid scope analysis: {err}"),
252        }
253    }
254}
255
256impl std::error::Error for Error {}