use crate::engine::api::error::Error;
use crate::engine::api::error::ErrorKind;
use crate::engine::api::error::NativeErrorMessage;
use crate::engine::compiler::lexer::Identifier;
use crate::engine::compiler::lexer::Keyword;
use crate::engine::compiler::lexer::LexError;
use crate::engine::compiler::lexer::LexErrorKind;
use crate::engine::compiler::lexer::Span;
use crate::engine::compiler::parser::context::Parser;
use crate::engine::value::JsString;
use crate::engine::value::JsStringError;
use crate::source::SourceOffset;
#[derive(Clone, Copy)]
pub(in crate::engine::compiler) enum IdentifierContext {
Reference,
Variable,
FunctionName,
Argument,
}
impl<'source> Parser<'source> {
pub(in crate::engine::compiler) fn validate_identifier(
&self,
identifier: &Identifier<'source>,
span: Span,
strict: bool,
context: IdentifierContext,
) -> Result<(), Error> {
self.validate_identifier_reservation(identifier, span, strict, context)?;
if strict
&& !matches!(context, IdentifierContext::Reference)
&& matches!(
self.identifier_text(identifier).as_ref(),
"eval" | "arguments"
)
{
let message = match context {
IdentifierContext::Variable => "invalid variable name in strict mode",
IdentifierContext::FunctionName => "invalid function name in strict code",
IdentifierContext::Argument => "invalid argument name in strict code",
IdentifierContext::Reference => unreachable!("reference context was excluded"),
};
return Err(Error::syntax(message, span));
}
Ok(())
}
pub(in crate::engine::compiler) fn validate_identifier_reservation(
&self,
identifier: &Identifier<'source>,
span: Span,
strict: bool,
context: IdentifierContext,
) -> Result<(), Error> {
if identifier.escaped_reserved_word {
let text = self.identifier_text(identifier);
return Err(syntax_atom_error(
"'",
&text,
"' is a reserved identifier",
span,
)?);
}
if strict
&& identifier
.keyword_hint
.is_some_and(strict_reserved_identifier)
{
let message = match context {
IdentifierContext::Reference => {
let text = self.identifier_text(identifier);
return Err(syntax_atom_error(
"'",
&text,
"' is a reserved identifier",
span,
)?);
}
IdentifierContext::Variable => "invalid variable name in strict mode",
IdentifierContext::FunctionName => "invalid function name in strict code",
IdentifierContext::Argument => "invalid argument name in strict code",
};
return Err(Error::syntax(message, span));
}
Ok(())
}
}
pub(in crate::engine::compiler) fn syntax_atom_error(
prefix: &str,
atom: &str,
suffix: &str,
span: Span,
) -> Result<Error, JsStringError> {
Ok(syntax_atom_error_without_span(prefix, atom, suffix)?.with_span(span))
}
pub(in crate::engine::compiler) fn syntax_atom_error_without_span(
prefix: &str,
atom: &str,
suffix: &str,
) -> Result<Error, JsStringError> {
let atom = JsString::try_from_utf8(atom)?;
let mut message = NativeErrorMessage::new();
message.push_utf8(prefix);
atom.push_atom_get_str_to(&mut message);
message.push_utf8(suffix);
Ok(Error::from_native_message(ErrorKind::Syntax, message))
}
pub(in crate::engine::compiler) const fn strict_reserved_identifier(keyword: Keyword) -> bool {
matches!(
keyword,
Keyword::Implements
| Keyword::Interface
| Keyword::Let
| Keyword::Package
| Keyword::Private
| Keyword::Protected
| Keyword::Public
| Keyword::Static
| Keyword::Yield
)
}
#[cold]
#[inline(never)]
pub(in crate::engine::compiler) fn lex_error(error: LexError) -> Error {
if error.kind == LexErrorKind::StringTooLong {
Error::new(ErrorKind::JsInternal, error.message)
} else {
Error::syntax(error.message, error.span)
}
}
pub(in crate::engine::compiler) fn source_offset(span: Span) -> Result<SourceOffset, Error> {
SourceOffset::try_from_usize(span.start.byte_offset)
.map_err(|error| Error::internal(error.to_string()))
}