Browse Source

Async/Await parse (#836)

* Add async keyword

* Created AsyncFunctionDecl/Expr nodes

* AsyncFunctionDecl/Expr parser created (not impl)

* Add async to HoistableDeclaration

* Added expect_no_lineterminator, updated some cursor docs

* AsyncFunctionDecl parsing

* AsyncFunctionExpr parsing

* Await expression parsing

* Added some async function expr parsing tests

* Reposition declaration parsing ready to add tests

* Moved function decl tests

* Added some failing async func decl tests

* mdn/spec links@

* Function expression parsing into own folder

* Added function expression test

* Fixed async function decl parse

* Async/await run returns undefined rather than panic

* Clippy :( -> :)

* Missing docs

* Update boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs

Co-authored-by: Halid Odat <halidodat@gmail.com>

* Update boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs

Co-authored-by: Halid Odat <halidodat@gmail.com>

* Update boa/src/syntax/parser/statement/mod.rs

Co-authored-by: Halid Odat <halidodat@gmail.com>

* Update boa/src/syntax/parser/statement/declaration/mod.rs

Co-authored-by: Halid Odat <halidodat@gmail.com>

* Update boa/src/syntax/ast/node/await_expr/mod.rs

Co-authored-by: Halid Odat <halidodat@gmail.com>

* Revert "Update boa/src/syntax/parser/statement/declaration/mod.rs"

This reverts commit 46b2917530.

* Revert "Update boa/src/syntax/parser/statement/mod.rs"

This reverts commit 4c0c1238ec.

* Update boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs

Co-authored-by: Iban Eguia <razican@protonmail.ch>

* Update boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs

Co-authored-by: Iban Eguia <razican@protonmail.ch>

* Addressed review comments

* Correct allow_yield parameters for async func

* Correct other usages of allow_yield

Co-authored-by: Halid Odat <halidodat@gmail.com>
Co-authored-by: Iban Eguia <razican@protonmail.ch>
pull/862/head
Paul Lancaster 4 years ago committed by GitHub
parent
commit
586a68970b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      boa/src/syntax/ast/keyword.rs
  2. 61
      boa/src/syntax/ast/node/await_expr/mod.rs
  3. 101
      boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs
  4. 98
      boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs
  5. 4
      boa/src/syntax/ast/node/declaration/mod.rs
  6. 23
      boa/src/syntax/ast/node/mod.rs
  7. 11
      boa/src/syntax/parser/cursor/mod.rs
  8. 2
      boa/src/syntax/parser/expression/assignment/arrow_function.rs
  9. 2
      boa/src/syntax/parser/expression/assignment/mod.rs
  10. 58
      boa/src/syntax/parser/expression/await_expr.rs
  11. 2
      boa/src/syntax/parser/expression/mod.rs
  12. 79
      boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs
  13. 65
      boa/src/syntax/parser/expression/primary/async_function_expression/tests.rs
  14. 3
      boa/src/syntax/parser/expression/primary/function_expression/mod.rs
  15. 58
      boa/src/syntax/parser/expression/primary/function_expression/tests.rs
  16. 8
      boa/src/syntax/parser/expression/primary/mod.rs
  17. 11
      boa/src/syntax/parser/expression/primary/object_initializer/mod.rs
  18. 4
      boa/src/syntax/parser/expression/unary.rs
  19. 89
      boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs
  20. 24
      boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs
  21. 73
      boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs
  22. 24
      boa/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs
  23. 82
      boa/src/syntax/parser/statement/declaration/hoistable/mod.rs
  24. 1
      boa/src/syntax/parser/statement/declaration/hoistable/tests.rs
  25. 2
      boa/src/syntax/parser/statement/declaration/mod.rs
  26. 28
      boa/src/syntax/parser/statement/declaration/tests.rs
  27. 6
      boa/src/syntax/parser/statement/mod.rs
  28. 2
      boa/src/syntax/parser/statement/throw/mod.rs

12
boa/src/syntax/ast/keyword.rs

@ -36,6 +36,16 @@ pub enum Keyword {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
Await,
/// The `async` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Async,
/// The `break` keyword.
///
/// More information:
@ -454,6 +464,7 @@ impl Keyword {
pub fn as_str(self) -> &'static str {
match self {
Self::Await => "await",
Self::Async => "async",
Self::Break => "break",
Self::Case => "case",
Self::Catch => "catch",
@ -526,6 +537,7 @@ impl FromStr for Keyword {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"await" => Ok(Self::Await),
"async" => Ok(Self::Async),
"break" => Ok(Self::Break),
"case" => Ok(Self::Case),
"catch" => Ok(Self::Catch),

61
boa/src/syntax/ast/node/await_expr/mod.rs

@ -0,0 +1,61 @@
//! Await expression node.
use super::Node;
use crate::{exec::Executable, BoaProfiler, Context, Result, Value};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// An await expression is used within an async function to pause execution and wait for a
/// promise to resolve.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct AwaitExpr {
expr: Box<Node>,
}
impl Executable for AwaitExpr {
fn run(&self, _: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("AwaitExpression", "exec");
// TODO: Implement AwaitExpr
Ok(Value::Undefined)
}
}
impl AwaitExpr {
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
writeln!(f, "await ")?;
self.expr.display(f, indentation)
}
}
impl<T> From<T> for AwaitExpr
where
T: Into<Box<Node>>,
{
fn from(e: T) -> Self {
Self { expr: e.into() }
}
}
impl fmt::Display for AwaitExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<AwaitExpr> for Node {
fn from(awaitexpr: AwaitExpr) -> Self {
Self::AwaitExpr(awaitexpr)
}
}

101
boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs

@ -0,0 +1,101 @@
//! Async Function Declaration.
use crate::{
exec::Executable,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// An async function is used to specify an action (or series of actions) to perform asynchronously.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-async-function-prototype-properties
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct AsyncFunctionDecl {
name: Option<Box<str>>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
impl AsyncFunctionDecl {
/// Creates a new async function declaration.
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self
where
N: Into<Option<Box<str>>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
name: name.into(),
parameters: parameters.into(),
body: body.into(),
}
}
/// Gets the name of the async function declaration.
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
/// Gets the list of parameters of the async function declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}
/// Gets the body of the async function declaration.
pub fn body(&self) -> &[Node] {
self.body.statements()
}
/// Implements the display formatting with indentation.
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
match &self.name {
Some(name) => {
write!(f, "async function {}(", name)?;
}
None => {
write!(f, "async function (")?;
}
}
join_nodes(f, &self.parameters)?;
f.write_str(") {{")?;
self.body.display(f, indentation + 1)?;
writeln!(f, "}}")
}
}
impl Executable for AsyncFunctionDecl {
fn run(&self, _: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("AsyncFunctionDecl", "exec");
// TODO: Implement AsyncFunctionDecl
Ok(Value::undefined())
}
}
impl From<AsyncFunctionDecl> for Node {
fn from(decl: AsyncFunctionDecl) -> Self {
Self::AsyncFunctionDecl(decl)
}
}
impl fmt::Display for AsyncFunctionDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}

98
boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs

@ -0,0 +1,98 @@
//! Async Function Expression.
use crate::{
exec::Executable,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// An async function expression is very similar to an async function declaration except used within
/// a wider expression (for example during an assignment).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct AsyncFunctionExpr {
name: Option<Box<str>>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
impl AsyncFunctionExpr {
/// Creates a new function expression
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self
where
N: Into<Option<Box<str>>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
name: name.into(),
parameters: parameters.into(),
body: body.into(),
}
}
/// Gets the name of the function declaration.
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(Box::as_ref)
}
/// Gets the list of parameters of the function declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}
/// Gets the body of the function declaration.
pub fn body(&self) -> &[Node] {
self.body.statements()
}
/// Implements the display formatting with indentation.
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
f.write_str("function")?;
if let Some(ref name) = self.name {
write!(f, " {}", name)?;
}
f.write_str("(")?;
join_nodes(f, &self.parameters)?;
f.write_str(") {{")?;
self.body.display(f, indentation + 1)?;
writeln!(f, "}}")
}
}
impl Executable for AsyncFunctionExpr {
fn run(&self, _: &mut Context) -> Result<Value> {
// TODO: Implement AsyncFunctionExpr
Ok(Value::Undefined)
}
}
impl fmt::Display for AsyncFunctionExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<AsyncFunctionExpr> for Node {
fn from(expr: AsyncFunctionExpr) -> Self {
Self::AsyncFunctionExpr(expr)
}
}

