mod cursor;
mod expression;
mod statement;
pub(crate) mod function;
#[cfg(test)]
mod tests;
use crate::{
Error, Source,
error::ParseResult,
lexer::{Error as LexError, InputElement},
parser::{
cursor::Cursor,
function::{FormalParameters, FunctionStatementList},
},
source::ReadChar,
};
use boa_ast::{
Position, StatementList,
function::{FormalParameterList, FunctionBody},
operations::{
ContainsSymbol, all_private_identifiers_valid, check_labels, contains,
contains_invalid_object_literal, lexically_declared_names, var_declared_names,
},
scope::Scope,
};
use boa_interner::{Interner, Sym};
use rustc_hash::FxHashSet;
use std::path::Path;
use self::statement::ModuleItemList;
type ScriptParseOutput = (boa_ast::Script, boa_ast::SourceText);
type ModuleParseOutput = (boa_ast::Module, boa_ast::SourceText);
trait TokenParser<R>: Sized
where
R: ReadChar,
{
type Output;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct AllowYield(bool);
impl From<bool> for AllowYield {
fn from(allow: bool) -> Self {
Self(allow)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct AllowAwait(bool);
impl From<bool> for AllowAwait {
fn from(allow: bool) -> Self {
Self(allow)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct AllowIn(bool);
impl From<bool> for AllowIn {
fn from(allow: bool) -> Self {
Self(allow)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct AllowReturn(bool);
impl From<bool> for AllowReturn {
fn from(allow: bool) -> Self {
Self(allow)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct AllowDefault(bool);
impl From<bool> for AllowDefault {
fn from(allow: bool) -> Self {
Self(allow)
}
}
#[derive(Debug)]
pub struct Parser<'a, R> {
#[allow(unused)] path: Option<&'a Path>,
cursor: Cursor<R>,
}
impl<'a, R: ReadChar> Parser<'a, R> {
pub fn new(source: Source<'a, R>) -> Self {
Self {
path: source.path,
cursor: Cursor::new(source.reader),
}
}
pub fn parse_script(
&mut self,
scope: &Scope,
interner: &mut Interner,
) -> ParseResult<boa_ast::Script> {
self.parse_script_with_source(scope, interner).map(|x| x.0)
}
pub fn parse_script_with_source(
&mut self,
scope: &Scope,
interner: &mut Interner,
) -> ParseResult<ScriptParseOutput> {
self.cursor.set_goal(InputElement::HashbangOrRegExp);
let (mut ast, source) = ScriptParser::new(false).parse(&mut self.cursor, interner)?;
if let Err(reason) = ast.analyze_scope(scope, interner) {
return Err(Error::general(
format!("invalid scope analysis: {reason}"),
Position::new(1, 1),
));
}
Ok((ast, source))
}
pub fn parse_module(
&mut self,
scope: &Scope,
interner: &mut Interner,
) -> ParseResult<boa_ast::Module>
where
R: ReadChar,
{
self.parse_module_with_source(scope, interner).map(|x| x.0)
}
pub fn parse_module_with_source(
&mut self,
scope: &Scope,
interner: &mut Interner,
) -> ParseResult<ModuleParseOutput>
where
R: ReadChar,
{
self.cursor.set_goal(InputElement::HashbangOrRegExp);
let (mut module, source) = ModuleParser.parse(&mut self.cursor, interner)?;
if let Err(reason) = module.analyze_scope(scope, interner) {
return Err(Error::general(
format!("invalid scope analysis: {reason}"),
Position::new(1, 1),
));
}
Ok((module, source))
}
pub fn parse_eval(
&mut self,
direct: bool,
interner: &mut Interner,
) -> ParseResult<ScriptParseOutput> {
self.cursor.set_goal(InputElement::HashbangOrRegExp);
ScriptParser::new(direct).parse(&mut self.cursor, interner)
}
pub fn parse_function_body(
&mut self,
interner: &mut Interner,
allow_yield: bool,
allow_await: bool,
) -> ParseResult<FunctionBody> {
let mut parser = FunctionStatementList::new(allow_yield, allow_await, "function body");
parser.parse_full_input(true);
parser.parse(&mut self.cursor, interner)
}
pub fn parse_formal_parameters(
&mut self,
interner: &mut Interner,
allow_yield: bool,
allow_await: bool,
) -> ParseResult<FormalParameterList> {
FormalParameters::new(allow_yield, allow_await).parse(&mut self.cursor, interner)
}
}
impl<R> Parser<'_, R> {
pub fn set_strict(&mut self)
where
R: ReadChar,
{
self.cursor.set_strict(true);
}
pub fn set_json_parse(&mut self)
where
R: ReadChar,
{
self.cursor.set_json_parse(true);
}
pub fn set_identifier(&mut self, identifier: u32)
where
R: ReadChar,
{
self.cursor.set_identifier(identifier);
}
}
#[derive(Debug, Clone, Copy)]
pub struct ScriptParser {
direct_eval: bool,
}
impl ScriptParser {
#[inline]
const fn new(direct_eval: bool) -> Self {
Self { direct_eval }
}
}
impl<R> TokenParser<R> for ScriptParser
where
R: ReadChar,
{
type Output = ScriptParseOutput;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let stmts =
ScriptBody::new(true, cursor.strict(), self.direct_eval).parse(cursor, interner)?;
let script = boa_ast::Script::new(stmts);
let mut lexical_names = FxHashSet::default();
for name in lexically_declared_names(&script) {
if !lexical_names.insert(name) {
return Err(Error::general(
"lexical name declared multiple times",
Position::new(1, 1),
));
}
}
for name in var_declared_names(&script) {
if lexical_names.contains(&name) {
return Err(Error::general(
"lexical name declared multiple times",
Position::new(1, 1),
));
}
}
let source = cursor.take_source();
Ok((script, source))
}
}
#[derive(Debug, Clone, Copy)]
pub struct ScriptBody {
directive_prologues: bool,
strict: bool,
direct_eval: bool,
}
impl ScriptBody {
#[inline]
const fn new(directive_prologues: bool, strict: bool, direct_eval: bool) -> Self {
Self {
directive_prologues,
strict,
direct_eval,
}
}
}
impl<R> TokenParser<R> for ScriptBody
where
R: ReadChar,
{
type Output = StatementList;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let (body, _end) = statement::StatementList::new(
false,
false,
false,
&[],
self.directive_prologues,
self.strict,
)
.parse(cursor, interner)?;
if !self.direct_eval {
if contains(&body, ContainsSymbol::Super) {
return Err(Error::general("invalid super usage", Position::new(1, 1)));
}
if contains(&body, ContainsSymbol::NewTarget) {
return Err(Error::general(
"invalid new.target usage",
Position::new(1, 1),
));
}
if !all_private_identifiers_valid(&body, Vec::new()) {
return Err(Error::general(
"invalid private identifier usage",
Position::new(1, 1),
));
}
}
if let Err(error) = check_labels(&body) {
return Err(Error::lex(LexError::Syntax(
error.message(interner).into(),
Position::new(1, 1),
)));
}
if contains_invalid_object_literal(&body) {
return Err(Error::lex(LexError::Syntax(
"invalid object literal in script statement list".into(),
Position::new(1, 1),
)));
}
Ok(body)
}
}
#[derive(Debug, Clone, Copy)]
struct ModuleParser;
impl<R> TokenParser<R> for ModuleParser
where
R: ReadChar,
{
type Output = ModuleParseOutput;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
cursor.set_module();
let module = boa_ast::Module::new(ModuleItemList.parse(cursor, interner)?);
let mut bindings = FxHashSet::default();
for name in lexically_declared_names(&module) {
if !bindings.insert(name) {
return Err(Error::general(
format!(
"lexical name `{}` declared multiple times",
interner.resolve_expect(name)
),
Position::new(1, 1),
));
}
}
for name in var_declared_names(&module) {
if !bindings.insert(name) {
return Err(Error::general(
format!(
"lexical name `{}` declared multiple times",
interner.resolve_expect(name)
),
Position::new(1, 1),
));
}
}
{
let mut exported_names = FxHashSet::default();
for name in module.items().exported_names() {
if !exported_names.insert(name) {
return Err(Error::general(
format!(
"exported name `{}` declared multiple times",
interner.resolve_expect(name)
),
Position::new(1, 1),
));
}
}
}
for name in module.items().exported_bindings() {
if !bindings.contains(&name) {
return Err(Error::general(
format!(
"could not find the exported binding `{}` in the declared names of the module",
interner.resolve_expect(name)
),
Position::new(1, 1),
));
}
}
if contains(&module, ContainsSymbol::Super) {
return Err(Error::general(
"module cannot contain `super` on the top-level",
Position::new(1, 1),
));
}
if contains(&module, ContainsSymbol::NewTarget) {
return Err(Error::general(
"module cannot contain `new.target` on the top-level",
Position::new(1, 1),
));
}
check_labels(&module).map_err(|error| {
Error::lex(LexError::Syntax(
error.message(interner).into(),
Position::new(1, 1),
))
})?;
if !all_private_identifiers_valid(&module, Vec::new()) {
return Err(Error::general(
"invalid private identifier usage",
Position::new(1, 1),
));
}
let source = cursor.take_source();
Ok((module, source))
}
}
fn name_in_lexically_declared_names(
bound_names: &[Sym],
lexical_names: &[Sym],
position: Position,
interner: &Interner,
) -> ParseResult<()> {
for name in bound_names {
if lexical_names.contains(name) {
return Err(Error::general(
format!(
"formal parameter `{}` declared in lexically declared names",
interner.resolve_expect(*name)
),
position,
));
}
}
Ok(())
}
trait OrAbrupt<T> {
fn or_abrupt(self) -> ParseResult<T>;
}
impl<T> OrAbrupt<T> for ParseResult<Option<T>> {
fn or_abrupt(self) -> ParseResult<T> {
self?.ok_or(Error::AbruptEnd)
}
}