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

All Expression Nodes Evaluate #649

Closed
wants to merge 3 commits into from
Closed
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
7 changes: 7 additions & 0 deletions boa/src/exec/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ impl Executable for Block {
// Early break.
break;
}
InterpreterState::Continue(_label) => {
// TODO, continue to a label.

// Continue actually breaks out of the block, because the containing loop will handle the control
// flow.
break;
}
_ => {
// Continue execution
}
Expand Down
29 changes: 27 additions & 2 deletions boa/src/exec/conditional/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
use super::{Executable, Interpreter};
use crate::{builtins::Value, syntax::ast::node::If, Result};
use super::{Executable, Interpreter, InterpreterState};
use crate::{
builtins::Value,
syntax::ast::node::{ConditionalOp, Continue, If},
Result,
};
use std::borrow::Borrow;

#[cfg(test)]
mod tests;

impl Executable for If {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.borrow().to_boolean() {
Expand All @@ -13,3 +20,21 @@ impl Executable for If {
})
}
}

impl Executable for ConditionalOp {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
if self.cond().run(interpreter)?.to_boolean() {
self.if_true().run(interpreter)
} else {
self.if_false().run(interpreter)
}
}
}

impl Executable for Continue {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
interpreter.set_current_state(InterpreterState::Continue(self.label().map(String::from)));

Ok(Value::undefined())
}
}
111 changes: 111 additions & 0 deletions boa/src/exec/conditional/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use crate::exec;

#[test]
fn if_true() {
let scenario = r#"
var a = 0;
if (true) {
a = 100;
} else {
a = 50;
}
a;
"#;

assert_eq!(&exec(scenario), "100");
}

#[test]
fn if_false() {
let scenario = r#"
var a = 0;
if (false) {
a = 100;
} else {
a = 50;
}
a;
"#;

assert_eq!(&exec(scenario), "50");
}

#[test]
fn if_truthy() {
let scenario = r#"
var a = 0;
if ("this value doesn't matter") {
a = 100;
} else {
a = 50;
}
a;
"#;

assert_eq!(&exec(scenario), "100");
}

#[test]
fn if_falsy() {
let scenario = r#"
var a = 0;
if (null) {
a = 100;
} else {
a = 50;
}
a;
"#;

assert_eq!(&exec(scenario), "50");
}

#[test]
fn if_throws() {
let scenario = r#"
var a = 0;
if ((function() { throw new Error("Oh no!") })()) {
a = 100;
} else {
a = 50;
}
a;
"#;

assert_eq!(&exec(scenario), r#"Error: "Error": "Oh no!""#);
}

#[test]
fn conditional_op_true() {
let scenario = r#"true ? 100 : 50"#;

assert_eq!(&exec(scenario), "100");
}

#[test]
fn conditional_op_false() {
let scenario = r#"false ? 100 : 50"#;

assert_eq!(&exec(scenario), "50");
}

#[test]
fn conditional_op_truthy() {
let scenario = r#""this text should be irrelevant" ? 100 : 50"#;

assert_eq!(&exec(scenario), "100");
}

#[test]
fn conditional_op_falsy() {
let scenario = r#"null ? 100 : 50"#;

assert_eq!(&exec(scenario), "50");
}

#[test]
fn conditional_op_throws() {
let scenario = r#"(function() { throw new Error("Oh no!") })() ? 100 : 50"#;

assert_eq!(&exec(scenario), r#"Error: "Error": "Oh no!""#);
}
30 changes: 30 additions & 0 deletions boa/src/exec/iteration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ impl Executable for ForLoop {
interpreter.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.

// Loops 'consume' continues.
interpreter.set_current_state(InterpreterState::Executing);

// Allow final_expr to execute.
}
InterpreterState::Return => {
return Ok(result);
}
Expand Down Expand Up @@ -76,6 +84,13 @@ impl Executable for WhileLoop {
interpreter.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.

// Loops 'consume' continues.
interpreter.set_current_state(InterpreterState::Executing);
continue;
}
InterpreterState::Return => {
return Ok(result);
}
Expand All @@ -99,6 +114,14 @@ impl Executable for DoWhileLoop {
interpreter.set_current_state(InterpreterState::Executing);
return Ok(result);
}
InterpreterState::Continue(_label) => {
// TODO continue to label.

// Loops 'consume' continues.
interpreter.set_current_state(InterpreterState::Executing);

// Continue execution.
}
InterpreterState::Return => {
return Ok(result);
}
Expand All @@ -117,6 +140,13 @@ impl Executable for DoWhileLoop {
interpreter.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.

// Loops 'consume' continues.
interpreter.set_current_state(InterpreterState::Executing);
continue;
}
InterpreterState::Return => {
return Ok(result);
}
Expand Down
55 changes: 54 additions & 1 deletion boa/src/exec/iteration/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ fn while_loop_early_break() {
assert_eq!(&exec(scenario), "3");
}

#[test]
fn while_loop_continue() {
let scenario = r#"
let a = 1;
let b = 1;
while (a < 5) {
a++;
if (a >= 3) {
continue;
}
b++;
}
[a, b];
"#;

assert_eq!(&exec(scenario), "[ 5, 2 ]");
}

#[test]
fn for_loop_break() {
let scenario = r#"
Expand All @@ -59,13 +77,30 @@ fn for_loop_return() {
}
}
}

foo();
"#;

assert_eq!(&exec(scenario), "3");
}

#[test]
fn for_loop_continue() {
let scenario = r#"
let a = 1;
let b = 1;
for (; a < 5; a++) {
if (a >= 3) {
continue;
}
b++;
}
[a, b];
"#;

assert_eq!(&exec(scenario), "[ 5, 3 ]");
}

#[test]
fn do_loop_late_break() {
// Ordering with statement before the break.
Expand Down Expand Up @@ -99,3 +134,21 @@ fn do_loop_early_break() {

assert_eq!(&exec(scenario), "3");
}

#[test]
fn do_loop_continue() {
let scenario = r#"
let a = 1;
let b = 1;
do {
a++;
if (a >= 3) {
continue;
}
b++;
} while (a < 5);
[a, b];
"#;

assert_eq!(&exec(scenario), "[ 5, 2 ]");
}
5 changes: 3 additions & 2 deletions boa/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub(crate) enum InterpreterState {
Executing,
Return,
Break(Option<String>),
Continue(Option<String>),
}

/// A Javascript intepreter
Expand Down Expand Up @@ -424,8 +425,8 @@ impl Executable for Node {
}
Node::Try(ref try_node) => try_node.run(interpreter),
Node::Break(ref break_node) => break_node.run(interpreter),
Node::ConditionalOp(_) => unimplemented!("ConditionalOp"),
Node::Continue(_) => unimplemented!("Continue"),
Node::ConditionalOp(ref conditional_op_node) => conditional_op_node.run(interpreter),
Node::Continue(ref continue_node) => continue_node.run(interpreter),
}
}
}
7 changes: 7 additions & 0 deletions boa/src/exec/statement_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ impl Executable for StatementList {
// Early break.
break;
}
InterpreterState::Continue(_label) => {
// TODO, continue to a label.

// Continue actually breaks out of the block, because the containing loop will handle the control
// flow.
break;
}
_ => {
// Continue execution
}
Expand Down
6 changes: 6 additions & 0 deletions boa/src/exec/switch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ impl Executable for Switch {
interpreter.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.

// Relinquish control to containing loop.
break;
}
_ => {
// Continuing execution / falling through to next case statement(s).
fall_through = true;
Expand Down
Loading