Browse Source

Implement for..in (#976)

* Implement for..in

* Fix code styling issue

* Add break and continue with label

* Add break and continue for while and do-while

* Add unit tests

* Fix formatting

* Split node ForInOfLoop into ForInLoop and ForOfLoop

* Fix issues in ForInOfLoop node splitting

* Merge with master branch

Co-authored-by: tofpie <tofpie@users.noreply.github.com>
pull/1023/head
tofpie 4 years ago committed by GitHub
parent
commit
57d5679a58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      boa/src/builtins/iterable/mod.rs
  2. 1
      boa/src/builtins/mod.rs
  3. 145
      boa/src/builtins/object/for_in_iterator.rs
  4. 1
      boa/src/builtins/object/mod.rs
  5. 19
      boa/src/object/mod.rs
  6. 53
      boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs
  7. 243
      boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs
  8. 27
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  9. 5
      boa/src/syntax/ast/node/iteration/mod.rs
  10. 149
      boa/src/syntax/ast/node/iteration/tests.rs
  11. 4
      boa/src/syntax/ast/node/iteration/while_loop/mod.rs
  12. 7
      boa/src/syntax/ast/node/mod.rs
  13. 1
      boa/src/syntax/parser/error.rs
  14. 63
      boa/src/syntax/parser/expression/mod.rs
  15. 18
      boa/src/syntax/parser/statement/iteration/for_statement.rs
  16. 9
      boa/src/syntax/parser/statement/labelled_stm/mod.rs

15
boa/src/builtins/iterable/mod.rs

@ -1,6 +1,7 @@
use crate::{
builtins::string::string_iterator::StringIterator,
builtins::ArrayIterator,
builtins::ForInIterator,
builtins::MapIterator,
object::{GcObject, ObjectInitializer},
property::{Attribute, DataDescriptor},
@ -13,6 +14,7 @@ pub struct IteratorPrototypes {
array_iterator: GcObject,
string_iterator: GcObject,
map_iterator: GcObject,
for_in_iterator: GcObject,
}
impl IteratorPrototypes {
@ -28,9 +30,12 @@ impl IteratorPrototypes {
string_iterator: StringIterator::create_prototype(context, iterator_prototype.clone())
.as_object()
.expect("String Iterator Prototype is not an object"),
map_iterator: MapIterator::create_prototype(context, iterator_prototype)
map_iterator: MapIterator::create_prototype(context, iterator_prototype.clone())
.as_object()
.expect("Map Iterator Prototype is not an object"),
for_in_iterator: ForInIterator::create_prototype(context, iterator_prototype)
.as_object()
.expect("For In Iterator Prototype is not an object"),
}
}
@ -49,9 +54,15 @@ impl IteratorPrototypes {
self.string_iterator.clone()
}
#[inline]
pub fn map_iterator(&self) -> GcObject {
self.map_iterator.clone()
}
#[inline]
pub fn for_in_iterator(&self) -> GcObject {
self.for_in_iterator.clone()
}
}
/// CreateIterResultObject( value, done )
@ -110,7 +121,7 @@ pub struct IteratorRecord {
}
impl IteratorRecord {
fn new(iterator_object: Value, next_function: Value) -> Self {
pub fn new(iterator_object: Value, next_function: Value) -> Self {
Self {
iterator_object,
next_function,

1
boa/src/builtins/mod.rs

@ -37,6 +37,7 @@ pub(crate) use self::{
math::Math,
nan::NaN,
number::Number,
object::for_in_iterator::ForInIterator,
object::Object as BuiltInObjectObject,
regexp::RegExp,
string::String,

145
boa/src/builtins/object/for_in_iterator.rs

@ -0,0 +1,145 @@
use crate::property::PropertyKey;
use crate::value::RcString;
use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object},
gc::{Finalize, Trace},
object::ObjectData,
property::{Attribute, DataDescriptor},
BoaProfiler, Context, Result, Value,
};
use rustc_hash::FxHashSet;
use std::collections::VecDeque;
/// The ForInIterator object represents an iteration over some specific object.
/// It implements the iterator protocol.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-for-in-iterator-objects
#[derive(Debug, Clone, Finalize, Trace)]
pub struct ForInIterator {
object: Value,
visited_keys: FxHashSet<RcString>,
remaining_keys: VecDeque<RcString>,
object_was_visited: bool,
}
impl ForInIterator {
pub(crate) const NAME: &'static str = "ForInIterator";
fn new(object: Value) -> Self {
ForInIterator {
object,
visited_keys: FxHashSet::default(),
remaining_keys: VecDeque::default(),
object_was_visited: false,
}
}
/// CreateForInIterator( object )
///
/// Creates a new iterator over the given object.
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-createforiniterator
pub(crate) fn create_for_in_iterator(context: &Context, object: Value) -> Result<Value> {
let for_in_iterator = Value::new_object(context);
for_in_iterator.set_data(ObjectData::ForInIterator(Self::new(object)));
for_in_iterator
.as_object()
.expect("for in iterator object")
.set_prototype_instance(context.iterator_prototypes().for_in_iterator().into());
Ok(for_in_iterator)
}
/// %ForInIteratorPrototype%.next( )
///
/// Gets the next result in the object.
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%foriniteratorprototype%.next
pub(crate) fn next(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
if let Value::Object(ref o) = this {
let mut for_in_iterator = o.borrow_mut();
if let Some(iterator) = for_in_iterator.as_for_in_iterator_mut() {
let mut object = iterator.object.to_object(context)?;
loop {
if !iterator.object_was_visited {
let keys = object.own_property_keys();
for k in keys {
match k {
PropertyKey::String(ref k) => {
iterator.remaining_keys.push_back(k.clone());
}
PropertyKey::Index(i) => {
iterator.remaining_keys.push_back(i.to_string().into());
}
_ => {}
}
}
iterator.object_was_visited = true;
}
while let Some(r) = iterator.remaining_keys.pop_front() {
if !iterator.visited_keys.contains(&r) {
if let Some(desc) =
object.get_own_property(&PropertyKey::from(r.clone()))
{
iterator.visited_keys.insert(r.clone());
if desc.enumerable() {
return Ok(create_iter_result_object(
context,
Value::from(r.to_string()),
false,
));
}
}
}
}
match object.prototype_instance().to_object(context) {
Ok(o) => {
object = o;
}
_ => {
return Ok(create_iter_result_object(context, Value::undefined(), true))
}
}
iterator.object = Value::from(object.clone());
iterator.object_was_visited = false;
}
} else {
context.throw_type_error("`this` is not a ForInIterator")
}
} else {
context.throw_type_error("`this` is not an ForInIterator")
}
}
/// Create the %ArrayIteratorPrototype% object
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%foriniteratorprototype%-object
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: Value) -> Value {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let for_in_iterator = Value::new_object(context);
make_builtin_fn(Self::next, "next", &for_in_iterator, 0, context);
for_in_iterator
.as_object()
.expect("for in iterator prototype object")
.set_prototype_instance(iterator_prototype);
let to_string_tag = context.well_known_symbols().to_string_tag_symbol();
let to_string_tag_property =
DataDescriptor::new("For In Iterator", Attribute::CONFIGURABLE);
for_in_iterator.set_property(to_string_tag, to_string_tag_property);
for_in_iterator
}
}

1
boa/src/builtins/object/mod.rs

@ -23,6 +23,7 @@ use crate::{
BoaProfiler, Context, Result,
};
pub mod for_in_iterator;
#[cfg(test)]
mod tests;

19
boa/src/object/mod.rs

@ -29,6 +29,7 @@ mod gcobject;
mod internal_methods;
mod iter;
use crate::builtins::object::for_in_iterator::ForInIterator;
pub use gcobject::{GcObject, RecursionLimiter, Ref, RefMut};
pub use iter::*;
@ -84,6 +85,7 @@ pub enum ObjectData {
RegExp(Box<RegExp>),
BigInt(RcBigInt),
Boolean(bool),
ForInIterator(ForInIterator),
Function(Function),
String(RcString),
StringIterator(StringIterator),
@ -104,6 +106,7 @@ impl Display for ObjectData {
match self {
Self::Array => "Array",
Self::ArrayIterator(_) => "ArrayIterator",
Self::ForInIterator(_) => "ForInIterator",
Self::Function(_) => "Function",
Self::RegExp(_) => "RegExp",
Self::Map(_) => "Map",
@ -311,6 +314,22 @@ impl Object {
}
}
#[inline]
pub fn as_for_in_iterator(&self) -> Option<&ForInIterator> {
match &self.data {
ObjectData::ForInIterator(iter) => Some(iter),
_ => None,
}
}
#[inline]
pub fn as_for_in_iterator_mut(&mut self) -> Option<&mut ForInIterator> {
match &mut self.data {
ObjectData::ForInIterator(iter) => Some(iter),
_ => None,
}
}
/// Checks if it is a `Map` object.pub
#[inline]
pub fn is_map(&self) -> bool {

53
boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs

@ -42,6 +42,10 @@ impl DoWhileLoop {
self.label.as_ref().map(Box::as_ref)
}
pub fn set_label(&mut self, label: Box<str>) {
self.label = Some(label);
}
/// Creates a `DoWhileLoop` AST node.
pub fn new<B, C>(body: B, condition: C) -> Self
where
@ -68,50 +72,16 @@ impl DoWhileLoop {
impl Executable for DoWhileLoop {
fn run(&self, context: &mut Context) -> Result<Value> {
let mut result = self.body().run(context)?;
match context.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
context
.executor()
.set_current_state(InterpreterState::Executing);
return Ok(result);
}
InterpreterState::Continue(_label) => {
// TODO continue to label;
context
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
while self.cond().run(context)?.to_boolean() {
let mut result;
loop {
result = self.body().run(context)?;
match context.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
context
.executor()
.set_current_state(InterpreterState::Executing);
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, context, break);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
context
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, context, continue);
}
InterpreterState::Return => {
return Ok(result);
@ -120,6 +90,9 @@ impl Executable for DoWhileLoop {
// Continue execution.
}
}
if !self.cond().run(context)?.to_boolean() {
break;
}
}
Ok(result)
}

243
boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs

@ -0,0 +1,243 @@
use crate::{
builtins::{iterable::IteratorRecord, ForInIterator},
environment::lexical_environment::{new_declarative_environment, VariableScope},
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ForInLoop {
variable: Box<Node>,
expr: Box<Node>,
body: Box<Node>,
label: Option<Box<str>>,
}
impl ForInLoop {
pub fn new<V, I, B>(variable: V, expr: I, body: B) -> Self
where
V: Into<Node>,
I: Into<Node>,
B: Into<Node>,
{
Self {
variable: Box::new(variable.into()),
expr: Box::new(expr.into()),
body: Box::new(body.into()),
label: None,
}
}
pub fn variable(&self) -> &Node {
&self.variable
}
pub fn expr(&self) -> &Node {
&self.expr
}
pub fn body(&self) -> &Node {
&self.body
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
pub fn set_label(&mut self, label: Box<str>) {
self.label = Some(label);
}
pub fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
write!(f, "for ({} in {}) {{", self.variable, self.expr,)?;
self.body().display(f, indentation + 1)?;
f.write_str("}")
}
}
impl fmt::Display for ForInLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<ForInLoop> for Node {
fn from(for_in: ForInLoop) -> Node {
Self::ForInLoop(for_in)
}
}
impl Executable for ForInLoop {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("ForIn", "exec");
let object = self.expr().run(context)?;
let mut result = Value::undefined();
if object.is_null_or_undefined() {
return Ok(result);
}
let object = object.to_object(context)?;
let for_in_iterator = ForInIterator::create_for_in_iterator(context, Value::from(object))?;
let next_function = for_in_iterator
.get_property("next")
.map(|p| p.as_data_descriptor().unwrap().value())
.ok_or_else(|| context.construct_type_error("Could not find property `next`"))?;
let iterator = IteratorRecord::new(for_in_iterator, next_function);
loop {
{
let env = &mut context.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
}
let iterator_result = iterator.next(context)?;
if iterator_result.is_done() {
break;
}
let next_result = iterator_result.value();
match self.variable() {
Node::Identifier(ref name) => {
let environment = &mut context.realm_mut().environment;
if environment.has_binding(name.as_ref()) {
// Binding already exists
environment
.set_mutable_binding(name.as_ref(), next_result.clone(), true)
.map_err(|e| e.to_error(context))?;
} else {
environment
.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
)
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(name.as_ref(), next_result.clone())
.map_err(|e| e.to_error(context))?;
}
}
Node::VarDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer");
}
if environment.has_binding(var.name()) {
environment
.set_mutable_binding(var.name(), next_result, true)
.map_err(|e| e.to_error(context))?;
} else {
environment
.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Function,
)
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
}
}
_ => {
return context.throw_syntax_error(
"only one variable can be declared in the head of a for-in loop",
)
}
},
Node::LetDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer");
}
environment
.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
)
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
}
_ => {
return context.throw_syntax_error(
"only one variable can be declared in the head of a for-in loop",
)
}
},
Node::ConstDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer");
}
environment
.create_immutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
)
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
}
_ => {
return context.throw_syntax_error(
"only one variable can be declared in the head of a for-in loop",
)
}
},
Node::Assign(_) => {
return context.throw_syntax_error(
"a declaration in the head of a for-in loop can't have an initializer",
);
}
_ => {
return context
.throw_syntax_error("unknown left hand side in head of for-in loop")
}
}
result = self.body().run(context)?;
match context.executor().get_current_state() {
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, context, break);
break;
}
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, context, continue);
}
InterpreterState::Return => return Ok(result),
InterpreterState::Executing => {
// Continue execution.
}
}
let _ = context.realm_mut().environment.pop();
}
Ok(result)
}
}

