mirror of https://github.com/boa-dev/boa.git
Browse Source
* 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 commitpull/862/head46b2917530
. * Revert "Update boa/src/syntax/parser/statement/mod.rs" This reverts commit4c0c1238ec
. * 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>
Paul Lancaster
4 years ago
committed by
GitHub
28 changed files with 824 additions and 109 deletions
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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()) |
||||||
|
} |
||||||
|
} |
@ -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)) |
||||||
|
} |
||||||
|
} |
@ -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()], |
||||||
|
); |
||||||
|
} |
@ -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()], |
||||||
|
); |
||||||
|
} |
@ -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)) |
||||||
|
} |
||||||
|
} |
@ -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()], |
||||||
|
); |
||||||
|
} |
@ -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()], |
||||||
|
); |
||||||
|
} |
@ -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), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue