Skip to content

Commit

Permalink
Consolidate function-like declaration parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
raskad committed Oct 6, 2021
1 parent e1cd40a commit bee658b
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 243 deletions.
13 changes: 5 additions & 8 deletions boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct AsyncFunctionDecl {
name: Option<Box<str>>,
name: Box<str>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
Expand All @@ -31,7 +31,7 @@ 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>>>,
N: Into<Box<str>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Expand All @@ -43,8 +43,8 @@ impl AsyncFunctionDecl {
}

/// Gets the name of the async function declaration.
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
pub fn name(&self) -> &str {
&self.name
}

/// Gets the list of parameters of the async function declaration.
Expand All @@ -63,10 +63,7 @@ impl AsyncFunctionDecl {
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
match &self.name {
Some(name) => write!(f, "async function {}(", name)?,
None => write!(f, "async function (")?,
}
write!(f, "async function {}(", self.name())?;
join_nodes(f, &self.parameters)?;
if self.body().is_empty() {
f.write_str(") {}")
Expand Down
8 changes: 5 additions & 3 deletions boa/src/syntax/ast/node/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,16 @@ impl Executable for Object {
}
&MethodDefinitionKind::Generator => {
// TODO: Implement generator method definition execution.
obj.set_property(
obj.__define_own_property__(
name,
PropertyDescriptor::builder()
.value(JsValue::undefined())
.writable(true)
.enumerable(true)
.configurable(true),
);
.configurable(true)
.build(),
context,
)?;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@
mod tests;

use crate::syntax::{
ast::{node::AsyncFunctionDecl, Keyword, Punctuator},
lexer::TokenKind,
ast::{node::AsyncFunctionDecl, Keyword},
parser::{
function::FormalParameters,
function::FunctionBody,
statement::{BindingIdentifier, LexError, Position},
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
statement::declaration::hoistable::parse_function_like_declaration, AllowAwait,
AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
},
};
use std::io::Read;
Expand Down Expand Up @@ -54,91 +51,19 @@ where
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);
};

if let Some(name) = &name {
// 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 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 function declaration")?
.span()
.end();

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")?;

// 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 AsyncFunctionBody 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.
// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
{
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),
},
)));
}
}
}

Ok(AsyncFunctionDecl::new(name, params.parameters, body))
let result = parse_function_like_declaration(
"async function declaration",
self.is_default.0,
self.allow_yield.0,
self.allow_await.0,
false,
true,
false,
true,
cursor,
)?;

Ok(AsyncFunctionDecl::new(result.0, result.1, result.2))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
mod tests;

use crate::syntax::{
ast::{node::FunctionDecl, Keyword, Punctuator},
ast::{node::FunctionDecl, Keyword},
parser::{
function::FormalParameters,
function::FunctionBody,
statement::{BindingIdentifier, LexError, Position},
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
statement::declaration::hoistable::parse_function_like_declaration, AllowAwait,
AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
},
};
use std::io::Read;
Expand Down Expand Up @@ -57,71 +55,18 @@ where
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
cursor.expect(Keyword::Function, "function declaration")?;

// TODO: If self.is_default, then this can be empty.
let name = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;

// 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 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, "generator declaration")?
.span()
.end();

let params = FormalParameters::new(false, false).parse(cursor)?;

cursor.expect(Punctuator::CloseParen, "function declaration")?;
cursor.expect(Punctuator::OpenBlock, "function declaration")?;

let body = FunctionBody::new(self.allow_yield, self.allow_await).parse(cursor)?;

cursor.expect(Punctuator::CloseBlock, "function declaration")?;

// 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 FunctionBody 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.
// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
{
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),
},
)));
}
}
}

Ok(FunctionDecl::new(name, params.parameters, body))
let result = parse_function_like_declaration(
"function declaration",
self.is_default.0,
self.allow_yield.0,
self.allow_await.0,
false,
false,
self.allow_yield.0,
self.allow_await.0,
cursor,
)?;

Ok(FunctionDecl::new(result.0, result.1, result.2))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ mod tests;
use crate::syntax::{
ast::{node::declaration::generator_decl::GeneratorDecl, Keyword, Punctuator},
parser::{
function::FormalParameters,
function::FunctionBody,
statement::{BindingIdentifier, LexError, Position},
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
statement::declaration::hoistable::parse_function_like_declaration, AllowAwait,
AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
},
};
use std::io::Read;
Expand Down Expand Up @@ -53,70 +51,18 @@ where
cursor.expect(Keyword::Function, "generator declaration")?;
cursor.expect(Punctuator::Mul, "generator declaration")?;

// TODO: If self.is_default, then this can be empty.
let name = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;

// 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 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, "generator declaration")?
.span()
.end();

let params = FormalParameters::new(true, false).parse(cursor)?;

cursor.expect(Punctuator::CloseParen, "generator declaration")?;
cursor.expect(Punctuator::OpenBlock, "generator declaration")?;

let body = FunctionBody::new(true, false).parse(cursor)?;

cursor.expect(Punctuator::CloseBlock, "generator declaration")?;

// 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,
)));
}

// Early Error: It is a Syntax Error if any element of the BoundNames of FormalParameters
// also occurs in the LexicallyDeclaredNames of GeneratorBody.
{
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),
},
)));
}
}
}

Ok(GeneratorDecl::new(name, params.parameters, body))
let result = parse_function_like_declaration(
"generator declaration",
self.is_default.0,
self.allow_yield.0,
self.allow_await.0,
true,
false,
true,
false,
cursor,
)?;

Ok(GeneratorDecl::new(result.0, result.1, result.2))
}
}
Loading

0 comments on commit bee658b

Please sign in to comment.