27
boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs

@ -17,6 +17,7 @@ pub struct ForOfLoop {
variable: Box<Node>,
iterable: Box<Node>,
body: Box<Node>,
label: Option<Box<str>>,
}
impl ForOfLoop {
@ -30,6 +31,7 @@ impl ForOfLoop {
variable: Box::new(variable.into()),
iterable: Box::new(iterable.into()),
body: Box::new(body.into()),
label: None,
}
}
@ -45,6 +47,14 @@ impl ForOfLoop {
&self.body
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
pub fn set_label(&mut self, label: Box<str>) {
self.label = Some(label);
}
pub fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
write!(f, "for ({} of {}) {{", self.variable, self.iterable)?;
self.body().display(f, indentation + 1)?;
@ -205,21 +215,12 @@ impl Executable for ForOfLoop {
result = self.body().run(context)?;
match context.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
context
.executor()
.set_current_state(InterpreterState::Executing);
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, context, break);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
context
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, context, continue);
}
InterpreterState::Return => return Ok(result),
InterpreterState::Executing => {

5
boa/src/syntax/ast/node/iteration/mod.rs

@ -1,8 +1,8 @@
//! Iteration nodes
pub use self::{
continue_node::Continue, do_while_loop::DoWhileLoop, for_loop::ForLoop, for_of_loop::ForOfLoop,
while_loop::WhileLoop,
continue_node::Continue, do_while_loop::DoWhileLoop, for_in_loop::ForInLoop, for_loop::ForLoop,
for_of_loop::ForOfLoop, while_loop::WhileLoop,
};
#[cfg(test)]
@ -32,6 +32,7 @@ macro_rules! handle_state_with_labels {
pub mod continue_node;
pub mod do_while_loop;
pub mod for_in_loop;
pub mod for_loop;
pub mod for_of_loop;
pub mod while_loop;

149
boa/src/syntax/ast/node/iteration/tests.rs

@ -353,3 +353,152 @@ fn for_loop_continue_label() {
"#;
assert_eq!(&exec(scenario), "10");
}
#[test]
fn for_in_declaration() {
let mut context = Context::new();
let init = r#"
let result = [];
let obj = { a: "a", b: 2};
var i;
for (i in obj) {
result = result.concat([i]);
}
"#;
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(
&mut context,
"result.length === 2 && result.includes('a') && result.includes('b')"
),
"true"
);
}
#[test]
fn for_in_var_object() {
let mut context = Context::new();
let init = r#"
let result = [];
let obj = { a: "a", b: 2};
for (var i in obj) {
result = result.concat([i]);
}
"#;
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(
&mut context,
"result.length === 2 && result.includes('a') && result.includes('b')"
),
"true"
);
}
#[test]
fn for_in_var_array() {
let mut context = Context::new();
let init = r#"
let result = [];
let arr = ["a", "b"];
for (var i in arr) {
result = result.concat([i]);
}
"#;
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(
&mut context,
"result.length === 2 && result.includes('0') && result.includes('1')"
),
"true"
);
}
#[test]
fn for_in_let_object() {
let mut context = Context::new();
let init = r#"
let result = [];
let obj = { a: "a", b: 2};
for (let i in obj) {
result = result.concat([i]);
}
"#;
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(
&mut context,
"result.length === 2 && result.includes('a') && result.includes('b')"
),
"true"
);
}
#[test]
fn for_in_const_array() {
let mut context = Context::new();
let init = r#"
let result = [];
let arr = ["a", "b"];
for (const i in arr) {
result = result.concat([i]);
}
"#;
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(
&mut context,
"result.length === 2 && result.includes('0') && result.includes('1')"
),
"true"
);
}
#[test]
fn for_in_break_label() {
let scenario = r#"
var str = "";
outer: for (let i in [1, 2]) {
inner: for (let b in [2, 3, 4]) {
if (b === "1") {
break outer;
}
str = str + b;
}
str = str + i;
}
str
"#;
assert_eq!(&exec(scenario), "\"0\"")
}
#[test]
fn for_in_continue_label() {
let scenario = r#"
var str = "";
outer: for (let i in [1, 2]) {
inner: for (let b in [2, 3, 4]) {
if (b === "1") {
continue outer;
}
str = str + b;
}
str = str + i;
}
str
"#;
assert_eq!(&exec(scenario), "\"00\"")
}

