mod arrow_function;
mod async_arrow_function;
mod conditional;
mod exponentiation;
mod r#yield;
use crate::{
Error,
lexer::{Error as LexError, InputElement, TokenKind},
parser::{
AllowAwait, AllowIn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,
expression::assignment::{
arrow_function::{ArrowFunction, ConciseBody},
async_arrow_function::AsyncArrowFunction,
conditional::ConditionalExpression,
r#yield::YieldExpression,
},
name_in_lexically_declared_names,
},
source::ReadChar,
};
use boa_ast::{
Expression, Keyword, Punctuator, Span, Spanned,
expression::operator::assign::{Assign, AssignOp, AssignTarget},
operations::{ContainsSymbol, bound_names, contains, lexically_declared_names},
};
use boa_interner::Interner;
pub(super) use exponentiation::ExponentiationExpression;
#[derive(Debug, Clone, Copy)]
pub(in crate::parser) struct AssignmentExpression {
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl AssignmentExpression {
pub(in crate::parser) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for AssignmentExpression
where
R: ReadChar,
{
type Output = Expression;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Expression> {
cursor.set_goal(InputElement::RegExp);
match cursor.peek(0, interner).or_abrupt()?.kind() {
TokenKind::Keyword((Keyword::Yield, _)) if self.allow_yield.0 => {
return YieldExpression::new(self.allow_in, self.allow_await)
.parse(cursor, interner);
}
TokenKind::IdentifierName(_)
| TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => {
cursor.set_goal(InputElement::Div);
let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? {
2
} else {
1
};
if let Some(tok) = cursor.peek_no_skip_line_term(skip_n, interner)?
&& tok.kind() == &TokenKind::Punctuator(Punctuator::Arrow)
{
return ArrowFunction::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)
.map(Expression::ArrowFunction);
}
}
TokenKind::Keyword((Keyword::Async, false)) => {
let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? {
2
} else {
1
};
let peek_1 = cursor.peek(1, interner).or_abrupt()?.kind().clone();
if !cursor
.peek_is_line_terminator(skip_n, interner)
.or_abrupt()?
&& (matches!(peek_1, TokenKind::Punctuator(Punctuator::OpenParen))
|| (matches!(
peek_1,
TokenKind::IdentifierName(_)
| TokenKind::Keyword((
Keyword::Yield | Keyword::Await | Keyword::Of,
_
))
) && matches!(
cursor.peek(2, interner).or_abrupt()?.kind(),
TokenKind::Punctuator(Punctuator::Arrow)
)))
{
return Ok(AsyncArrowFunction::new(self.allow_in, self.allow_yield)
.parse(cursor, interner)?
.into());
}
}
_ => {}
}
cursor.set_goal(InputElement::Div);
let peek_token = cursor.peek(0, interner).or_abrupt()?;
let position = peek_token.span().start();
let start_linear_span = peek_token.linear_span();
let mut lhs = ConditionalExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
if let Expression::FormalParameterList(parameters) = lhs {
cursor.peek_expect_no_lineterminator(0, "arrow function", interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::Arrow),
"arrow function",
interner,
)?;
let arrow = cursor.arrow();
cursor.set_arrow(true);
let body = ConciseBody::new(self.allow_in).parse(cursor, interner)?;
cursor.set_arrow(arrow);
if parameters.has_duplicates() {
return Err(Error::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
position,
)));
}
if contains(¶meters, ContainsSymbol::YieldExpression) {
return Err(Error::lex(LexError::Syntax(
"Yield expression not allowed in this context".into(),
position,
)));
}
if contains(¶meters, ContainsSymbol::AwaitExpression) {
return Err(Error::lex(LexError::Syntax(
"Await expression not allowed in this context".into(),
position,
)));
}
if body.strict() && !parameters.is_simple() {
return Err(Error::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list"
.into(),
position,
)));
}
name_in_lexically_declared_names(
&bound_names(¶meters),
&lexically_declared_names(&body),
position,
interner,
)?;
let linear_pos_end = body.linear_pos_end();
let linear_span = start_linear_span.union(linear_pos_end);
let body_span_end = body.span().end();
return Ok(boa_ast::function::ArrowFunction::new(
None,
parameters,
body,
linear_span,
Span::new(position, body_span_end),
)
.into());
}
if let Some(tok) = cursor.peek(0, interner)?.cloned() {
match tok.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
cursor.advance(interner);
cursor.set_goal(InputElement::RegExp);
let lhs_name = if let Expression::Identifier(ident) = lhs {
Some(ident)
} else {
None
};
if let Some(target) = AssignTarget::from_expression(&lhs, cursor.strict()) {
let mut expr = self.parse(cursor, interner)?;
if let Some(ident) = lhs_name {
expr.set_anonymous_function_definition_name(&ident);
}
lhs = Assign::new(AssignOp::Assign, target, expr).into();
} else {
return Err(Error::lex(LexError::Syntax(
"Invalid left-hand side in assignment".into(),
tok.span().start(),
)));
}
}
TokenKind::Punctuator(p) if p.as_assign_op().is_some() => {
cursor.advance(interner);
if let Some(target) =
AssignTarget::from_expression_simple(&lhs, cursor.strict())
{
let assignop = p.as_assign_op().expect("assignop disappeared");
let mut rhs = self.parse(cursor, interner)?;
if (assignop == AssignOp::BoolAnd
|| assignop == AssignOp::BoolOr
|| assignop == AssignOp::Coalesce)
&& let AssignTarget::Identifier(ident) = target
{
rhs.set_anonymous_function_definition_name(&ident);
}
lhs = Assign::new(assignop, target, rhs).into();
} else {
return Err(Error::lex(LexError::Syntax(
"Invalid left-hand side in assignment".into(),
tok.span().start(),
)));
}
}
_ => {}
}
}
Ok(lhs)
}
}