Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Generator parsing #1575

Merged
merged 5 commits into from
Oct 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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