4
boa/src/syntax/ast/node/iteration/while_loop/mod.rs

@ -41,6 +41,10 @@ impl WhileLoop {
self.label.as_ref().map(Box::as_ref)
}
pub fn set_label(&mut self, label: Box<str>) {
self.label = Some(label);
}
/// Creates a `WhileLoop` AST node.
pub fn new<C, B>(condition: C, body: B) -> Self
where

7
boa/src/syntax/ast/node/mod.rs

@ -33,7 +33,7 @@ pub use self::{
},
field::{GetConstField, GetField},
identifier::Identifier,
iteration::{Continue, DoWhileLoop, ForLoop, ForOfLoop, WhileLoop},
iteration::{Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},
new::New,
object::Object,
operator::{Assign, BinOp, UnaryOp},
@ -130,6 +130,9 @@ pub enum Node {
/// A `for` statement. [More information](./iteration/struct.ForLoop.html).
ForLoop(ForLoop),
/// A `for...of` or `for..in` statement. [More information](./iteration/struct.ForIn.html).
ForInLoop(ForInLoop),
/// A `for...of` statement. [More information](./iteration/struct.ForOf.html).
ForOfLoop(ForOfLoop),
@ -230,6 +233,7 @@ impl Node {
Self::ConditionalOp(ref cond_op) => Display::fmt(cond_op, f),
Self::ForLoop(ref for_loop) => for_loop.display(f, indentation),
Self::ForOfLoop(ref for_of) => for_of.display(f, indentation),
Self::ForInLoop(ref for_in) => for_in.display(f, indentation),
Self::This => write!(f, "this"),
Self::Try(ref try_catch) => try_catch.display(f, indentation),
Self::Break(ref break_smt) => Display::fmt(break_smt, f),
@ -290,6 +294,7 @@ impl Executable for Node {
Node::DoWhileLoop(ref do_while) => do_while.run(context),
Node::ForLoop(ref for_loop) => for_loop.run(context),
Node::ForOfLoop(ref for_of_loop) => for_of_loop.run(context),
Node::ForInLoop(ref for_in_loop) => for_in_loop.run(context),
Node::If(ref if_smt) => if_smt.run(context),
Node::ConditionalOp(ref op) => op.run(context),
Node::Switch(ref switch) => switch.run(context),

1
boa/src/syntax/parser/error.rs

@ -98,6 +98,7 @@ impl ParseError {
}
/// Creates a new `Unimplemented` parsing error.
#[allow(dead_code)]
pub(super) fn unimplemented(message: &'static str, position: Position) -> Self {
Self::Unimplemented { message, position }
}

63
boa/src/syntax/parser/expression/mod.rs

@ -473,20 +473,55 @@ impl RelationalExpression {
}
}
expression!(
RelationalExpression,
ShiftExpression,
[
Punctuator::LessThan,
Punctuator::GreaterThan,
Punctuator::LessThanOrEq,
Punctuator::GreaterThanOrEq,
Keyword::InstanceOf,
Keyword::In
],
[allow_yield, allow_await],
None::<InputElement>
);
impl<R> TokenParser<R> for RelationalExpression
where
R: Read,
{
type Output = Node;
fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("Relation Expression", "Parsing");
if None::<InputElement>.is_some() {
cursor.set_goal(None::<InputElement>.unwrap());
}
let mut lhs = ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor)?;
while let Some(tok) = cursor.peek(0)? {
match *tok.kind() {
TokenKind::Punctuator(op)
if op == Punctuator::LessThan
|| op == Punctuator::GreaterThan
|| op == Punctuator::LessThanOrEq
|| op == Punctuator::GreaterThanOrEq =>
{
let _ = cursor.next().expect("token disappeared");
lhs = BinOp::new(
op.as_binop().expect("Could not get binary operation."),
lhs,
ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor)?,
)
.into();
}
TokenKind::Keyword(op)
if op == Keyword::InstanceOf
|| (op == Keyword::In && self.allow_in == AllowIn(true)) =>
{
let _ = cursor.next().expect("token disappeared");
lhs = BinOp::new(
op.as_binop().expect("Could not get binary operation."),
lhs,
ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor)?,
)
.into();
}
_ => break,
}
}
Ok(lhs)
}
}
/// Parses a bitwise shift expression.
///