4
boa/src/syntax/ast/node/declaration/mod.rs

@ -1,6 +1,8 @@
//! Declaration nodes
pub mod arrow_function_decl;
pub mod async_function_decl;
pub mod async_function_expr;
pub mod const_decl_list;
pub mod function_decl;
pub mod function_expr;
@ -9,6 +11,8 @@ pub mod var_decl_list;
pub use self::{
arrow_function_decl::ArrowFunctionDecl,
async_function_decl::AsyncFunctionDecl,
async_function_expr::AsyncFunctionExpr,
const_decl_list::{ConstDecl, ConstDeclList},
function_decl::FunctionDecl,
function_expr::FunctionExpr,

23
boa/src/syntax/ast/node/mod.rs

@ -1,6 +1,7 @@
//! This module implements the `Node` structure, which composes the AST.
pub mod array;
pub mod await_expr;
pub mod block;
pub mod break_node;
pub mod call;
@ -21,13 +22,14 @@ pub mod try_node;
pub use self::{
array::ArrayDecl,
await_expr::AwaitExpr,
block::Block,
break_node::Break,
call::Call,
conditional::{ConditionalOp, If},
declaration::{
ArrowFunctionDecl, ConstDecl, ConstDeclList, FunctionDecl, FunctionExpr, LetDecl,
LetDeclList, VarDecl, VarDeclList,
ArrowFunctionDecl, AsyncFunctionDecl, AsyncFunctionExpr, ConstDecl, ConstDeclList,
FunctionDecl, FunctionExpr, LetDecl, LetDeclList, VarDecl, VarDeclList,
},
field::{GetConstField, GetField},
identifier::Identifier,
@ -65,6 +67,15 @@ pub enum Node {
/// An assignment operator node. [More information](./operator/struct.Assign.html).
Assign(Assign),
/// An async function declaration node. [More information](./declaration/struct.AsyncFunctionDecl.html).
AsyncFunctionDecl(AsyncFunctionDecl),
/// An async function expression node. [More information](./declaration/struct.AsyncFunctionExpr.html).
AsyncFunctionExpr(AsyncFunctionExpr),
/// An await expression node. [More information](./await_expr/struct.AwaitExpression.html).
AwaitExpr(AwaitExpr),
/// A binary operator node. [More information](./operator/struct.BinOp.html).
BinOp(BinOp),
@ -104,7 +115,7 @@ pub enum Node {
/// A function declaration node. [More information](./declaration/struct.FunctionDecl.html).
FunctionDecl(FunctionDecl),
/// A function expressino node. [More information](./declaration/struct.FunctionExpr.html).
/// A function expression node. [More information](./declaration/struct.FunctionExpr.html).
FunctionExpr(FunctionExpr),
/// Provides access to an object types' constant properties. [More information](./declaration/struct.GetConstField.html).
@ -243,6 +254,9 @@ impl Node {
Self::Assign(ref op) => Display::fmt(op, f),
Self::LetDeclList(ref decl) => Display::fmt(decl, f),
Self::ConstDeclList(ref decl) => Display::fmt(decl, f),
Self::AsyncFunctionDecl(ref decl) => decl.display(f, indentation),
Self::AsyncFunctionExpr(ref expr) => expr.display(f, indentation),
Self::AwaitExpr(ref expr) => expr.display(f, indentation),
}
}
}
@ -251,6 +265,9 @@ impl Executable for Node {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Executable", "exec");
match *self {
Node::AsyncFunctionDecl(ref decl) => decl.run(interpreter),
Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(interpreter),
Node::AwaitExpr(ref expr) => expr.run(interpreter),
Node::Call(ref call) => call.run(interpreter),
Node::Const(Const::Null) => Ok(Value::null()),
Node::Const(Const::Num(num)) => Ok(Value::rational(num)),

11
boa/src/syntax/parser/cursor/mod.rs

@ -67,10 +67,6 @@ where
}
/// Returns an error if the next token is not of kind `kind`.
///
/// Note: it will consume the next token only if the next token is the expected type.
///
/// If skip_line_terminators is true then line terminators will be discarded.
#[inline]
pub(super) fn expect<K>(&mut self, kind: K, context: &'static str) -> Result<Token, ParseError>
where
@ -134,15 +130,16 @@ where
///
/// It expects that the token stream does not end here.
///
/// This is just syntatic sugar for a .peek(skip_n, false) call followed by a check that the result is not a line terminator or None.
/// This is just syntatic sugar for a .peek(skip_n) call followed by a check that the result is not a line terminator or None.
#[inline]
pub(super) fn peek_expect_no_lineterminator(
&mut self,
skip_n: usize,
context: &'static str,
) -> Result<&Token, ParseError> {
if let Some(t) = self.buffered_lexer.peek(skip_n, false)? {
if t.kind() == &TokenKind::LineTerminator {
Err(ParseError::unexpected(t.clone(), None))
Err(ParseError::unexpected(t.clone(), context))
} else {
Ok(t)
}
@ -156,8 +153,6 @@ where
/// When the next token is a `kind` token, get the token, otherwise return `None`.
///
/// No next token also returns None.
///
/// If skip_line_terminators is true then line terminators will be discarded.
#[inline]
pub(super) fn next_if<K>(&mut self, kind: K) -> Result<Option<Token>, ParseError>
where

2
boa/src/syntax/parser/expression/assignment/arrow_function.rs

@ -86,7 +86,7 @@ where
Box::new([FormalParameter::new(param, None, false)])
};
cursor.peek_expect_no_lineterminator(0)?;
cursor.peek_expect_no_lineterminator(0, "arrow function")?;
cursor.expect(TokenKind::Punctuator(Punctuator::Arrow), "arrow function")?;
let body = ConciseBody::new(self.allow_in).parse(cursor)?;

2
boa/src/syntax/parser/expression/assignment/mod.rs

@ -88,7 +88,7 @@ where
TokenKind::Identifier(_)
| TokenKind::Keyword(Keyword::Yield)
| TokenKind::Keyword(Keyword::Await) => {
if let Ok(tok) = cursor.peek_expect_no_lineterminator(1) {
if let Ok(tok) = cursor.peek_expect_no_lineterminator(1, "assignment expression") {
if tok.kind() == &TokenKind::Punctuator(Punctuator::Arrow) {
return ArrowFunction::new(
self.allow_in,

58
boa/src/syntax/parser/expression/await_expr.rs

@ -0,0 +1,58 @@
//! Await expression parsing.
//!
//! More information:
//! - [MDN documentation][mdn]
//! - [ECMAScript specification][spec]
//!
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
//! [spec]: https://tc39.es/ecma262/#prod-AwaitExpression
use super::unary::UnaryExpression;
use crate::syntax::{
ast::{node::AwaitExpr, Keyword},
lexer::TokenKind,
parser::{AllowYield, Cursor, ParseError, TokenParser},
};
use std::io::Read;
/// Parses an await expression.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript specification][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct AwaitExpression {
allow_yield: AllowYield,
}
impl AwaitExpression {
/// Creates a new `AwaitExpression` parser.
pub(in crate::syntax::parser) fn new<Y>(allow_yield: Y) -> Self
where
Y: Into<AllowYield>,
{
Self {
allow_yield: allow_yield.into(),
}
}
}
impl<R> TokenParser<R> for AwaitExpression
where
R: Read,
{
type Output = AwaitExpr;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
cursor.expect(
TokenKind::Keyword(Keyword::Await),
"Await expression parsing",
)?;
let expr = UnaryExpression::new(self.allow_yield, true).parse(cursor)?;
Ok(expr.into())
}
}

2
boa/src/syntax/parser/expression/mod.rs

@ -15,6 +15,8 @@ mod tests;
mod unary;
mod update;
pub(in crate::syntax::parser) mod await_expr;
use self::assignment::ExponentiationExpression;
pub(super) use self::{assignment::AssignmentExpression, primary::Initializer};
use super::{AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser};

79
boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs

@ -0,0 +1,79 @@
#[cfg(test)]
mod tests;
use crate::{
syntax::{
ast::{node::AsyncFunctionExpr, Keyword, Punctuator},
lexer::TokenKind,
parser::{
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
AllowYield, Cursor, ParseError, TokenParser,
},
},
BoaProfiler,
};
use std::io::Read;
/// Async Function expression parsing.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript specification][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function
/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct AsyncFunctionExpression {
allow_yield: AllowYield,
}
impl AsyncFunctionExpression {
/// Creates a new `AsyncFunctionExpression` parser.
pub(super) fn new<Y>(allow_yield: Y) -> Self
where
Y: Into<AllowYield>,
{
Self {
allow_yield: allow_yield.into(),
}
}
}
impl<R> TokenParser<R> for AsyncFunctionExpression
where
R: Read,
{
type Output = AsyncFunctionExpr;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("AsyncFunctionExpression", "Parsing");
cursor.peek_expect_no_lineterminator(0, "async function expression")?;
cursor.expect(Keyword::Function, "async function expression")?;
let tok = cursor.peek(0)?;
let name = if let Some(token) = tok {
match token.kind() {
TokenKind::Punctuator(Punctuator::OpenParen) => None,
_ => Some(BindingIdentifier::new(self.allow_yield, true).parse(cursor)?),
}
} else {
return Err(ParseError::AbruptEnd);
};
cursor.expect(Punctuator::OpenParen, "async function expression")?;
let params = FormalParameters::new(false, true).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "async function expression")?;
cursor.expect(Punctuator::OpenBlock, "async function expression")?;
let body = FunctionBody::new(false, true).parse(cursor)?;
cursor.expect(Punctuator::CloseBlock, "async function expression")?;
Ok(AsyncFunctionExpr::new(name, params, body))
}
}

65
boa/src/syntax/parser/expression/primary/async_function_expression/tests.rs

@ -0,0 +1,65 @@
use crate::syntax::{
ast::{
node::{AsyncFunctionExpr, ConstDecl, ConstDeclList, Return, StatementList},
Const,
},
parser::tests::check_parser,
};
/// Checks async expression parsing.
#[test]
fn check_async_expression() {
check_parser(
"const add = async function() {
return 1;
};
",
vec![ConstDeclList::from(vec![ConstDecl::new(
"add",
Some(
AsyncFunctionExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![Return::new::<_, _, Option<Box<str>>>(Const::from(1), None).into()].into(),
),
),
)])
.into()],
);
}
#[test]
fn check_nested_async_expression() {
check_parser(
"const a = async function() {
const b = async function() {
return 1;
};
};
",
vec![ConstDeclList::from(vec![ConstDecl::new(
"a",
Some(
AsyncFunctionExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![ConstDeclList::from(vec![ConstDecl::new(
"b",
Some(
AsyncFunctionExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![Return::new::<_, _, Option<Box<str>>>(Const::from(1), None)
.into()]
.into(),
),
),
)])
.into()]
.into(),
),
),
)])
.into()],
);
}

3
boa/src/syntax/parser/expression/primary/function_expression.rs → boa/src/syntax/parser/expression/primary/function_expression/mod.rs

@ -7,6 +7,9 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
//! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
#[cfg(test)]
mod tests;
use crate::{
syntax::{
ast::{node::FunctionExpr, Keyword, Punctuator},

58
boa/src/syntax/parser/expression/primary/function_expression/tests.rs

@ -0,0 +1,58 @@
use crate::syntax::{
ast::{
node::{ConstDecl, ConstDeclList, FunctionExpr, Return, StatementList},
Const,
},
parser::tests::check_parser,
};
/// Checks async expression parsing.
#[test]
fn check_function_expression() {
check_parser(
"const add = function() {
return 1;
};
",
vec![ConstDeclList::from(vec![ConstDecl::new(
"add",
Some(FunctionExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![Return::new::<_, _, Option<Box<str>>>(Const::from(1), None).into()].into(),
)),
)])
.into()],
);
}
#[test]
fn check_nested_function_expression() {
check_parser(
"const a = function() {
const b = function() {
return 1;
};
};
",
vec![ConstDeclList::from(vec![ConstDecl::new(
"a",
Some(FunctionExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![ConstDeclList::from(vec![ConstDecl::new(
"b",
Some(FunctionExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![Return::new::<_, _, Option<Box<str>>>(Const::from(1), None).into()]
.into(),
)),
)])
.into()]
.into(),
)),
)])
.into()],
);
}

