Skip to content

Commit ec7cbd1

Browse files
authored
Add nullish coalescing
1 parent b4a6e52 commit ec7cbd1

File tree

7 files changed

+570
-4
lines changed

7 files changed

+570
-4
lines changed

acorn-loose/src/expression.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ lp.parseExprOp = function(left, start, minPrec, noIn, indent, line) {
102102
let rightStart = this.storeCurrentPos()
103103
node.right = this.parseExprOp(this.parseMaybeUnary(false), rightStart, prec, noIn, indent, line)
104104
}
105-
this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression")
105+
this.finishNode(node, /&&|\|\||\?\?/.test(node.operator) ? "LogicalExpression" : "BinaryExpression")
106106
return this.parseExprOp(node, start, minPrec, noIn, indent, line)
107107
}
108108
}

acorn/src/expression.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,20 @@ pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
186186
if (prec != null && (!noIn || this.type !== tt._in)) {
187187
if (prec > minPrec) {
188188
let logical = this.type === tt.logicalOR || this.type === tt.logicalAND
189+
let coalesce = this.type === tt.coalesce
190+
if (coalesce) {
191+
// Handle the precedence of `tt.coalesce` as equal to the range of logical expressions.
192+
// In other words, `node.right` shouldn't contain logical expressions in order to check the mixed error.
193+
prec = tt.logicalAND.binop
194+
}
189195
let op = this.value
190196
this.next()
191197
let startPos = this.start, startLoc = this.startLoc
192198
let right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn)
193-
let node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical)
199+
let node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce)
200+
if ((logical && this.type === tt.coalesce) || (coalesce && (this.type === tt.logicalOR || this.type === tt.logicalAND))) {
201+
this.raiseRecoverable(this.start, "Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses")
202+
}
194203
return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
195204
}
196205
}

acorn/src/tokenize.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,14 @@ pp.readToken_eq_excl = function(code) { // '=!'
289289
return this.finishOp(code === 61 ? tt.eq : tt.prefix, 1)
290290
}
291291

292+
pp.readToken_question = function() { // '?'
293+
if (this.options.ecmaVersion >= 11) {
294+
let next = this.input.charCodeAt(this.pos + 1)
295+
if (next === 63) return this.finishOp(tt.coalesce, 2)
296+
}
297+
return this.finishOp(tt.question, 1)
298+
}
299+
292300
pp.getTokenFromCode = function(code) {
293301
switch (code) {
294302
// The interpretation of a dot depends on whether it is followed
@@ -306,7 +314,6 @@ pp.getTokenFromCode = function(code) {
306314
case 123: ++this.pos; return this.finishToken(tt.braceL)
307315
case 125: ++this.pos; return this.finishToken(tt.braceR)
308316
case 58: ++this.pos; return this.finishToken(tt.colon)
309-
case 63: ++this.pos; return this.finishToken(tt.question)
310317

311318
case 96: // '`'
312319
if (this.options.ecmaVersion < 6) break
@@ -356,6 +363,9 @@ pp.getTokenFromCode = function(code) {
356363
case 61: case 33: // '=!'
357364
return this.readToken_eq_excl(code)
358365

366+
case 63: // '?'
367+
return this.readToken_question()
368+
359369
case 126: // '~'
360370
return this.finishOp(tt.prefix, 1)
361371
}

acorn/src/tokentype.js

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export const types = {
108108
star: binop("*", 10),
109109
slash: binop("/", 10),
110110
starstar: new TokenType("**", {beforeExpr: true}),
111+
coalesce: binop("??", 1),
111112

112113
// Keyword token types.
113114
_break: kw("break"),

bin/run_test262.js

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const unsupportedFeatures = [
1010
"class-static-fields-private",
1111
"class-static-fields-public",
1212
"class-static-methods-private",
13-
"coalesce-expression",
1413
"numeric-separator-literal",
1514
"optional-chaining",
1615
"top-level-await"

test/run.js

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
require("./tests-dynamic-import.js");
2020
require("./tests-export-all-as-ns-from-source.js");
2121
require("./tests-import-meta.js");
22+
require("./tests-nullish-coalescing.js");
2223
var acorn = require("../acorn")
2324
var acorn_loose = require("../acorn-loose")
2425

0 commit comments

Comments
 (0)