diff --git a/src/parser.ts b/src/parser.ts index fe21966ec..78369e027 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -516,7 +516,10 @@ export class Parser { op === '>>>=' || op === '&=' || op === '^=' || - op === '|='; + op === '|=' || + op === '&&=' || + op === '||=' || + op === '??='; } // Cover grammar support. diff --git a/src/scanner.ts b/src/scanner.ts index 2a746e029..59a392f52 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -613,7 +613,12 @@ export class Scanner { ++this.index; if (this.source[this.index] === '?') { ++this.index; - str = '??'; + if (this.source[this.index] === '=') { + ++this.index; + str = '??='; + } else { + str = '??'; + } } if (this.source[this.index] === '.' && !/^\d$/.test(this.source[this.index + 1])) { // "?." in "foo?.3:0" should not be treated as optional chaining. // See https://github.com/tc39/proposal-optional-chaining#notes @@ -642,13 +647,14 @@ export class Scanner { // 3-character punctuators. str = str.substr(0, 3); if (str === '===' || str === '!==' || str === '>>>' || - str === '<<=' || str === '>>=' || str === '**=') { + str === '<<=' || str === '>>=' || str === '**=' || + str === '&&=' || str === '||=') { this.index += 3; } else { // 2-character punctuators. str = str.substr(0, 2); - if (str === '&&' || str === '||' || str === '??' || + if (str === '&&' || str === '||' || str === '==' || str === '!=' || str === '+=' || str === '-=' || str === '*=' || str === '/=' || str === '++' || str === '--' || diff --git a/test/fixtures/es2021/logical-assignment/logical-and-assignment.js b/test/fixtures/es2021/logical-assignment/logical-and-assignment.js new file mode 100644 index 000000000..6d65d0624 --- /dev/null +++ b/test/fixtures/es2021/logical-assignment/logical-and-assignment.js @@ -0,0 +1 @@ +foo &&= true \ No newline at end of file diff --git a/test/fixtures/es2021/logical-assignment/logical-and-assignment.tree.json b/test/fixtures/es2021/logical-assignment/logical-and-assignment.tree.json new file mode 100644 index 000000000..183791eaa --- /dev/null +++ b/test/fixtures/es2021/logical-assignment/logical-and-assignment.tree.json @@ -0,0 +1,148 @@ +{ + "type": "Program", + "body": [ + { + "type": "ExpressionStatement", + "expression": { + "type": "AssignmentExpression", + "operator": "&&=", + "left": { + "type": "Identifier", + "name": "foo", + "range": [ + 0, + 3 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 3 + } + } + }, + "right": { + "type": "Literal", + "value": true, + "raw": "true", + "range": [ + 8, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + } + } + }, + "range": [ + 0, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + } + }, + "range": [ + 0, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + } + } + ], + "sourceType": "script", + "range": [ + 0, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "tokens": [ + { + "type": "Identifier", + "value": "foo", + "range": [ + 0, + 3 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 3 + } + } + }, + { + "type": "Punctuator", + "value": "&&=", + "range": [ + 4, + 7 + ], + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 7 + } + } + }, + { + "type": "Boolean", + "value": "true", + "range": [ + 8, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/es2021/logical-assignment/nullish-coalescing-assignment.js b/test/fixtures/es2021/logical-assignment/nullish-coalescing-assignment.js new file mode 100644 index 000000000..2698ecc30 --- /dev/null +++ b/test/fixtures/es2021/logical-assignment/nullish-coalescing-assignment.js @@ -0,0 +1 @@ +foo ??= true \ No newline at end of file diff --git a/test/fixtures/es2021/logical-assignment/nullish-coalescing-assignment.tree.json b/test/fixtures/es2021/logical-assignment/nullish-coalescing-assignment.tree.json new file mode 100644 index 000000000..b56db6ac9 --- /dev/null +++ b/test/fixtures/es2021/logical-assignment/nullish-coalescing-assignment.tree.json @@ -0,0 +1,148 @@ +{ + "type": "Program", + "body": [ + { + "type": "ExpressionStatement", + "expression": { + "type": "AssignmentExpression", + "operator": "??=", + "left": { + "type": "Identifier", + "name": "foo", + "range": [ + 0, + 3 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 3 + } + } + }, + "right": { + "type": "Literal", + "value": true, + "raw": "true", + "range": [ + 8, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + } + } + }, + "range": [ + 0, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + } + }, + "range": [ + 0, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + } + } + ], + "sourceType": "script", + "range": [ + 0, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "tokens": [ + { + "type": "Identifier", + "value": "foo", + "range": [ + 0, + 3 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 3 + } + } + }, + { + "type": "Punctuator", + "value": "??=", + "range": [ + 4, + 7 + ], + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 7 + } + } + }, + { + "type": "Boolean", + "value": "true", + "range": [ + 8, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + } + } + } + ] +} \ No newline at end of file diff --git a/test/test-262-whitelist.txt b/test/test-262-whitelist.txt index d1bb4ad5e..2bc45046f 100644 --- a/test/test-262-whitelist.txt +++ b/test/test-262-whitelist.txt @@ -15310,20 +15310,12 @@ test/language/expressions/async-generator/yield-thenable-create-resolving-functi test/language/expressions/async-generator/yield-thenable-create-resolving-functions-resolve.js(strict mode) test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-assignment-expression-coalesce.js(default) test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-assignment-expression-coalesce.js(strict mode) -test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-assignment-expression-logical-and.js(default) -test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-assignment-expression-logical-and.js(strict mode) -test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-assignment-expression-logical-or.js(default) -test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-assignment-expression-logical-or.js(strict mode) test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-await-expression.js(default) test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-await-expression.js(strict mode) test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-integer-separators.js(default) test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-integer-separators.js(strict mode) test/language/expressions/class/cpn-class-expr-computed-property-name-from-assignment-expression-coalesce.js(default) test/language/expressions/class/cpn-class-expr-computed-property-name-from-assignment-expression-coalesce.js(strict mode) -test/language/expressions/class/cpn-class-expr-computed-property-name-from-assignment-expression-logical-and.js(default) -test/language/expressions/class/cpn-class-expr-computed-property-name-from-assignment-expression-logical-and.js(strict mode) -test/language/expressions/class/cpn-class-expr-computed-property-name-from-assignment-expression-logical-or.js(default) -test/language/expressions/class/cpn-class-expr-computed-property-name-from-assignment-expression-logical-or.js(strict mode) test/language/expressions/class/cpn-class-expr-computed-property-name-from-await-expression.js(default) test/language/expressions/class/cpn-class-expr-computed-property-name-from-await-expression.js(strict mode) test/language/expressions/class/cpn-class-expr-computed-property-name-from-integer-separators.js(default) @@ -15534,28 +15526,6 @@ test/language/expressions/class/private-static-setter-multiple-evaluations-of-cl test/language/expressions/class/private-static-setter-multiple-evaluations-of-class-factory.js(strict mode) test/language/expressions/logical-assignment/lgcl-and-assignment-operator-bigint.js(default) test/language/expressions/logical-assignment/lgcl-and-assignment-operator-bigint.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-lhs-before-rhs.js(default) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-lhs-before-rhs.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-arrow-function.js(default) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-arrow-function.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-class-expression.js(default) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-class-expression.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-function.js(default) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-function.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set-put.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-extensible.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable-put.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-lhs.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs-put.js(default) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs-put.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs.js(default) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator.js(default) -test/language/expressions/logical-assignment/lgcl-and-assignment-operator.js(strict mode) -test/language/expressions/logical-assignment/lgcl-and-whitespace.js(default) -test/language/expressions/logical-assignment/lgcl-and-whitespace.js(strict mode) test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-bigint.js(default) test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-bigint.js(strict mode) test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-lhs-before-rhs.js(default) @@ -15582,28 +15552,6 @@ test/language/expressions/logical-assignment/lgcl-nullish-whitespace.js(default) test/language/expressions/logical-assignment/lgcl-nullish-whitespace.js(strict mode) test/language/expressions/logical-assignment/lgcl-or-assignment-operator-bigint.js(default) test/language/expressions/logical-assignment/lgcl-or-assignment-operator-bigint.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-lhs-before-rhs.js(default) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-lhs-before-rhs.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-arrow-function.js(default) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-arrow-function.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-class-expression.js(default) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-class-expression.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-function.js(default) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-function.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set-put.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-extensible.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable-put.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-lhs.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs-put.js(default) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs-put.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs.js(default) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator.js(default) -test/language/expressions/logical-assignment/lgcl-or-assignment-operator.js(strict mode) -test/language/expressions/logical-assignment/lgcl-or-whitespace.js(default) -test/language/expressions/logical-assignment/lgcl-or-whitespace.js(strict mode) test/language/expressions/object/__proto__-permitted-dup.js(default) test/language/expressions/object/__proto__-permitted-dup.js(strict mode) test/language/expressions/object/covered-ident-name-prop-name-literal-break-escaped.js(default) @@ -15680,10 +15628,6 @@ test/language/expressions/object/covered-ident-name-prop-name-literal-with-escap test/language/expressions/object/covered-ident-name-prop-name-literal-with-escaped.js(strict mode) test/language/expressions/object/cpn-obj-lit-computed-property-name-from-assignment-expression-coalesce.js(default) test/language/expressions/object/cpn-obj-lit-computed-property-name-from-assignment-expression-coalesce.js(strict mode) -test/language/expressions/object/cpn-obj-lit-computed-property-name-from-assignment-expression-logical-and.js(default) -test/language/expressions/object/cpn-obj-lit-computed-property-name-from-assignment-expression-logical-and.js(strict mode) -test/language/expressions/object/cpn-obj-lit-computed-property-name-from-assignment-expression-logical-or.js(default) -test/language/expressions/object/cpn-obj-lit-computed-property-name-from-assignment-expression-logical-or.js(strict mode) test/language/expressions/object/cpn-obj-lit-computed-property-name-from-await-expression.js(default) test/language/expressions/object/cpn-obj-lit-computed-property-name-from-await-expression.js(strict mode) test/language/expressions/object/cpn-obj-lit-computed-property-name-from-integer-separators.js(default) @@ -15885,20 +15829,12 @@ test/language/statements/async-generator/yield-star-return-missing-value-is-awai test/language/statements/async-generator/yield-star-return-missing-value-is-awaited.js(strict mode) test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-assignment-expression-coalesce.js(default) test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-assignment-expression-coalesce.js(strict mode) -test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-assignment-expression-logical-and.js(default) -test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-assignment-expression-logical-and.js(strict mode) -test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-assignment-expression-logical-or.js(default) -test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-assignment-expression-logical-or.js(strict mode) test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-await-expression.js(default) test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-await-expression.js(strict mode) test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-integer-separators.js(default) test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-integer-separators.js(strict mode) test/language/statements/class/cpn-class-decl-computed-property-name-from-assignment-expression-coalesce.js(default) test/language/statements/class/cpn-class-decl-computed-property-name-from-assignment-expression-coalesce.js(strict mode) -test/language/statements/class/cpn-class-decl-computed-property-name-from-assignment-expression-logical-and.js(default) -test/language/statements/class/cpn-class-decl-computed-property-name-from-assignment-expression-logical-and.js(strict mode) -test/language/statements/class/cpn-class-decl-computed-property-name-from-assignment-expression-logical-or.js(default) -test/language/statements/class/cpn-class-decl-computed-property-name-from-assignment-expression-logical-or.js(strict mode) test/language/statements/class/cpn-class-decl-computed-property-name-from-await-expression.js(default) test/language/statements/class/cpn-class-decl-computed-property-name-from-await-expression.js(strict mode) test/language/statements/class/cpn-class-decl-computed-property-name-from-integer-separators.js(default) @@ -16101,12 +16037,8 @@ test/language/statements/class/ident-name-method-def-with-escaped.js(default) test/language/statements/class/ident-name-method-def-with-escaped.js(strict mode) test/language/statements/switch/scope-lex-async-generator.js(default) test/language/statements/switch/scope-lex-async-generator.js(strict mode) -test/annexB/language/expressions/logical-assignment/emulates-undefined-and.js(default) -test/annexB/language/expressions/logical-assignment/emulates-undefined-and.js(strict mode) test/annexB/language/expressions/logical-assignment/emulates-undefined-coalesce.js(default) test/annexB/language/expressions/logical-assignment/emulates-undefined-coalesce.js(strict mode) -test/annexB/language/expressions/logical-assignment/emulates-undefined-or.js(default) -test/annexB/language/expressions/logical-assignment/emulates-undefined-or.js(strict mode) test/built-ins/Atomics/add/bigint/non-shared-bufferdata.js(default) test/built-ins/Atomics/add/bigint/non-shared-bufferdata.js(strict mode) test/built-ins/Atomics/and/bigint/non-shared-bufferdata.js(default)