Skip to content

Commit

Permalink
Implement Generator parsing (#1575)
Browse files Browse the repository at this point in the history
  • Loading branch information
raskad authored Oct 6, 2021
1 parent 21a5b34 commit 45c8fa0
Show file tree
Hide file tree
Showing 32 changed files with 1,605 additions and 373 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
96 changes: 96 additions & 0 deletions boa/src/syntax/ast/node/declaration/generator_decl/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
BoaProfiler, Context, JsResult, JsValue,
};
use std::fmt;

#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};

/// The `function*` declaration (`function` keyword followed by an asterisk) defines a generator function,
/// which returns a `Generator` object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-GeneratorDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct GeneratorDecl {
name: Box<str>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}

impl GeneratorDecl {
/// Creates a new 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 generator declaration.
pub fn name(&self) -> &str {
&self.name
}

/// Gets the list of parameters of the generator declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}

/// Gets the body of the generator 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, "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 GeneratorDecl {
fn run(&self, _context: &mut Context) -> JsResult<JsValue> {
let _timer = BoaProfiler::global().start_event("GeneratorDecl", "exec");
// TODO: Implement GeneratorFunction
// https://tc39.es/ecma262/#sec-generatorfunction-objects
Ok(JsValue::undefined())
}
}

impl From<GeneratorDecl> for Node {
fn from(decl: GeneratorDecl) -> Self {
Self::GeneratorDecl(decl)
}
}

impl fmt::Display for GeneratorDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
109 changes: 109 additions & 0 deletions boa/src/syntax/ast/node/declaration/generator_expr/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
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 `function*` keyword can be used to define a generator function inside an expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function*
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct GeneratorExpr {
name: Option<Box<str>>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}

impl GeneratorExpr {
/// Creates a new 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 generator declaration.
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(Box::as_ref)
}

/// Gets the list of parameters of the generator declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}

/// Gets the body of the generator declaration.
pub fn body(&self) -> &StatementList {
&self.body
}

/// 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.display_block(f, indentation)
}

/// Displays the generator's body. This includes the curly braces at the start and end.
/// This will not indent the first brace, but will indent the last brace.
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 GeneratorExpr {
fn run(&self, _context: &mut Context) -> JsResult<JsValue> {
// TODO: Implement GeneratorFunction
// https://tc39.es/ecma262/#sec-generatorfunction-objects
Ok(JsValue::undefined())
}
}

impl fmt::Display for GeneratorExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}

impl From<GeneratorExpr> for Node {
fn from(expr: GeneratorExpr) -> Self {
Self::GeneratorExpr(expr)
}
}
2 changes: 2 additions & 0 deletions boa/src/syntax/ast/node/declaration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub mod async_function_decl;
pub mod async_function_expr;
pub mod function_decl;
pub mod function_expr;
pub mod generator_decl;
pub mod generator_expr;

pub use self::{
arrow_function_decl::ArrowFunctionDecl, async_function_decl::AsyncFunctionDecl,
Expand Down
33 changes: 30 additions & 3 deletions boa/src/syntax/ast/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod switch;
pub mod template;
pub mod throw;
pub mod try_node;
pub mod r#yield;

pub use self::{
array::ArrayDecl,
Expand All @@ -29,15 +30,17 @@ pub use self::{
call::Call,
conditional::{ConditionalOp, If},
declaration::{
ArrowFunctionDecl, AsyncFunctionDecl, AsyncFunctionExpr, Declaration, DeclarationList,
FunctionDecl, FunctionExpr,
generator_decl::GeneratorDecl, generator_expr::GeneratorExpr, ArrowFunctionDecl,
AsyncFunctionDecl, AsyncFunctionExpr, Declaration, DeclarationList, FunctionDecl,
FunctionExpr,
},
field::{GetConstField, GetField},
identifier::Identifier,
iteration::{Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},
new::New,
object::Object,
operator::{Assign, BinOp, UnaryOp},
r#yield::Yield,
return_smt::Return,
spread::Spread,
statement_list::{RcStatementList, StatementList},
Expand Down Expand Up @@ -209,6 +212,15 @@ pub enum Node {
/// [spec]: https://tc39.es/ecma262/#prod-EmptyStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/Empty
Empty,

/// A `yield` node. [More information](./yield/struct.Yield.html).
Yield(Yield),

/// A generator function declaration node. [More information](./declaration/struct.GeneratorDecl.html).
GeneratorDecl(GeneratorDecl),

/// A generator function expression node. [More information](./declaration/struct.GeneratorExpr.html).
GeneratorExpr(GeneratorExpr),
}

impl Display for Node {
Expand Down Expand Up @@ -302,6 +314,9 @@ impl Node {
Self::AsyncFunctionExpr(ref expr) => expr.display(f, indentation),
Self::AwaitExpr(ref expr) => Display::fmt(expr, f),
Self::Empty => write!(f, ";"),
Self::Yield(ref y) => Display::fmt(y, f),
Self::GeneratorDecl(ref decl) => Display::fmt(decl, f),
Self::GeneratorExpr(ref expr) => expr.display(f, indentation),
}
}
}
Expand Down Expand Up @@ -363,6 +378,9 @@ impl Executable for Node {
Node::Break(ref break_node) => break_node.run(context),
Node::Continue(ref continue_node) => continue_node.run(context),
Node::Empty => Ok(JsValue::undefined()),
Node::Yield(ref y) => y.run(context),
Node::GeneratorDecl(ref decl) => decl.run(context),
Node::GeneratorExpr(ref expr) => expr.run(context),
}
}
}
Expand Down Expand Up @@ -597,7 +615,16 @@ pub enum MethodDefinitionKind {
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Method_definition_syntax
Ordinary,
// TODO: support other method definition kinds, like `Generator`.

/// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#generator_methods
Generator,
}

unsafe impl Trace for MethodDefinitionKind {
Expand Down
15 changes: 14 additions & 1 deletion boa/src/syntax/ast/node/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl Object {
match &kind {
MethodDefinitionKind::Get => write!(f, "get ")?,
MethodDefinitionKind::Set => write!(f, "set ")?,
MethodDefinitionKind::Ordinary => (),
MethodDefinitionKind::Ordinary | MethodDefinitionKind::Generator => (),
}
write!(f, "{}(", key)?;
join_nodes(f, node.parameters())?;
Expand Down Expand Up @@ -166,6 +166,19 @@ impl Executable for Object {
context,
)?;
}
&MethodDefinitionKind::Generator => {
// TODO: Implement generator method definition execution.
obj.__define_own_property__(
name,
PropertyDescriptor::builder()
.value(JsValue::undefined())
.writable(true)
.enumerable(true)
.configurable(true)
.build(),
context,
)?;
}
}
}
// [spec]: https://tc39.es/ecma262/#sec-runtime-semantics-propertydefinitionevaluation
Expand Down
Loading

0 comments on commit 45c8fa0

Please sign in to comment.