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::Shr => x.shr(&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 => {
if x.is_null_or_undefined() {
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");
}
#[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
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).
///
/// Syntax: `x ??= y`
@ -1000,6 +1024,8 @@ impl Display for AssignOp {
Self::Shl => "<<=",
Self::Shr => ">>=",
Self::Ushr => ">>>=",
Self::BoolAnd => "&&=",
Self::BoolOr => "||=",
Self::Coalesce => "??=",
}
)

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

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

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

@ -119,10 +119,10 @@ impl<R> Tokenizer<R> for Operator {
Ok(Punctuator::Mod)
),
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), {
Some(b'&') => Ok(Punctuator::BoolAnd)
Some(b'&') => vop!(cursor, Ok(Punctuator::AssignBoolAnd), Ok(Punctuator::BoolAnd))
}),
b'?' => match cursor.peek()? {
Some(b'?') => {

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

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

Loading…
Cancel
Save