1#[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 #[allow(dead_code)]
20 fn context(&self) -> Option<&'static str>;
21}
22
23impl<T> ErrorContext for ParseResult<T> {
24 fn set_context(self, context: &'static str) -> Self {
25 self.map_err(|e| e.set_context(context))
26 }
27
28 fn context(&self) -> Option<&'static str> {
29 self.as_ref().err().and_then(Error::context)
30 }
31}
32
33impl From<LexError> for Error {
34 #[inline]
35 fn from(e: LexError) -> Self {
36 Self::lex(e)
37 }
38}
39
40#[derive(Debug)]
42pub enum Error {
43 Expected {
45 expected: Box<[String]>,
47
48 found: Box<str>,
50
51 context: &'static str,
53
54 span: Span,
56 },
57
58 Unexpected {
60 message: Box<str>,
62
63 found: Box<str>,
65
66 span: Span,
68 },
69
70 AbruptEnd,
72
73 Lex {
75 err: LexError,
77 },
78
79 General {
81 message: Box<str>,
83
84 position: Position,
86 },
87}
88
89impl Error {
90 fn set_context(self, new_context: &'static str) -> Self {
92 match self {
93 Self::Expected {
94 expected,
95 found,
96 span,
97 ..
98 } => Self::expected(expected, found, span, new_context),
99 e => e,
100 }
101 }
102
103 #[allow(unused)] const fn context(&self) -> Option<&'static str> {
106 if let Self::Expected { context, .. } = self {
107 Some(context)
108 } else {
109 None
110 }
111 }
112
113 pub(crate) fn expected<E, F>(expected: E, found: F, span: Span, context: &'static str) -> Self
115 where
116 E: Into<Box<[String]>>,
117 F: Into<Box<str>>,
118 {
119 let expected = expected.into();
120 debug_assert_ne!(expected.len(), 0);
121
122 Self::Expected {
123 expected,
124 found: found.into(),
125 span,
126 context,
127 }
128 }
129
130 pub(crate) fn unexpected<F, C>(found: F, span: Span, message: C) -> Self
132 where
133 F: Into<Box<str>>,
134 C: Into<Box<str>>,
135 {
136 Self::Unexpected {
137 found: found.into(),
138 span,
139 message: message.into(),
140 }
141 }
142
143 pub(crate) fn general<S, P>(message: S, position: P) -> Self
145 where
146 S: Into<Box<str>>,
147 P: Into<Position>,
148 {
149 Self::General {
150 message: message.into(),
151 position: position.into(),
152 }
153 }
154
155 pub(crate) fn misplaced_function_declaration(position: Position, strict: bool) -> Self {
157 Self::General {
158 message: format!(
159 "{}functions can only be declared at the top level or inside a block.",
160 if strict { "in strict mode code, " } else { "" }
161 )
162 .into(),
163 position,
164 }
165 }
166
167 pub(crate) fn wrong_labelled_function_declaration(position: Position) -> Self {
169 Self::General {
170 message: "labelled functions can only be declared at the top level or inside a block"
171 .into(),
172 position,
173 }
174 }
175
176 pub(crate) const fn lex(e: LexError) -> Self {
178 Self::Lex { err: e }
179 }
180}
181
182impl fmt::Display for Error {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 match self {
185 Self::Expected {
186 expected,
187 found,
188 span,
189 context,
190 } => {
191 write!(f, "expected ")?;
192 match &**expected {
193 [single] => write!(f, "token '{single}'")?,
194 expected => {
195 write!(f, "one of ")?;
196 for (i, token) in expected.iter().enumerate() {
197 let prefix = if i == 0 {
198 ""
199 } else if i == expected.len() - 1 {
200 " or "
201 } else {
202 ", "
203 };
204 write!(f, "{prefix}'{token}'")?;
205 }
206 }
207 }
208 write!(
209 f,
210 ", got '{found}' in {context} at line {}, col {}",
211 span.start().line_number(),
212 span.start().column_number()
213 )
214 }
215 Self::Unexpected {
216 found,
217 span,
218 message,
219 } => write!(
220 f,
221 "unexpected token '{found}', {message} at line {}, col {}",
222 span.start().line_number(),
223 span.start().column_number()
224 ),
225 Self::AbruptEnd => f.write_str("abrupt end"),
226 Self::General { message, position } => write!(
227 f,
228 "{message} at line {}, col {}",
229 position.line_number(),
230 position.column_number()
231 ),
232 Self::Lex { err } => err.fmt(f),
233 }
234 }
235}
236
237impl std::error::Error for Error {}