#[cfg(test)]
mod tests;
use crate::lexer::Error as LexError;
use boa_ast::{Position, Span};
use std::fmt;
pub type ParseResult<T> = Result<T, Error>;
pub(crate) trait ErrorContext {
fn set_context(self, context: &'static str) -> Self;
#[allow(dead_code)]
fn context(&self) -> Option<&'static str>;
}
impl<T> ErrorContext for ParseResult<T> {
fn set_context(self, context: &'static str) -> Self {
self.map_err(|e| e.set_context(context))
}
fn context(&self) -> Option<&'static str> {
self.as_ref().err().and_then(Error::context)
}
}
impl From<LexError> for Error {
#[inline]
fn from(e: LexError) -> Self {
Self::lex(e)
}
}
#[derive(Debug)]
pub enum Error {
Expected {
expected: Box<[String]>,
found: Box<str>,
context: &'static str,
span: Span,
},
Unexpected {
message: Box<str>,
found: Box<str>,
span: Span,
},
AbruptEnd,
Lex {
err: LexError,
},
General {
message: Box<str>,
position: Position,
},
}
impl Error {
fn set_context(self, new_context: &'static str) -> Self {
match self {
Self::Expected {
expected,
found,
span,
..
} => Self::expected(expected, found, span, new_context),
e => e,
}
}
#[allow(unused)] const fn context(&self) -> Option<&'static str> {
if let Self::Expected { context, .. } = self {
Some(context)
} else {
None
}
}
pub(crate) fn expected<E, F>(expected: E, found: F, span: Span, context: &'static str) -> Self
where
E: Into<Box<[String]>>,
F: Into<Box<str>>,
{
let expected = expected.into();
debug_assert_ne!(expected.len(), 0);
Self::Expected {
expected,
found: found.into(),
span,
context,
}
}
pub(crate) fn unexpected<F, C>(found: F, span: Span, message: C) -> Self
where
F: Into<Box<str>>,
C: Into<Box<str>>,
{
Self::Unexpected {
found: found.into(),
span,
message: message.into(),
}
}
pub(crate) fn general<S, P>(message: S, position: P) -> Self
where
S: Into<Box<str>>,
P: Into<Position>,
{
Self::General {
message: message.into(),
position: position.into(),
}
}
pub(crate) fn misplaced_function_declaration(position: Position, strict: bool) -> Self {
Self::General {
message: format!(
"{}functions can only be declared at the top level or inside a block.",
if strict { "in strict mode code, " } else { "" }
)
.into(),
position,
}
}
pub(crate) fn wrong_labelled_function_declaration(position: Position) -> Self {
Self::General {
message: "labelled functions can only be declared at the top level or inside a block"
.into(),
position,
}
}
pub(crate) const fn lex(e: LexError) -> Self {
Self::Lex { err: e }
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Expected {
expected,
found,
span,
context,
} => {
write!(f, "expected ")?;
match &**expected {
[single] => write!(f, "token '{single}'")?,
expected => {
write!(f, "one of ")?;
for (i, token) in expected.iter().enumerate() {
let prefix = if i == 0 {
""
} else if i == expected.len() - 1 {
" or "
} else {
", "
};
write!(f, "{prefix}'{token}'")?;
}
}
}
write!(
f,
", got '{found}' in {context} at line {}, col {}",
span.start().line_number(),
span.start().column_number()
)
}
Self::Unexpected {
found,
span,
message,
} => write!(
f,
"unexpected token '{found}', {message} at line {}, col {}",
span.start().line_number(),
span.start().column_number()
),
Self::AbruptEnd => f.write_str("abrupt end"),
Self::General { message, position } => write!(
f,
"{message} at line {}, col {}",
position.line_number(),
position.column_number()
),
Self::Lex { err } => err.fmt(f),
}
}
}
impl std::error::Error for Error {}