Browse Source

Implement logical assignment operators (&&= and ||=) (#1018)

* Implement logical assignment operators (&&= and ||=)

* Add unit tests

Co-authored-by: tofpie <tofpie@users.noreply.github.com>
pull/1023/head
tofpie 4 years ago committed by GitHub
parent
commit
0331f486a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      boa/src/syntax/ast/node/operator/bin_op/mod.rs
  2. 34
      boa/src/syntax/ast/node/operator/tests.rs
  3. 26
      boa/src/syntax/ast/op.rs
  4. 8
      boa/src/syntax/ast/punctuator.rs
  5. 4
      boa/src/syntax/lexer/operator.rs
  6. 4
      boa/src/syntax/lexer/tests.rs

14
boa/src/syntax/ast/node/operator/bin_op/mod.rs

@ -71,6 +71,20 @@ impl BinOp {
AssignOp::Shl => x.shl(&y.run(context)?, context), AssignOp::Shl => x.shl(&y.run(context)?, context),
AssignOp::Shr => x.shr(&y.run(context)?, context), AssignOp::Shr => x.shr(&y.run(context)?, context),
AssignOp::Ushr => x.ushr(&y.run(context)?, context), AssignOp::Ushr => x.ushr(&y.run(context)?, context),
AssignOp::BoolAnd => {
if x.to_boolean() {
Ok(y.run(context)?)
} else {
Ok(x)
}
}
AssignOp::BoolOr => {
if x.to_boolean() {
Ok(x)
} else {
Ok(y.run(context)?)
}
}
AssignOp::Coalesce => { AssignOp::Coalesce => {
if x.is_null_or_undefined() { if x.is_null_or_undefined() {
Ok(y.run(context)?) Ok(y.run(context)?)

34
boa/src/syntax/ast/node/operator/tests.rs

@ -79,3 +79,37 @@ fn logical_nullish_assignment() {
assert_eq!(&exec(scenario), "20"); assert_eq!(&exec(scenario), "20");
} }
#[test]
fn logical_assignment() {
let scenario = r#"
let a = false;
a &&= 10;
a;
"#;
assert_eq!(&exec(scenario), "false");
let scenario = r#"
let a = 20;
a &&= 10;
a;
"#;
assert_eq!(&exec(scenario), "10");
let scenario = r#"
let a = null;
a ||= 10;
a;
"#;
assert_eq!(&exec(scenario), "10");
let scenario = r#"
let a = 20;
a ||= 10;
a;
"#;
assert_eq!(&exec(scenario), "20");
}

26
boa/src/syntax/ast/op.rs

@ -965,6 +965,30 @@ pub enum AssignOp {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift_assignment /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift_assignment
Ushr, Ushr,
/// The logical and assignment operator only assigns if the target variable is truthy.
///
/// Syntax: `x &&= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND_assignment
BoolAnd,
/// The logical or assignment operator only assigns if the target variable is falsy.
///
/// Syntax: `x ||= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment
BoolOr,
/// The logical nullish assignment operator only assigns if the target variable is nullish (null or undefined). /// The logical nullish assignment operator only assigns if the target variable is nullish (null or undefined).
/// ///
/// Syntax: `x ??= y` /// Syntax: `x ??= y`
@ -1000,6 +1024,8 @@ impl Display for AssignOp {
Self::Shl => "<<=", Self::Shl => "<<=",
Self::Shr => ">>=", Self::Shr => ">>=",
Self::Ushr => ">>>=", Self::Ushr => ">>>=",
Self::BoolAnd => "&&=",
Self::BoolOr => "||=",
Self::Coalesce => "??=", Self::Coalesce => "??=",
} }
) )

8
boa/src/syntax/ast/punctuator.rs

@ -35,6 +35,10 @@ pub enum Punctuator {
AssignAdd, AssignAdd,
/// `&=` /// `&=`
AssignAnd, AssignAnd,
/// `&&=`
AssignBoolAnd,
/// `||=`
AssignBoolOr,
/// `??=`, /// `??=`,
AssignCoalesce, AssignCoalesce,
/// `/=` /// `/=`
@ -141,6 +145,8 @@ impl Punctuator {
match self { match self {
Self::AssignAdd => Some(BinOp::Assign(AssignOp::Add)), Self::AssignAdd => Some(BinOp::Assign(AssignOp::Add)),
Self::AssignAnd => Some(BinOp::Assign(AssignOp::And)), Self::AssignAnd => Some(BinOp::Assign(AssignOp::And)),
Self::AssignBoolAnd => Some(BinOp::Assign(AssignOp::BoolAnd)),
Self::AssignBoolOr => Some(BinOp::Assign(AssignOp::BoolOr)),
Self::AssignCoalesce => Some(BinOp::Assign(AssignOp::Coalesce)), Self::AssignCoalesce => Some(BinOp::Assign(AssignOp::Coalesce)),
Self::AssignDiv => Some(BinOp::Assign(AssignOp::Div)), Self::AssignDiv => Some(BinOp::Assign(AssignOp::Div)),
Self::AssignLeftSh => Some(BinOp::Assign(AssignOp::Shl)), Self::AssignLeftSh => Some(BinOp::Assign(AssignOp::Shl)),
@ -200,6 +206,8 @@ impl Display for Punctuator {
Self::Assign => "=", Self::Assign => "=",
Self::AssignAdd => "+=", Self::AssignAdd => "+=",
Self::AssignAnd => "&=", Self::AssignAnd => "&=",
Self::AssignBoolAnd => "&&=",
Self::AssignBoolOr => "||=",
Self::AssignCoalesce => "??=", Self::AssignCoalesce => "??=",
Self::AssignDiv => "/=", Self::AssignDiv => "/=",
Self::AssignLeftSh => "<<=", Self::AssignLeftSh => "<<=",

4
boa/src/syntax/lexer/operator.rs

@ -119,10 +119,10 @@ impl<R> Tokenizer<R> for Operator {
Ok(Punctuator::Mod) Ok(Punctuator::Mod)
), ),
b'|' => op!(cursor, start_pos, Ok(Punctuator::AssignOr), Ok(Punctuator::Or), { b'|' => op!(cursor, start_pos, Ok(Punctuator::AssignOr), Ok(Punctuator::Or), {
Some(b'|') => Ok(Punctuator::BoolOr) Some(b'|') => vop!(cursor, Ok(Punctuator::AssignBoolOr), Ok(Punctuator::BoolOr))
}), }),
b'&' => op!(cursor, start_pos, Ok(Punctuator::AssignAnd), Ok(Punctuator::And), { b'&' => op!(cursor, start_pos, Ok(Punctuator::AssignAnd), Ok(Punctuator::And), {
Some(b'&') => Ok(Punctuator::BoolAnd) Some(b'&') => vop!(cursor, Ok(Punctuator::AssignBoolAnd), Ok(Punctuator::BoolAnd))
}), }),
b'?' => match cursor.peek()? { b'?' => match cursor.peek()? {
Some(b'?') => { Some(b'?') => {

4
boa/src/syntax/lexer/tests.rs

@ -109,7 +109,7 @@ fn check_punctuators() {
// https://tc39.es/ecma262/#sec-punctuators // https://tc39.es/ecma262/#sec-punctuators
let s = "{ ( ) [ ] . ... ; , < > <= >= == != === !== \ let s = "{ ( ) [ ] . ... ; , < > <= >= == != === !== \
+ - * % -- << >> >>> & | ^ ! ~ && || ? : \ + - * % -- << >> >>> & | ^ ! ~ && || ? : \
= += -= *= &= **= ++ ** <<= >>= >>>= &= |= ^= => ?? ??="; = += -= *= &= **= ++ ** <<= >>= >>>= &= |= ^= => ?? ??= &&= ||=";
let mut lexer = Lexer::new(s.as_bytes()); let mut lexer = Lexer::new(s.as_bytes());
let expected = [ let expected = [
@ -164,6 +164,8 @@ fn check_punctuators() {
TokenKind::Punctuator(Punctuator::Arrow), TokenKind::Punctuator(Punctuator::Arrow),
TokenKind::Punctuator(Punctuator::Coalesce), TokenKind::Punctuator(Punctuator::Coalesce),
TokenKind::Punctuator(Punctuator::AssignCoalesce), TokenKind::Punctuator(Punctuator::AssignCoalesce),
TokenKind::Punctuator(Punctuator::AssignBoolAnd),
TokenKind::Punctuator(Punctuator::AssignBoolOr),
]; ];
expect_tokens(&mut lexer, &expected); expect_tokens(&mut lexer, &expected);

Loading…
Cancel
Save