obeli_sk_boa_parser/error/
mod.rs1#[cfg(test)]
4mod tests;
5
6use crate::lexer::Error as LexError;
7use boa_ast::{Position, Span};
8use std::fmt;
9
10pub type ParseResult<T> = Result<T, Error>;
12
13pub(crate) trait ErrorContext {
15 fn set_context(self, context: &'static str) -> Self;
17
18 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#[derive(Debug)]
63pub enum Error {
64 Expected {
66 expected: Box<[String]>,
68
69 found: Box<str>,
71
72 context: &'static str,
74
75 span: Span,
77 },
78
79 Unexpected {
81 message: Box<str>,
83
84 found: Box<str>,
86
87 span: Span,
89 },
90
91 AbruptEnd,
93
94 Lex {
96 err: LexError,
98 },
99
100 ScopeAnalysis {
102 err: &'static str,
104 },
105
106 General {
108 message: Box<str>,
110
111 position: Position,
113 },
114}
115
116impl Error {
117 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 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 pub(crate) fn scope_analysis(err: &'static str) -> Self {
149 Self::ScopeAnalysis { err }
150 }
151
152 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 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 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 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 {}