mirror of https://github.com/boa-dev/boa.git
Kevin
3 years ago
committed by
GitHub
16 changed files with 862 additions and 20 deletions
@ -0,0 +1,94 @@ |
|||||||
|
//! Async Generator Declaration
|
||||||
|
|
||||||
|
use crate::{ |
||||||
|
exec::Executable, |
||||||
|
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, |
||||||
|
BoaProfiler, Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
use gc::{Finalize, Trace}; |
||||||
|
use std::fmt; |
||||||
|
|
||||||
|
#[cfg(feature = "deser")] |
||||||
|
use serde::{Deserialize, Serialize}; |
||||||
|
|
||||||
|
/// The 'async function*' defines an async generator function
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod
|
||||||
|
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] |
||||||
|
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] |
||||||
|
pub struct AsyncGeneratorDecl { |
||||||
|
name: Box<str>, |
||||||
|
parameters: Box<[FormalParameter]>, |
||||||
|
body: StatementList, |
||||||
|
} |
||||||
|
|
||||||
|
impl AsyncGeneratorDecl { |
||||||
|
/// Creates a new async generator declaration.
|
||||||
|
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self |
||||||
|
where |
||||||
|
N: Into<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) -> &str { |
||||||
|
&self.name |
||||||
|
} |
||||||
|
|
||||||
|
/// 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.items() |
||||||
|
} |
||||||
|
|
||||||
|
/// Implements the display formatting with indentation.
|
||||||
|
pub(in crate::syntax::ast::node) fn display( |
||||||
|
&self, |
||||||
|
f: &mut fmt::Formatter<'_>, |
||||||
|
indentation: usize, |
||||||
|
) -> fmt::Result { |
||||||
|
write!(f, "async function* {}(", self.name())?; |
||||||
|
join_nodes(f, &self.parameters)?; |
||||||
|
if self.body().is_empty() { |
||||||
|
f.write_str(") {}") |
||||||
|
} else { |
||||||
|
f.write_str(") {\n")?; |
||||||
|
self.body.display(f, indentation + 1)?; |
||||||
|
write!(f, "{}}}", " ".repeat(indentation)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Executable for AsyncGeneratorDecl { |
||||||
|
fn run(&self, _: &mut Context) -> JsResult<JsValue> { |
||||||
|
let _timer = BoaProfiler::global().start_event("AsyncGeneratorDecl", "exec"); |
||||||
|
//TODO: Implement AsyncGeneratorDecl
|
||||||
|
Ok(JsValue::undefined()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<AsyncGeneratorDecl> for Node { |
||||||
|
fn from(decl: AsyncGeneratorDecl) -> Self { |
||||||
|
Self::AsyncGeneratorDecl(decl) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for AsyncGeneratorDecl { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
self.display(f, 0) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
//! Async Generator Expression
|
||||||
|
|
||||||
|
use crate::{ |
||||||
|
exec::Executable, |
||||||
|
gc::{Finalize, Trace}, |
||||||
|
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
use std::fmt; |
||||||
|
|
||||||
|
#[cfg(feature = "deser")] |
||||||
|
use serde::{Deserialize, Serialize}; |
||||||
|
|
||||||
|
/// The `async function*` keyword can be used to define a generator function inside an expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression
|
||||||
|
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] |
||||||
|
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] |
||||||
|
pub struct AsyncGeneratorExpr { |
||||||
|
name: Option<Box<str>>, |
||||||
|
parameters: Box<[FormalParameter]>, |
||||||
|
body: StatementList, |
||||||
|
} |
||||||
|
|
||||||
|
impl AsyncGeneratorExpr { |
||||||
|
/// Creates a new async generator 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 async generator expression
|
||||||
|
pub fn name(&self) -> Option<&str> { |
||||||
|
self.name.as_ref().map(Box::as_ref) |
||||||
|
} |
||||||
|
|
||||||
|
/// Gets the list of parameters of the async generator expression
|
||||||
|
pub fn parameters(&self) -> &[FormalParameter] { |
||||||
|
&self.parameters |
||||||
|
} |
||||||
|
|
||||||
|
/// Gets the body of the async generator expression
|
||||||
|
pub fn body(&self) -> &StatementList { |
||||||
|
&self.body |
||||||
|
} |
||||||
|
|
||||||
|
pub(in crate::syntax::ast::node) fn display( |
||||||
|
&self, |
||||||
|
f: &mut fmt::Formatter<'_>, |
||||||
|
indentation: usize, |
||||||
|
) -> fmt::Result { |
||||||
|
f.write_str("async function*")?; |
||||||
|
if let Some(ref name) = self.name { |
||||||
|
write!(f, " {}", name)?; |
||||||
|
} |
||||||
|
f.write_str("(")?; |
||||||
|
join_nodes(f, &self.parameters)?; |
||||||
|
f.write_str(") ")?; |
||||||
|
self.display_block(f, indentation) |
||||||
|
} |
||||||
|
|
||||||
|
pub(in crate::syntax::ast::node) fn display_block( |
||||||
|
&self, |
||||||
|
f: &mut fmt::Formatter<'_>, |
||||||
|
indentation: usize, |
||||||
|
) -> fmt::Result { |
||||||
|
if self.body().items().is_empty() { |
||||||
|
f.write_str("{}") |
||||||
|
} else { |
||||||
|
f.write_str("{\n")?; |
||||||
|
self.body.display(f, indentation + 1)?; |
||||||
|
write!(f, "{}}}", " ".repeat(indentation)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Executable for AsyncGeneratorExpr { |
||||||
|
fn run(&self, _context: &mut Context) -> JsResult<JsValue> { |
||||||
|
//TODO: Implement AsyncGeneratorFunction
|
||||||
|
Ok(JsValue::undefined()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for AsyncGeneratorExpr { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
self.display(f, 0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<AsyncGeneratorExpr> for Node { |
||||||
|
fn from(expr: AsyncGeneratorExpr) -> Self { |
||||||
|
Self::AsyncGeneratorExpr(expr) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,129 @@ |
|||||||
|
//! Async Generator Expression Parser
|
||||||
|
//!
|
||||||
|
//! Implements TokenParser for AsyncGeneratorExpression and outputs
|
||||||
|
//! an AsyncGeneratorExpr ast node
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression
|
||||||
|
#[cfg(test)] |
||||||
|
mod test; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
syntax::{ |
||||||
|
ast::{node::AsyncGeneratorExpr, Keyword, Punctuator}, |
||||||
|
lexer::{Error as LexError, Position, TokenKind}, |
||||||
|
parser::{ |
||||||
|
function::{FormalParameters, FunctionBody}, |
||||||
|
statement::BindingIdentifier, |
||||||
|
Cursor, ParseError, TokenParser, |
||||||
|
}, |
||||||
|
}, |
||||||
|
BoaProfiler, |
||||||
|
}; |
||||||
|
|
||||||
|
use std::io::Read; |
||||||
|
|
||||||
|
/// Async Generator Expression Parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct AsyncGeneratorExpression; |
||||||
|
|
||||||
|
impl<R> TokenParser<R> for AsyncGeneratorExpression |
||||||
|
where |
||||||
|
R: Read, |
||||||
|
{ |
||||||
|
//The below needs to be implemented in ast::node
|
||||||
|
type Output = AsyncGeneratorExpr; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> { |
||||||
|
let _timer = BoaProfiler::global().start_event("AsyncGeneratorExpression", "Parsing"); |
||||||
|
|
||||||
|
cursor.peek_expect_no_lineterminator(0, "async generator expression")?; |
||||||
|
cursor.expect(Keyword::Function, "async generator expression")?; |
||||||
|
cursor.expect( |
||||||
|
TokenKind::Punctuator(Punctuator::Mul), |
||||||
|
"async generator expression", |
||||||
|
)?; |
||||||
|
|
||||||
|
let name = if let Some(token) = cursor.peek(0)? { |
||||||
|
match token.kind() { |
||||||
|
TokenKind::Punctuator(Punctuator::OpenParen) => None, |
||||||
|
_ => Some(BindingIdentifier::new(true, true).parse(cursor)?), |
||||||
|
} |
||||||
|
} else { |
||||||
|
return Err(ParseError::AbruptEnd); |
||||||
|
}; |
||||||
|
|
||||||
|
// Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict
|
||||||
|
// mode code, it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments".
|
||||||
|
if let Some(name) = &name { |
||||||
|
if cursor.strict_mode() && ["eval", "arguments"].contains(&name.as_ref()) { |
||||||
|
return Err(ParseError::lex(LexError::Syntax( |
||||||
|
"Unexpected eval or arguments in strict mode".into(), |
||||||
|
match cursor.peek(0)? { |
||||||
|
Some(token) => token.span().end(), |
||||||
|
None => Position::new(1, 1), |
||||||
|
}, |
||||||
|
))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let params_start_position = cursor |
||||||
|
.expect(Punctuator::OpenParen, "async generator expression")? |
||||||
|
.span() |
||||||
|
.end(); |
||||||
|
|
||||||
|
let params = FormalParameters::new(true, true).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseParen, "async generator expression")?; |
||||||
|
cursor.expect(Punctuator::OpenBlock, "async generator expression")?; |
||||||
|
|
||||||
|
let body = FunctionBody::new(true, true).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseBlock, "async generator expression")?; |
||||||
|
|
||||||
|
// Early Error: If the source code matching FormalParameters is strict mode code,
|
||||||
|
// the Early Error rules for UniqueFormalParameters : FormalParameters are applied.
|
||||||
|
if (cursor.strict_mode() || body.strict()) && params.has_duplicates { |
||||||
|
return Err(ParseError::lex(LexError::Syntax( |
||||||
|
"Duplicate parameter name not allowed in this context".into(), |
||||||
|
params_start_position, |
||||||
|
))); |
||||||
|
} |
||||||
|
|
||||||
|
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of GeneratorBody is true
|
||||||
|
// and IsSimpleParameterList of FormalParameters is false.
|
||||||
|
if body.strict() && !params.is_simple { |
||||||
|
return Err(ParseError::lex(LexError::Syntax( |
||||||
|
"Illegal 'use strict' directive in function with non-simple parameter list".into(), |
||||||
|
params_start_position, |
||||||
|
))); |
||||||
|
} |
||||||
|
|
||||||
|
// It is a Syntax Error if any element of the BoundNames of FormalParameters
|
||||||
|
// also occurs in the LexicallyDeclaredNames of FunctionBody.
|
||||||
|
{ |
||||||
|
let lexically_declared_names = body.lexically_declared_names(); |
||||||
|
for param in params.parameters.as_ref() { |
||||||
|
if lexically_declared_names.contains(param.name()) { |
||||||
|
return Err(ParseError::lex(LexError::Syntax( |
||||||
|
format!("Redeclaration of formal parameter `{}`", param.name()).into(), |
||||||
|
match cursor.peek(0)? { |
||||||
|
Some(token) => token.span().end(), |
||||||
|
None => Position::new(1, 1), |
||||||
|
}, |
||||||
|
))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//implement the below AsyncGeneratorExpr in ast::node
|
||||||
|
Ok(AsyncGeneratorExpr::new(name, params.parameters, body)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
use crate::syntax::{ |
||||||
|
ast::{ |
||||||
|
node::{AsyncGeneratorExpr, Declaration, DeclarationList, Return, StatementList}, |
||||||
|
Const, |
||||||
|
}, |
||||||
|
parser::tests::check_parser, |
||||||
|
}; |
||||||
|
|
||||||
|
///checks async generator expression parsing
|
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_async_generator_expr() { |
||||||
|
check_parser( |
||||||
|
"const add = async function*(){ |
||||||
|
return 1; |
||||||
|
}; |
||||||
|
", |
||||||
|
vec![DeclarationList::Const( |
||||||
|
vec![Declaration::new_with_identifier( |
||||||
|
"add", |
||||||
|
Some( |
||||||
|
AsyncGeneratorExpr::new::<Option<Box<str>>, _, StatementList>( |
||||||
|
None, |
||||||
|
[], |
||||||
|
vec![Return::new::<_, _, Option<Box<str>>>(Const::from(1), None).into()] |
||||||
|
.into(), |
||||||
|
) |
||||||
|
.into(), |
||||||
|
), |
||||||
|
)] |
||||||
|
.into(), |
||||||
|
) |
||||||
|
.into()], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_nested_async_generator_expr() { |
||||||
|
check_parser( |
||||||
|
"const a = async function*() { |
||||||
|
const b = async function*() { |
||||||
|
return 1; |
||||||
|
}; |
||||||
|
}; |
||||||
|
", |
||||||
|
vec![DeclarationList::Const( |
||||||
|
vec![Declaration::new_with_identifier( |
||||||
|
"a", |
||||||
|
Some( |
||||||
|
AsyncGeneratorExpr::new::<Option<Box<str>>, _, StatementList>( |
||||||
|
None, |
||||||
|
[], |
||||||
|
vec![DeclarationList::Const( |
||||||
|
vec![Declaration::new_with_identifier( |
||||||
|
"b", |
||||||
|
Some( |
||||||
|
AsyncGeneratorExpr::new::<Option<Box<str>>, _, StatementList>( |
||||||
|
None, |
||||||
|
[], |
||||||
|
vec![Return::new::<_, _, Option<Box<str>>>( |
||||||
|
Const::from(1), |
||||||
|
None, |
||||||
|
) |
||||||
|
.into()] |
||||||
|
.into(), |
||||||
|
) |
||||||
|
.into(), |
||||||
|
), |
||||||
|
)] |
||||||
|
.into(), |
||||||
|
) |
||||||
|
.into()] |
||||||
|
.into(), |
||||||
|
) |
||||||
|
.into(), |
||||||
|
), |
||||||
|
)] |
||||||
|
.into(), |
||||||
|
) |
||||||
|
.into()], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
//! Async Generator Declaration parsing
|
||||||
|
//!
|
||||||
|
//! Implements TokenParser for AsyncGeneratorDeclaration and outputs an
|
||||||
|
//! AsyncGeneratorDecl ast node
|
||||||
|
//!
|
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{node::AsyncGeneratorDecl, Keyword, Punctuator}, |
||||||
|
parser::{ |
||||||
|
statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, |
||||||
|
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
use std::io::Read; |
||||||
|
|
||||||
|
/// Async Generator Declaration Parser
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorDeclaration
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct AsyncGeneratorDeclaration { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
is_default: AllowDefault, |
||||||
|
} |
||||||
|
|
||||||
|
impl AsyncGeneratorDeclaration { |
||||||
|
/// Creates a new `AsyncGeneratorDeclaration` 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 CallableDeclaration for AsyncGeneratorDeclaration { |
||||||
|
#[inline] |
||||||
|
fn error_context(&self) -> &'static str { |
||||||
|
"async generator declaration" |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn is_default(&self) -> bool { |
||||||
|
self.is_default.0 |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn name_allow_yield(&self) -> bool { |
||||||
|
self.allow_yield.0 |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn name_allow_await(&self) -> bool { |
||||||
|
self.allow_await.0 |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn parameters_allow_yield(&self) -> bool { |
||||||
|
true |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn parameters_allow_await(&self) -> bool { |
||||||
|
true |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn body_allow_yield(&self) -> bool { |
||||||
|
true |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn body_allow_await(&self) -> bool { |
||||||
|
true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<R> TokenParser<R> for AsyncGeneratorDeclaration |
||||||
|
where |
||||||
|
R: Read, |
||||||
|
{ |
||||||
|
type Output = AsyncGeneratorDecl; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> { |
||||||
|
cursor.expect(Keyword::Async, "async hoistable declaration")?; |
||||||
|
cursor.peek_expect_no_lineterminator(0, "async hoistable declaration")?; |
||||||
|
cursor.expect(Keyword::Function, "async hoistable declaration")?; |
||||||
|
cursor.expect(Punctuator::Mul, "async generator declaration")?; |
||||||
|
|
||||||
|
let result = parse_callable_declaration(&self, cursor)?; |
||||||
|
|
||||||
|
Ok(AsyncGeneratorDecl::new(result.0, result.1, result.2)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
use crate::syntax::{ast::node::AsyncGeneratorDecl, parser::tests::check_parser}; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn async_generator_function_declaration() { |
||||||
|
check_parser( |
||||||
|
"async function* gen() {}", |
||||||
|
vec![AsyncGeneratorDecl::new(Box::from("gen"), vec![], vec![]).into()], |
||||||
|
); |
||||||
|
} |
Loading…
Reference in new issue