diff --git a/boa/src/syntax/parser/expression/assignment/conditional.rs b/boa/src/syntax/parser/expression/assignment/conditional.rs new file mode 100644 index 00000000000..874aa950c9d --- /dev/null +++ b/boa/src/syntax/parser/expression/assignment/conditional.rs @@ -0,0 +1,79 @@ +//! Conditional operator parsing. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript specification][spec] +//! +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator +//! [spec]: https://tc39.es/ecma262/#sec-conditional-operator + +use crate::syntax::{ + ast::{node::Node, punc::Punctuator, token::TokenKind}, + parser::{ + expression::{AssignmentExpression, LogicalORExpression}, + AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser, + }, +}; + +/// Conditional expression parsing. +/// +/// More information: +/// - [MDN documentation][mdn] +/// - [ECMAScript specification][spec] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator +/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression +#[derive(Debug, Clone, Copy)] +pub(in crate::syntax::parser::expression) struct ConditionalExpression { + allow_in: AllowIn, + allow_yield: AllowYield, + allow_await: AllowAwait, +} + +impl ConditionalExpression { + /// Creates a new `ConditionalExpression` parser. + pub(in crate::syntax::parser::expression) fn new( + allow_in: I, + allow_yield: Y, + allow_await: A, + ) -> Self + where + I: Into, + Y: Into, + A: Into, + { + Self { + allow_in: allow_in.into(), + allow_yield: allow_yield.into(), + allow_await: allow_await.into(), + } + } +} + +impl TokenParser for ConditionalExpression { + type Output = Node; + + fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + // TODO: coalesce expression + let lhs = LogicalORExpression::new(self.allow_in, self.allow_yield, self.allow_await) + .parse(cursor)?; + + if let Some(tok) = cursor.next() { + if tok.kind == TokenKind::Punctuator(Punctuator::Question) { + let then_clause = + AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await) + .parse(cursor)?; + cursor.expect(Punctuator::Colon, "conditional expression")?; + + let else_clause = + AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await) + .parse(cursor)?; + return Ok(Node::conditional_op(lhs, then_clause, else_clause)); + } else { + cursor.back(); + } + } + + Ok(lhs) + } +} diff --git a/boa/src/syntax/parser/expression/assignment/exponentiation.rs b/boa/src/syntax/parser/expression/assignment/exponentiation.rs new file mode 100644 index 00000000000..5da2b310326 --- /dev/null +++ b/boa/src/syntax/parser/expression/assignment/exponentiation.rs @@ -0,0 +1,94 @@ +//! Exponentiation operator parsing. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript specification][spec] +//! +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation +//! [spec]: https://tc39.es/ecma262/#sec-exp-operator + +use crate::syntax::{ + ast::{ + keyword::Keyword, + node::Node, + op::{BinOp, NumOp}, + punc::Punctuator, + token::TokenKind, + }, + parser::{ + expression::{unary::UnaryExpression, update::UpdateExpression}, + AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, + }, +}; + +/// Parses an exponentiation expression. +/// +/// More information: +/// - [MDN documentation][mdn] +/// - [ECMAScript specification][spec] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation +/// [spec]: https://tc39.es/ecma262/#prod-ExponentiationExpression +#[derive(Debug, Clone, Copy)] +pub(in crate::syntax::parser::expression) struct ExponentiationExpression { + allow_yield: AllowYield, + allow_await: AllowAwait, +} + +impl ExponentiationExpression { + /// Creates a new `ExponentiationExpression` parser. + pub(in crate::syntax::parser::expression) fn new(allow_yield: Y, allow_await: A) -> Self + where + Y: Into, + A: Into, + { + Self { + allow_yield: allow_yield.into(), + allow_await: allow_await.into(), + } + } +} + +impl ExponentiationExpression { + /// Checks by looking at the next token to see whether it's a unary operator or not. + fn is_unary_expression(cursor: &mut Cursor<'_>) -> bool { + if let Some(tok) = cursor.peek(0) { + match tok.kind { + TokenKind::Keyword(Keyword::Delete) + | TokenKind::Keyword(Keyword::Void) + | TokenKind::Keyword(Keyword::TypeOf) + | TokenKind::Punctuator(Punctuator::Add) + | TokenKind::Punctuator(Punctuator::Sub) + | TokenKind::Punctuator(Punctuator::Not) + | TokenKind::Punctuator(Punctuator::Neg) => true, + _ => false, + } + } else { + false + } + } +} + +impl TokenParser for ExponentiationExpression { + type Output = Node; + + fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + if Self::is_unary_expression(cursor) { + return UnaryExpression::new(self.allow_yield, self.allow_await).parse(cursor); + } + + let lhs = UpdateExpression::new(self.allow_yield, self.allow_await).parse(cursor)?; + if let Some(tok) = cursor.next() { + if let TokenKind::Punctuator(Punctuator::Exp) = tok.kind { + return Ok(Node::bin_op( + BinOp::Num(NumOp::Exp), + lhs, + self.parse(cursor)?, + )); + } else { + cursor.back(); + } + } + Ok(lhs) + } +} diff --git a/boa/src/syntax/parser/expression/unary.rs b/boa/src/syntax/parser/expression/unary.rs new file mode 100644 index 00000000000..14a2c7a29be --- /dev/null +++ b/boa/src/syntax/parser/expression/unary.rs @@ -0,0 +1,79 @@ +//! Unary operator parsing. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript specification][spec] +//! +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary +//! [spec]: https://tc39.es/ecma262/#sec-unary-operators + +use crate::syntax::{ + ast::{keyword::Keyword, node::Node, op::UnaryOp, punc::Punctuator, token::TokenKind}, + parser::{ + expression::update::UpdateExpression, AllowAwait, AllowYield, Cursor, ParseError, + ParseResult, TokenParser, + }, +}; + +/// Parses a unary expression. +/// +/// More information: +/// - [MDN documentation][mdn] +/// - [ECMAScript specification][spec] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary +/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression +#[derive(Debug, Clone, Copy)] +pub(super) struct UnaryExpression { + allow_yield: AllowYield, + allow_await: AllowAwait, +} + +impl UnaryExpression { + /// Creates a new `UnaryExpression` parser. + pub(super) fn new(allow_yield: Y, allow_await: A) -> Self + where + Y: Into, + A: Into, + { + Self { + allow_yield: allow_yield.into(), + allow_await: allow_await.into(), + } + } +} + +impl TokenParser for UnaryExpression { + type Output = Node; + + fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; + match tok.kind { + TokenKind::Keyword(Keyword::Delete) => { + Ok(Node::unary_op(UnaryOp::Delete, self.parse(cursor)?)) + } + TokenKind::Keyword(Keyword::Void) => { + Ok(Node::unary_op(UnaryOp::Void, self.parse(cursor)?)) + } + TokenKind::Keyword(Keyword::TypeOf) => { + Ok(Node::unary_op(UnaryOp::TypeOf, self.parse(cursor)?)) + } + TokenKind::Punctuator(Punctuator::Add) => { + Ok(Node::unary_op(UnaryOp::Plus, self.parse(cursor)?)) + } + TokenKind::Punctuator(Punctuator::Sub) => { + Ok(Node::unary_op(UnaryOp::Minus, self.parse(cursor)?)) + } + TokenKind::Punctuator(Punctuator::Neg) => { + Ok(Node::unary_op(UnaryOp::Tilde, self.parse(cursor)?)) + } + TokenKind::Punctuator(Punctuator::Not) => { + Ok(Node::unary_op(UnaryOp::Not, self.parse(cursor)?)) + } + _ => { + cursor.back(); + UpdateExpression::new(self.allow_yield, self.allow_await).parse(cursor) + } + } + } +}