Browse Source

Implement do..while loop (#306)

Co-authored-by: HalidOdat <halidodat@gmail.com>
Co-authored-by: Iban Eguia <razican@protonmail.ch>
Co-authored-by: Iban Eguia <razican@protonmail.ch>
pull/318/head
Radek Krahl 4 years ago committed by GitHub
parent
commit
9c638cc14f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      boa/src/exec/mod.rs
  2. 35
      boa/src/exec/tests.rs
  3. 7
      boa/src/syntax/ast/node.rs
  4. 28
      boa/src/syntax/parser/mod.rs
  5. 17
      boa/src/syntax/parser/tests.rs

7
boa/src/exec/mod.rs

@ -165,6 +165,13 @@ impl Executor for Interpreter {
}
Ok(result)
}
Node::DoWhileLoop(ref body, ref cond) => {
let mut result = self.run(body)?;
while self.run(cond)?.borrow().is_true() {
result = self.run(body)?;
}
Ok(result)
}
Node::If(ref cond, ref expr, None) => Ok(if self.run(cond)?.borrow().is_true() {
self.run(expr)?
} else {

35
boa/src/exec/tests.rs

@ -247,3 +247,38 @@ fn test_short_circuit_evaluation() {
"#;
assert_eq!(exec(short_circuit_eval), String::from("1"));
}
#[test]
fn test_do_while_loop() {
let simple_one = r#"
a = 0;
do {
a += 1;
} while (a < 10);
a
"#;
assert_eq!(exec(simple_one), String::from("10"));
let multiline_statement = r#"
pow = 0;
b = 1;
do {
pow += 1;
b *= 2;
} while (pow < 8);
b
"#;
assert_eq!(exec(multiline_statement), String::from("256"));
let body_is_executed_at_least_once = r#"
a = 0;
do
{
a += 1;
}
while (false);
a
"#;
assert_eq!(exec(body_is_executed_at_least_once), String::from("1"));
}

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

@ -34,6 +34,8 @@ pub enum Node {
ConstDecl(Vec<(String, Node)>),
/// Continue with an optional label.
Continue(Option<String>),
/// do [body] while [cond]
DoWhileLoop(Box<Node>, Box<Node>),
/// Create a function with the given name, arguments, and internal AST node.
FunctionDecl(Option<String>, Vec<FormalParameter>, Box<Node>),
/// Gets the constant field of a value.
@ -209,6 +211,11 @@ impl Node {
write!(f, "while ({}) ", cond)?;
node.display(f, indentation)
}
Self::DoWhileLoop(ref node, ref cond) => {
write!(f, "do")?;
node.display(f, indentation)?;
write!(f, "while ({})", cond)
}
Self::If(ref cond, ref node, None) => {
write!(f, "if ({}) ", cond)?;
node.display(f, indentation)

28
boa/src/syntax/parser/mod.rs

@ -444,6 +444,7 @@ impl<'a> Parser<'a> {
TokenKind::Keyword(Keyword::If) => self.read_if_statement(),
TokenKind::Keyword(Keyword::Var) => self.read_variable_statement(),
TokenKind::Keyword(Keyword::While) => self.read_while_statement(),
TokenKind::Keyword(Keyword::Do) => self.read_do_while_statement(),
TokenKind::Keyword(Keyword::For) => self.read_for_statement(),
TokenKind::Keyword(Keyword::Return) => self.read_return_statement(),
TokenKind::Keyword(Keyword::Break) => self.read_break_statement(),
@ -633,6 +634,33 @@ impl<'a> Parser<'a> {
Ok(Node::WhileLoop(Box::new(cond), Box::new(body)))
}
/// https://tc39.es/ecma262/#sec-do-while-statement
fn read_do_while_statement(&mut self) -> ParseResult {
let body = self.read_statement()?;
let next_token = self
.peek_skip_lineterminator()
.ok_or(ParseError::AbruptEnd)?;
if next_token.kind != TokenKind::Keyword(Keyword::While) {
return Err(ParseError::Expected(
vec![TokenKind::Keyword(Keyword::While)],
next_token.clone(),
Some("do while statement"),
));
}
let _ = self.next_skip_lineterminator(); // skip while token
self.expect_punc(Punctuator::OpenParen, Some("do while statement"))?;
let cond = self.read_expression()?;
self.expect_punc(Punctuator::CloseParen, Some("do while statement"))?;
Ok(Node::DoWhileLoop(Box::new(body), Box::new(cond)))
}
/// <https://tc39.es/ecma262/#sec-try-statement>
fn read_try_statement(&mut self) -> ParseResult {
// TRY

17
boa/src/syntax/parser/tests.rs

@ -711,7 +711,24 @@ fn check_function_declarations() {
}
#[test]
fn check_do_while() {
check_parser(
r#"do {
a += 1;
} while (true)"#,
&[Node::DoWhileLoop(
Box::new(Node::Block(vec![create_bin_op(
BinOp::Assign(AssignOp::Add),
Node::Local(String::from("a")),
Node::Const(Const::Num(1.0)),
)])),
Box::new(Node::Const(Const::Bool(true))),
)],
);
}
/// Should be parsed as `new Class().method()` instead of `new (Class().method())`
#[test]
fn check_construct_call_precedence() {
check_parser(
"new Date().getTime()",

Loading…
Cancel
Save