Skip to content

Commit

Permalink
Fixed assignment expression parsing (#2268)
Browse files Browse the repository at this point in the history
This Pull Request fixes/closes #2148.

It changes the following:

- When we start an assignment expression, the `/` token must be a regular expression literal. The division can only occur between expressions after the assignment operator.
- Added tests for the new behaviour, taken from #2177

This overrides #2177
  • Loading branch information
Razican committed Sep 16, 2022
1 parent f4d88b7 commit af35be6
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 4 deletions.
19 changes: 19 additions & 0 deletions boa_engine/src/syntax/lexer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,25 @@ fn regex_literal() {
expect_tokens(&mut lexer, &expected, &mut interner);
}

#[test]
fn regex_equals_following_assignment() {
let mut lexer = Lexer::new(&b"const myRegex = /=/;"[..]);
let mut interner = Interner::default();

let expected = [
TokenKind::Keyword((Keyword::Const, false)),
TokenKind::identifier(interner.get_or_intern_static("myRegex")),
TokenKind::Punctuator(Punctuator::Assign),
TokenKind::regular_expression_literal(
interner.get_or_intern_static("="),
Sym::EMPTY_STRING,
),
TokenKind::Punctuator(Punctuator::Semicolon),
];

expect_tokens(&mut lexer, &expected, &mut interner);
}

#[test]
fn regex_literal_flags() {
let mut lexer = Lexer::new(&br"/\/[^\/]*\/*/gmi"[..]);
Expand Down
6 changes: 5 additions & 1 deletion boa_engine/src/syntax/parser/expression/assignment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ where

fn parse(mut self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult {
let _timer = Profiler::global().start_event("AssignmentExpression", "Parsing");
cursor.set_goal(InputElement::Div);
cursor.set_goal(InputElement::RegExp);

match cursor
.peek(0, interner)?
Expand All @@ -104,6 +104,8 @@ where
}
// ArrowFunction[?In, ?Yield, ?Await] -> ArrowParameters[?Yield, ?Await] -> BindingIdentifier[?Yield, ?Await]
TokenKind::Identifier(_) | TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => {
cursor.set_goal(InputElement::Div);

// Because we already peeked the identifier token, there may be a line terminator before the identifier token.
// In that case we have to skip an additional token on the next peek.
let skip_n = if cursor
Expand Down Expand Up @@ -202,6 +204,8 @@ where
}

cursor.next(interner)?.expect("= token vanished");
cursor.set_goal(InputElement::RegExp);

if let Some(target) = AssignTarget::from_node(&lhs, cursor.strict_mode()) {
if let AssignTarget::Identifier(ident) = target {
self.name = Some(ident.sym());
Expand Down
26 changes: 23 additions & 3 deletions boa_engine/src/syntax/parser/expression/tests.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::syntax::{
ast::op::{AssignOp, BitOp, CompOp, LogOp, NumOp},
ast::{
node::{BinOp, Identifier},
Const,
node::{BinOp, Call, Declaration, DeclarationList, Identifier, New},
Const, Node,
},
parser::tests::{check_invalid, check_parser},
};
use boa_interner::Interner;
use boa_interner::{Interner, Sym};

/// Checks numeric operations
#[test]
Expand Down Expand Up @@ -83,6 +83,26 @@ fn check_numeric_operations() {
interner,
);

let mut interner = Interner::default();
check_parser(
"let myRegex = /=/;",
vec![DeclarationList::Let(
vec![Declaration::new_with_identifier(
interner.get_or_intern_static("myRegex"),
Node::from(New::from(Call::new(
Identifier::new(Sym::REGEXP),
vec![
Node::from(Const::from(interner.get_or_intern_static("="))),
Node::from(Const::from(Sym::EMPTY_STRING)),
],
))),
)]
.into(),
)
.into()],
interner,
);

let mut interner = Interner::default();
check_parser(
"a * b",
Expand Down

0 comments on commit af35be6

Please sign in to comment.