18
boa/src/syntax/parser/statement/iteration/for_statement.rs

@ -11,7 +11,7 @@ use crate::syntax::lexer::TokenKind;
use crate::{
syntax::{
ast::{
node::{ForLoop, ForOfLoop, Node},
node::{ForInLoop, ForLoop, ForOfLoop, Node},
Const, Keyword, Punctuator,
},
parser::{
@ -85,16 +85,18 @@ where
Some(Declaration::new(self.allow_yield, self.allow_await, false).parse(cursor)?)
}
TokenKind::Punctuator(Punctuator::Semicolon) => None,
_ => Some(Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?),
_ => Some(Expression::new(false, self.allow_yield, self.allow_await).parse(cursor)?),
};
match cursor.peek(0)? {
Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::In) => {
// TODO: for...in
return Err(ParseError::unimplemented(
"for...in loops",
tok.span().start(),
));
Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::In) && init.is_some() => {
let _ = cursor.next();
let expr =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "for in statement")?;
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor)?;
return Ok(ForInLoop::new(init.unwrap(), expr, body).into());
}
Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::Of) && init.is_some() => {
let _ = cursor.next();

9
boa/src/syntax/parser/statement/labelled_stm/mod.rs

@ -60,7 +60,12 @@ where
}
fn set_label_for_node(stmt: &mut Node, name: Box<str>) {
if let Node::ForLoop(ref mut for_loop) = stmt {
for_loop.set_label(name)
match stmt {
Node::ForLoop(ref mut for_loop) => for_loop.set_label(name),
Node::ForOfLoop(ref mut for_of_loop) => for_of_loop.set_label(name),
Node::ForInLoop(ref mut for_in_loop) => for_in_loop.set_label(name),
Node::DoWhileLoop(ref mut do_while_loop) => do_while_loop.set_label(name),
Node::WhileLoop(ref mut while_loop) => while_loop.set_label(name),
_ => (),
}
}

Loading…
Cancel
Save