From 926a86355205ebe7f180ec0faaeb14aadd2273b1 Mon Sep 17 00:00:00 2001 From: "William C. Johnson" Date: Sat, 23 Sep 2017 15:55:42 -0400 Subject: [PATCH] Converge with Babel re optional chaining MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Eliminate `SafeMemberExpression` node type; use `MemberExpression` with `optional = true` instead. - Use `optional = true` for safe `CallExpression`s - Allow proposed `x?.(arg, arg…)` syntax for safe calls --- src/parser/expression.js | 43 +++++--- src/plugins/safeCallExistential.js | 2 +- src/plugins/tildeCall.js | 2 +- .../bang-call/safe/basic/expected.json | 2 +- .../basic/expected.json | 5 +- .../chained/expected.json | 15 +-- .../computed/expected.json | 5 +- .../index/expected.json | 15 +-- .../safe-call-expression/js/basic/actual.js | 1 + .../js/basic/expected.json | 101 ++++++++++++++++++ .../lightscript/basic/expected.json | 4 +- .../lightscript/chained/expected.json | 8 +- .../lightscript/match-test/expected.json | 2 +- .../match-typecast-unfortunate/expected.json | 2 +- .../lightscript/ternary-3/expected.json | 4 +- .../expected.json | 4 +- .../lightscript/tilde/expected.json | 2 +- .../safe/basic/expected.json | 2 +- 18 files changed, 169 insertions(+), 50 deletions(-) create mode 100644 test/fixtures/safe-call-expression/js/basic/actual.js create mode 100644 test/fixtures/safe-call-expression/js/basic/expected.json diff --git a/src/parser/expression.js b/src/parser/expression.js index ee50937efa..f55e40c689 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -426,28 +426,39 @@ pp.parseSubscripts = function (base, startPos, startLoc, noCalls) { if (this.hasPlugin("enforceSubscriptIndentation") && this.isNonIndentedBreakFrom(startPos)) { this.unexpected(null, "Indentation required."); } - // `x?.y` - const node = this.startNodeAt(startPos, startLoc); + const op = this.state.value; this.next(); - node.object = base; - if (op === "?.") { - // arr?.0 -> arr?[0] - if (this.match(tt.num)) { - node.property = this.parseLiteral(this.state.value, "NumericLiteral"); + + if (op === "?." && this.match(tt.parenL) && this.hasPlugin("safeCallExpression")) { + // x?.(...) = JS safe call proposal + const [next, canSubscript] = this.parseSafeCall(base, startPos, startLoc); + if (canSubscript) base = next; else return base; + } else { + // `x?.y` `x?[y]` + const node = this.startNodeAt(startPos, startLoc); + node.object = base; + + if (op === "?.") { + if (this.match(tt.num)) { + // arr?.0 -> arr?[0] + node.property = this.parseLiteral(this.state.value, "NumericLiteral"); + node.computed = true; + } else { + node.property = this.parseIdentifierOrPlaceholder(true); + node.computed = false; + } + } else if (op === "?[") { + node.property = this.parseExpression(); node.computed = true; + this.expect(tt.bracketR); } else { - node.property = this.parseIdentifierOrPlaceholder(true); - node.computed = false; + this.unexpected(); } - } else if (op === "?[") { - node.property = this.parseExpression(); - node.computed = true; - this.expect(tt.bracketR); - } else { - this.unexpected(); + + node.optional = true; + base = this.finishNode(node, "MemberExpression"); } - base = this.finishNode(node, "SafeMemberExpression"); } else if ( (this.hasPlugin("safeCallExpression") || this.hasPlugin("existentialExpression")) && this.match(tt.question) && diff --git a/src/plugins/safeCallExistential.js b/src/plugins/safeCallExistential.js index eaa39ecd0d..8c4ed2a2f8 100644 --- a/src/plugins/safeCallExistential.js +++ b/src/plugins/safeCallExistential.js @@ -10,7 +10,7 @@ export default function(parser) { pp.parseSafeCall = function(expr, startPos, startLoc) { const node = this.startNodeAt(startPos, startLoc); node.callee = expr; - node.safe = true; + node.optional = true; if (this.hasPlugin("bangCall") && this.isBang()) { const canSubscript = this.parseBangCall(node, "CallExpression"); diff --git a/src/plugins/tildeCall.js b/src/plugins/tildeCall.js index 76a0c08556..17c169422b 100644 --- a/src/plugins/tildeCall.js +++ b/src/plugins/tildeCall.js @@ -18,7 +18,7 @@ export default function(parser) { // Allow safe tilde calls (a~b?(c)) if (this.hasPlugin("safeCallExpression") && this.eat(tt.question)) { - node.safe = true; + node.optional = true; } // Allow bang tilde calls diff --git a/test/fixtures/bang-call/safe/basic/expected.json b/test/fixtures/bang-call/safe/basic/expected.json index ac510aa773..c6125aec10 100644 --- a/test/fixtures/bang-call/safe/basic/expected.json +++ b/test/fixtures/bang-call/safe/basic/expected.json @@ -73,7 +73,7 @@ }, "name": "a" }, - "safe": true, + "optional": true, "arguments": [ { "type": "Identifier", diff --git a/test/fixtures/lightscript/safe-member-expression/basic/expected.json b/test/fixtures/lightscript/safe-member-expression/basic/expected.json index 062c5f5ff1..40f44870eb 100644 --- a/test/fixtures/lightscript/safe-member-expression/basic/expected.json +++ b/test/fixtures/lightscript/safe-member-expression/basic/expected.json @@ -43,7 +43,7 @@ } }, "expression": { - "type": "SafeMemberExpression", + "type": "MemberExpression", "start": 0, "end": 4, "loc": { @@ -90,7 +90,8 @@ }, "name": "y" }, - "computed": false + "computed": false, + "optional": true } } ], diff --git a/test/fixtures/lightscript/safe-member-expression/chained/expected.json b/test/fixtures/lightscript/safe-member-expression/chained/expected.json index e6f6c2c71f..f9fc92f96c 100644 --- a/test/fixtures/lightscript/safe-member-expression/chained/expected.json +++ b/test/fixtures/lightscript/safe-member-expression/chained/expected.json @@ -43,7 +43,7 @@ } }, "expression": { - "type": "SafeMemberExpression", + "type": "MemberExpression", "start": 0, "end": 26, "loc": { @@ -113,7 +113,7 @@ } }, "object": { - "type": "SafeMemberExpression", + "type": "MemberExpression", "start": 0, "end": 9, "loc": { @@ -127,7 +127,7 @@ } }, "object": { - "type": "SafeMemberExpression", + "type": "MemberExpression", "start": 0, "end": 6, "loc": { @@ -207,7 +207,8 @@ }, "name": "a" }, - "computed": false + "computed": false, + "optional": true }, "property": { "type": "Identifier", @@ -226,7 +227,8 @@ }, "name": "b" }, - "computed": false + "computed": false, + "optional": true }, "property": { "type": "Identifier", @@ -339,7 +341,8 @@ }, "name": "h" }, - "computed": false + "computed": false, + "optional": true } } ], diff --git a/test/fixtures/lightscript/safe-member-expression/computed/expected.json b/test/fixtures/lightscript/safe-member-expression/computed/expected.json index c3870b58b9..35829dbce4 100644 --- a/test/fixtures/lightscript/safe-member-expression/computed/expected.json +++ b/test/fixtures/lightscript/safe-member-expression/computed/expected.json @@ -43,7 +43,7 @@ } }, "expression": { - "type": "SafeMemberExpression", + "type": "MemberExpression", "start": 0, "end": 9, "loc": { @@ -123,7 +123,8 @@ "name": "y" } }, - "computed": true + "computed": true, + "optional": true } } ], diff --git a/test/fixtures/lightscript/safe-member-expression/index/expected.json b/test/fixtures/lightscript/safe-member-expression/index/expected.json index 2aca9d8bda..72c526b248 100644 --- a/test/fixtures/lightscript/safe-member-expression/index/expected.json +++ b/test/fixtures/lightscript/safe-member-expression/index/expected.json @@ -43,7 +43,7 @@ } }, "expression": { - "type": "SafeMemberExpression", + "type": "MemberExpression", "start": 0, "end": 4, "loc": { @@ -87,16 +87,17 @@ "column": 4 } }, - "value": 0, "extra": { - "raw": "0", - "rawValue": 0 - } + "rawValue": 0, + "raw": "0" + }, + "value": 0 }, - "computed": true + "computed": true, + "optional": true } } ], "directives": [] } -} +} \ No newline at end of file diff --git a/test/fixtures/safe-call-expression/js/basic/actual.js b/test/fixtures/safe-call-expression/js/basic/actual.js new file mode 100644 index 0000000000..fcd86af6e6 --- /dev/null +++ b/test/fixtures/safe-call-expression/js/basic/actual.js @@ -0,0 +1 @@ +f?.(x) diff --git a/test/fixtures/safe-call-expression/js/basic/expected.json b/test/fixtures/safe-call-expression/js/basic/expected.json new file mode 100644 index 0000000000..e3d21383c0 --- /dev/null +++ b/test/fixtures/safe-call-expression/js/basic/expected.json @@ -0,0 +1,101 @@ +{ + "type": "File", + "start": 0, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 6 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 6 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 6 + } + }, + "expression": { + "type": "CallExpression", + "start": 0, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 6 + } + }, + "callee": { + "type": "Identifier", + "start": 0, + "end": 1, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 1 + }, + "identifierName": "f" + }, + "name": "f" + }, + "optional": true, + "arguments": [ + { + "type": "Identifier", + "start": 4, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "x" + }, + "name": "x" + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/safe-call-expression/lightscript/basic/expected.json b/test/fixtures/safe-call-expression/lightscript/basic/expected.json index 7242a73744..741e29e793 100644 --- a/test/fixtures/safe-call-expression/lightscript/basic/expected.json +++ b/test/fixtures/safe-call-expression/lightscript/basic/expected.json @@ -73,6 +73,7 @@ }, "name": "f" }, + "optional": true, "arguments": [ { "type": "Identifier", @@ -91,8 +92,7 @@ }, "name": "x" } - ], - "safe": true + ] } } ], diff --git a/test/fixtures/safe-call-expression/lightscript/chained/expected.json b/test/fixtures/safe-call-expression/lightscript/chained/expected.json index f15600c028..2129c7ae44 100644 --- a/test/fixtures/safe-call-expression/lightscript/chained/expected.json +++ b/test/fixtures/safe-call-expression/lightscript/chained/expected.json @@ -87,11 +87,11 @@ }, "name": "f" }, - "arguments": [], - "safe": true + "optional": true, + "arguments": [] }, - "arguments": [], - "safe": true + "optional": true, + "arguments": [] } } ], diff --git a/test/fixtures/safe-call-expression/lightscript/match-test/expected.json b/test/fixtures/safe-call-expression/lightscript/match-test/expected.json index 546c1a6817..7a1ce01be3 100644 --- a/test/fixtures/safe-call-expression/lightscript/match-test/expected.json +++ b/test/fixtures/safe-call-expression/lightscript/match-test/expected.json @@ -106,7 +106,7 @@ }, "name": "it" }, - "safe": true, + "optional": true, "arguments": [] } ], diff --git a/test/fixtures/safe-call-expression/lightscript/match-typecast-unfortunate/expected.json b/test/fixtures/safe-call-expression/lightscript/match-typecast-unfortunate/expected.json index 047aca836f..84e7179e33 100644 --- a/test/fixtures/safe-call-expression/lightscript/match-typecast-unfortunate/expected.json +++ b/test/fixtures/safe-call-expression/lightscript/match-typecast-unfortunate/expected.json @@ -120,7 +120,7 @@ }, "name": "it" }, - "safe": true, + "optional": true, "arguments": [ { "type": "Identifier", diff --git a/test/fixtures/safe-call-expression/lightscript/ternary-3/expected.json b/test/fixtures/safe-call-expression/lightscript/ternary-3/expected.json index 880245a128..6b4ce86849 100644 --- a/test/fixtures/safe-call-expression/lightscript/ternary-3/expected.json +++ b/test/fixtures/safe-call-expression/lightscript/ternary-3/expected.json @@ -121,6 +121,7 @@ }, "name": "c" }, + "optional": true, "arguments": [ { "type": "Identifier", @@ -139,8 +140,7 @@ }, "name": "d" } - ], - "safe": true + ] } } } diff --git a/test/fixtures/safe-call-expression/lightscript/ternary-typecast-unfortunate/expected.json b/test/fixtures/safe-call-expression/lightscript/ternary-typecast-unfortunate/expected.json index 7569321a26..1b69a03e1e 100644 --- a/test/fixtures/safe-call-expression/lightscript/ternary-typecast-unfortunate/expected.json +++ b/test/fixtures/safe-call-expression/lightscript/ternary-typecast-unfortunate/expected.json @@ -87,6 +87,7 @@ }, "name": "a" }, + "optional": true, "arguments": [ { "type": "Identifier", @@ -105,8 +106,7 @@ }, "name": "b" } - ], - "safe": true + ] }, "typeAnnotation": { "type": "TypeAnnotation", diff --git a/test/fixtures/safe-call-expression/lightscript/tilde/expected.json b/test/fixtures/safe-call-expression/lightscript/tilde/expected.json index 71bb10d16e..b4b490f751 100644 --- a/test/fixtures/safe-call-expression/lightscript/tilde/expected.json +++ b/test/fixtures/safe-call-expression/lightscript/tilde/expected.json @@ -90,7 +90,7 @@ }, "name": "b" }, - "safe": true, + "optional": true, "arguments": [ { "type": "Identifier", diff --git a/test/fixtures/tilde-call-expression/safe/basic/expected.json b/test/fixtures/tilde-call-expression/safe/basic/expected.json index ac93c270d4..918bdd48aa 100644 --- a/test/fixtures/tilde-call-expression/safe/basic/expected.json +++ b/test/fixtures/tilde-call-expression/safe/basic/expected.json @@ -90,7 +90,7 @@ }, "name": "b" }, - "safe": true, + "optional": true, "arguments": [] } }