8
boa/src/syntax/parser/expression/primary/mod.rs

@ -8,14 +8,15 @@
//! [spec]: https://tc39.es/ecma262/#prod-PrimaryExpression
mod array_initializer;
mod async_function_expression;
mod function_expression;
mod object_initializer;
#[cfg(test)]
mod tests;
use self::{
array_initializer::ArrayLiteral, function_expression::FunctionExpression,
object_initializer::ObjectLiteral,
array_initializer::ArrayLiteral, async_function_expression::AsyncFunctionExpression,
function_expression::FunctionExpression, object_initializer::ObjectLiteral,
};
use super::Expression;
use crate::{
@ -77,6 +78,9 @@ where
TokenKind::Keyword(Keyword::Function) => {
FunctionExpression.parse(cursor).map(Node::from)
}
TokenKind::Keyword(Keyword::Async) => AsyncFunctionExpression::new(self.allow_yield)
.parse(cursor)
.map(Node::from),
TokenKind::Punctuator(Punctuator::OpenParen) => {
cursor.set_goal(InputElement::RegExp);
let expr =

11
boa/src/syntax/parser/expression/primary/object_initializer/mod.rs

@ -141,6 +141,17 @@ where
return Ok(node::PropertyDefinition::property(prop_name, val));
}
// TODO GeneratorMethod
// https://tc39.es/ecma262/#prod-GeneratorMethod
if prop_name.as_str() == "async" {
// TODO - AsyncMethod.
// https://tc39.es/ecma262/#prod-AsyncMethod
// TODO - AsyncGeneratorMethod
// https://tc39.es/ecma262/#prod-AsyncGeneratorMethod
}
if cursor
.next_if(TokenKind::Punctuator(Punctuator::OpenParen))?
.is_some()

4
boa/src/syntax/parser/expression/unary.rs

@ -33,14 +33,14 @@ use std::io::Read;
/// [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 {
pub(in crate::syntax::parser) struct UnaryExpression {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl UnaryExpression {
/// Creates a new `UnaryExpression` parser.
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,

89
boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs

@ -0,0 +1,89 @@
#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{node::AsyncFunctionDecl, Keyword, Punctuator},
lexer::TokenKind,
parser::{
function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
},
};
use std::io::Read;
/// Async Function declaration parsing.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript specification][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
/// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#prod-AsyncFunctionDeclaration
#[derive(Debug, Clone, Copy)]
pub(super) struct AsyncFunctionDeclaration {
allow_yield: AllowYield,
allow_await: AllowAwait,
is_default: AllowDefault,
}
impl AsyncFunctionDeclaration {
/// Creates a new `FunctionDeclaration` parser.
pub(super) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
D: Into<AllowDefault>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
is_default: is_default.into(),
}
}
}
impl<R> TokenParser<R> for AsyncFunctionDeclaration
where
R: Read,
{
type Output = AsyncFunctionDecl;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
cursor.expect(Keyword::Async, "async function declaration")?;
cursor.peek_expect_no_lineterminator(0, "async function declaration")?;
cursor.expect(Keyword::Function, "async function declaration")?;
let tok = cursor.peek(0)?;
let name = if let Some(token) = tok {
match token.kind() {
TokenKind::Punctuator(Punctuator::OpenParen) => {
if !self.is_default.0 {
return Err(ParseError::unexpected(
token.clone(),
" in async function declaration",
));
}
None
}
_ => {
Some(BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?)
}
}
} else {
return Err(ParseError::AbruptEnd);
};
cursor.expect(Punctuator::OpenParen, "async function declaration")?;
let params = FormalParameters::new(false, true).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "async function declaration")?;
cursor.expect(Punctuator::OpenBlock, "async function declaration")?;
let body = FunctionBody::new(false, true).parse(cursor)?;
cursor.expect(Punctuator::CloseBlock, "async function declaration")?;
Ok(AsyncFunctionDecl::new(name, params, body))
}
}

