mod arrow_function;
mod conditional;
mod exponentiation;
use self::{arrow_function::ArrowFunction, conditional::ConditionalExpression};
use crate::syntax::lexer::{Error as LexError, InputElement, TokenKind};
use crate::{
syntax::{
ast::{
node::{Assign, BinOp, Node},
Keyword, Punctuator,
},
parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
},
BoaProfiler,
};
pub(super) use exponentiation::ExponentiationExpression;
use std::io::Read;
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct AssignmentExpression {
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl AssignmentExpression {
pub(in crate::syntax::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: Read,
{
type Output = Node;
fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("AssignmentExpression", "Parsing");
cursor.set_goal(InputElement::Div);
match cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.kind() {
TokenKind::Identifier(_)
| TokenKind::Keyword(Keyword::Yield)
| TokenKind::Keyword(Keyword::Await) => {
if let Ok(tok) = cursor.peek_expect_no_lineterminator(1) {
if tok.kind() == &TokenKind::Punctuator(Punctuator::Arrow) {
return ArrowFunction::new(
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor)
.map(Node::ArrowFunctionDecl);
}
}
}
TokenKind::Punctuator(Punctuator::OpenParen) => {
if let Some(next_token) = cursor.peek(1)? {
match *next_token.kind() {
TokenKind::Punctuator(Punctuator::CloseParen) => {
if let Some(t) = cursor.peek(2)? {
if t.kind() == &TokenKind::Punctuator(Punctuator::Arrow) {
return ArrowFunction::new(
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor)
.map(Node::ArrowFunctionDecl);
}
}
}
TokenKind::Punctuator(Punctuator::Spread) => {
return ArrowFunction::new(
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor)
.map(Node::ArrowFunctionDecl);
}
TokenKind::Identifier(_) => {
if let Some(t) = cursor.peek(2)? {
match *t.kind() {
TokenKind::Punctuator(Punctuator::Comma) => {
return ArrowFunction::new(
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor)
.map(Node::ArrowFunctionDecl);
}
TokenKind::Punctuator(Punctuator::CloseParen) => {
if let Some(t) = cursor.peek(2)? {
if t.kind() == &TokenKind::Punctuator(Punctuator::Arrow)
{
return ArrowFunction::new(
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor)
.map(Node::ArrowFunctionDecl);
}
}
}
_ => {}
}
}
}
_ => {}
}
}
}
_ => {}
}
cursor.set_goal(InputElement::Div);
let mut lhs = ConditionalExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor)?;
if let Some(tok) = cursor.peek(0)?.cloned() {
match tok.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
cursor.next()?.expect("= token vanished"); if is_assignable(&lhs) {
lhs = Assign::new(lhs, self.parse(cursor)?).into();
} else {
return Err(ParseError::lex(LexError::Syntax(
"Invalid left-hand side in assignment".into(),
tok.span().start(),
)));
}
}
TokenKind::Punctuator(p) if p.as_binop().is_some() && p != &Punctuator::Comma => {
cursor.next()?.expect("token vanished"); if is_assignable(&lhs) {
let binop = p.as_binop().expect("binop disappeared");
let expr = self.parse(cursor)?;
lhs = BinOp::new(binop, lhs, expr).into();
} else {
return Err(ParseError::lex(LexError::Syntax(
"Invalid left-hand side in assignment".into(),
tok.span().start(),
)));
}
}
_ => {}
}
}
Ok(lhs)
}
}
#[inline]
pub(crate) fn is_assignable(node: &Node) -> bool {
!matches!(node, Node::Const(_) | Node::ArrayDecl(_))
}