Skip to content

Commit

Permalink
Implemented automatic semicolon insertion after do-while statements
Browse files Browse the repository at this point in the history
  • Loading branch information
Razican committed Apr 16, 2020
1 parent 8116c5b commit 05d3467
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 14 deletions.
2 changes: 1 addition & 1 deletion boa/benches/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn symbol_creation(c: &mut Criterion) {
}

fn create_realm(c: &mut Criterion) {
c.bench_function("Create Realm", move |b| b.iter(|| Realm::create()));
c.bench_function("Create Realm", move |b| b.iter(Realm::create));
}

criterion_group!(benches, create_realm, symbol_creation);
Expand Down
2 changes: 1 addition & 1 deletion boa/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ impl Executor for Interpreter {
!(num_v_a as i32)
})
}
_ => unreachable!(),
_ => unimplemented!(),
})
}
Node::BinOp(BinOp::Bit(ref op), ref a, ref b) => {
Expand Down
58 changes: 58 additions & 0 deletions boa/src/exec/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,61 @@ fn test_do_while_loop() {
"#;
assert_eq!(exec(body_is_executed_at_least_once), String::from("1"));
}

#[test]
#[ignore]
fn test_do_while_post_inc() {
let with_post_incrementors = r#"
var i = 0;
do {} while(i++ < 10) i;
"#;
assert_eq!(exec(with_post_incrementors), String::from("11"));
}

#[test]
#[ignore]
fn test_unary_pre() {
let unary_inc = r#"
let a = 5;
++a;
a;
"#;
assert_eq!(exec(unary_inc), String::from("6"));

let unary_dec = r#"
let a = 5;
--a;
a;
"#;
assert_eq!(exec(unary_dec), String::from("6"));

let execs_before = r#"
let a = 5;
++a === 6;
"#;
assert_eq!(exec(execs_before), String::from("true"));
}

#[test]
#[ignore]
fn test_unary_post() {
let unary_inc = r#"
let a = 5;
a++;
a;
"#;
assert_eq!(exec(unary_inc), String::from("6"));

let unary_dec = r#"
let a = 5;
a--;
a;
"#;
assert_eq!(exec(unary_dec), String::from("6"));

let execs_after = r#"
let a = 5;
a++ === 5;
"#;
assert_eq!(exec(execs_after), String::from("true"));
}
35 changes: 27 additions & 8 deletions boa/src/syntax/parser/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,21 +151,40 @@ impl<'a> Cursor<'a> {
/// It will automatically insert a semicolon if needed, as specified in the [spec][spec].
///
/// [spec]: https://tc39.es/ecma262/#sec-automatic-semicolon-insertion
pub(super) fn expect_semicolon(&mut self, routine: &'static str) -> Result<(), ParseError> {
pub(super) fn expect_semicolon(
&mut self,
do_while: bool,
routine: &'static str,
) -> Result<(), ParseError> {
match self.peek(0) {
Some(tk) => match tk.kind {
TokenKind::Punctuator(Punctuator::Semicolon) => {
let _ = self.next();
Ok(())
}
TokenKind::LineTerminator | TokenKind::Punctuator(Punctuator::CloseBlock) => Ok(()),
// TODO: The previous token is ) and the inserted semicolon would then be parsed as
// the terminating semicolon of a do-while statement (13.7.2).
_ => Err(ParseError::Expected(
vec![TokenKind::Punctuator(Punctuator::Semicolon)],
tk.clone(),
routine,
)),
_ => {
if do_while {
debug_assert!(
self.pos != 0,
"cannot be finishing a do-while if we are at the beginning"
);

let tok = self
.tokens
.get(self.pos - 1)
.expect("could not find previous token");
if tok.kind == TokenKind::Punctuator(Punctuator::CloseParen) {
return Ok(());
}
}

Err(ParseError::Expected(
vec![TokenKind::Punctuator(Punctuator::Semicolon)],
tk.clone(),
routine,
))
}
},
None => Ok(()),
}
Expand Down
6 changes: 3 additions & 3 deletions boa/src/syntax/parser/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ impl TokenParser for VariableStatement {

let decl_list = VariableDeclarationList::parse(cursor)?;

cursor.expect_semicolon("variable statement")?;
cursor.expect_semicolon(false, "variable statement")?;

Ok(decl_list)
}
Expand Down Expand Up @@ -474,7 +474,7 @@ impl TokenParser for ReturnStatement {

let expr = Expression::parse(cursor)?;

cursor.expect_semicolon("return statement")?;
cursor.expect_semicolon(false, "return statement")?;

Ok(Node::return_node(expr))
}
Expand Down Expand Up @@ -787,7 +787,7 @@ impl TokenParser for DoWhileStatement {
let cond = Expression::parse(cursor)?;

cursor.expect_punc(Punctuator::CloseParen, "do while statement")?;
cursor.expect_semicolon("do while statement")?;
cursor.expect_semicolon(true, "do while statement")?;

Ok(Node::do_while_loop(body, cond))
}
Expand Down
26 changes: 25 additions & 1 deletion boa/src/syntax/parser/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use super::*;
use crate::syntax::{
ast::node::{FormalParameter, MethodDefinitionKind, Node, PropertyDefinition},
ast::op::{AssignOp, BinOp, BitOp, NumOp},
ast::op::{AssignOp, BinOp, BitOp, CompOp, NumOp, UnaryOp},
lexer::Lexer,
};

Expand Down Expand Up @@ -673,6 +673,30 @@ fn check_do_while() {
Node::const_node(true),
)],
);

// Check semicolon insertion after do-while
check_parser(
r#"var i = 0;
do {console.log("hello");} while(i++ < 10) console.log("end");"#,
&[
Node::VarDecl(vec![(String::from("i"), Some(Node::const_node(0.0)))]),
Node::do_while_loop(
Node::Block(vec![Node::call(
Node::get_const_field(Node::local("console"), "log"),
vec![Node::const_node("hello")],
)]),
Node::bin_op(
BinOp::Comp(CompOp::LessThan),
Node::unary_op(UnaryOp::IncrementPost, Node::local("i")),
Node::const_node(10.0),
),
),
Node::call(
Node::get_const_field(Node::local("console"), "log"),
vec![Node::const_node("end")],
),
],
);
}

#[test]
Expand Down

0 comments on commit 05d3467

Please sign in to comment.