24
boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs

@ -0,0 +1,24 @@
use crate::syntax::{ast::node::AsyncFunctionDecl, parser::tests::check_parser};
/// Async function declaration parsing.
#[test]
fn async_function_declaration() {
check_parser(
"async function hello() {}",
vec![AsyncFunctionDecl::new(Box::from("hello"), vec![], vec![]).into()],
);
}
/// Async function declaration parsing with keywords.
#[test]
fn async_function_declaration_keywords() {
check_parser(
"async function yield() {}",
vec![AsyncFunctionDecl::new(Box::from("yield"), vec![], vec![]).into()],
);
check_parser(
"async function await() {}",
vec![AsyncFunctionDecl::new(Box::from("await"), vec![], vec![]).into()],
);
}

73
boa/src/syntax/parser/statement/declaration/hoistable.rs → boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs

@ -1,67 +1,15 @@
//! Hoistable declaration parsing.
//!
//! More information:
//! - [ECMAScript specification][spec]
//!
//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration
use crate::{
syntax::{
ast::{node::FunctionDecl, Keyword, Node, Punctuator},
parser::{
function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{node::FunctionDecl, Keyword, Punctuator},
parser::{
function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
},
BoaProfiler,
};
use std::io::Read;
/// Hoistable declaration parsing.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration
#[derive(Debug, Clone, Copy)]
pub(super) struct HoistableDeclaration {
allow_yield: AllowYield,
allow_await: AllowAwait,
is_default: AllowDefault,
}
impl HoistableDeclaration {
/// Creates a new `HoistableDeclaration` parser.
pub(super) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
D: Into<AllowDefault>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
is_default: is_default.into(),
}
}
}
impl<R> TokenParser<R> for HoistableDeclaration
where
R: Read,
{
type Output = Node;
fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("HoistableDeclaration", "Parsing");
// TODO: check for generators and async functions + generators
FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default)
.parse(cursor)
.map(Node::from)
}
}
/// Function declaration parsing.
///
/// More information:
@ -70,8 +18,9 @@ where
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration
#[derive(Debug, Clone, Copy)]
struct FunctionDeclaration {
pub(super) struct FunctionDeclaration {
allow_yield: AllowYield,
allow_await: AllowAwait,
is_default: AllowDefault,
@ -79,7 +28,7 @@ struct FunctionDeclaration {
impl FunctionDeclaration {
/// Creates a new `FunctionDeclaration` parser.
fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self
pub(super) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,

24
boa/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs

@ -0,0 +1,24 @@
use crate::syntax::{ast::node::FunctionDecl, parser::tests::check_parser};
/// Function declaration parsing.
#[test]
fn function_declaration() {
check_parser(
"function hello() {}",
vec![FunctionDecl::new(Box::from("hello"), vec![], vec![]).into()],
);
}
/// Function declaration parsing with keywords.
#[test]
fn function_declaration_keywords() {
check_parser(
"function yield() {}",
vec![FunctionDecl::new(Box::from("yield"), vec![], vec![]).into()],
);
check_parser(
"function await() {}",
vec![FunctionDecl::new(Box::from("await"), vec![], vec![]).into()],
);
}

82
boa/src/syntax/parser/statement/declaration/hoistable/mod.rs

@ -0,0 +1,82 @@
//! Hoistable declaration parsing.
//!
//! More information:
//! - [ECMAScript specification][spec]
//!
//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration
#[cfg(test)]
mod tests;
mod async_function_decl;
mod function_decl;
use async_function_decl::AsyncFunctionDeclaration;
use function_decl::FunctionDeclaration;
use crate::{
syntax::{
ast::{Keyword, Node},
lexer::TokenKind,
parser::{
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
},
BoaProfiler,
};
use std::io::Read;
/// Hoistable declaration parsing.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration
#[derive(Debug, Clone, Copy)]
pub(super) struct HoistableDeclaration {
allow_yield: AllowYield,
allow_await: AllowAwait,
is_default: AllowDefault,
}
impl HoistableDeclaration {
/// Creates a new `HoistableDeclaration` parser.
pub(super) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
D: Into<AllowDefault>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
is_default: is_default.into(),
}
}
}
impl<R> TokenParser<R> for HoistableDeclaration
where
R: Read,
{
type Output = Node;
fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("HoistableDeclaration", "Parsing");
let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?;
match tok.kind() {
TokenKind::Keyword(Keyword::Function) => {
FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default)
.parse(cursor)
.map(Node::from)
}
TokenKind::Keyword(Keyword::Async) => {
AsyncFunctionDeclaration::new(self.allow_yield, self.allow_await, false)
.parse(cursor)
.map(Node::from)
}
_ => unreachable!("unknown token found: {:?}", tok),
}
}
}

1
boa/src/syntax/parser/statement/declaration/hoistable/tests.rs

@ -0,0 +1 @@

2
boa/src/syntax/parser/statement/declaration/mod.rs

@ -63,7 +63,7 @@ where
let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?;
match tok.kind() {
TokenKind::Keyword(Keyword::Function) => {
TokenKind::Keyword(Keyword::Function) | TokenKind::Keyword(Keyword::Async) => {
HoistableDeclaration::new(self.allow_yield, self.allow_await, false).parse(cursor)
}
TokenKind::Keyword(Keyword::Const) | TokenKind::Keyword(Keyword::Let) => {

28
boa/src/syntax/parser/statement/declaration/tests.rs

@ -1,9 +1,6 @@
use crate::syntax::{
ast::{
node::{
ConstDecl, ConstDeclList, FunctionDecl, LetDecl, LetDeclList, Node, VarDecl,
VarDeclList,
},
node::{ConstDecl, ConstDeclList, LetDecl, LetDeclList, Node, VarDecl, VarDeclList},
Const,
},
parser::tests::{check_invalid, check_parser},
@ -169,26 +166,3 @@ fn multiple_const_declaration() {
.into()],
);
}
/// Function declaration parsing.
#[test]
fn function_declaration() {
check_parser(
"function hello() {}",
vec![FunctionDecl::new(Box::from("hello"), vec![], vec![]).into()],
);
}
/// Function declaration parsing with keywords.
#[test]
fn function_declaration_keywords() {
check_parser(
"function yield() {}",
vec![FunctionDecl::new(Box::from("yield"), vec![], vec![]).into()],
);
check_parser(
"function await() {}",
vec![FunctionDecl::new(Box::from("await"), vec![], vec![]).into()],
);
}

6
boa/src/syntax/parser/statement/mod.rs

@ -42,6 +42,7 @@ use crate::{
syntax::{
ast::{node, Keyword, Node, Punctuator},
lexer::{Error as LexError, InputElement, TokenKind},
parser::expression::await_expr::AwaitExpression,
},
BoaProfiler,
};
@ -110,6 +111,9 @@ where
let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?;
match tok.kind() {
TokenKind::Keyword(Keyword::Await) => AwaitExpression::new(self.allow_yield)
.parse(cursor)
.map(Node::from),
TokenKind::Keyword(Keyword::If) => {
IfStatement::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor)
@ -386,7 +390,7 @@ where
let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?;
match *tok.kind() {
TokenKind::Keyword(Keyword::Function) => {
TokenKind::Keyword(Keyword::Function) | TokenKind::Keyword(Keyword::Async) => {
if strict_mode && self.in_block {
return Err(ParseError::lex(LexError::Syntax(
"Function declaration in blocks not allowed in strict mode".into(),

2
boa/src/syntax/parser/statement/throw/mod.rs

@ -50,7 +50,7 @@ where
let _timer = BoaProfiler::global().start_event("ThrowStatement", "Parsing");
cursor.expect(Keyword::Throw, "throw statement")?;
cursor.peek_expect_no_lineterminator(0)?;
cursor.peek_expect_no_lineterminator(0, "throw statement")?;
let expr = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?;
if let Some(tok) = cursor.peek(0)? {

Loading…
Cancel
Save