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