[Pkg-javascript-commits] [node-acorn-jsx] 422/484: Implement shorthand property assignment in ambiguous contexts.
Bastien Roucariès
rouca at moszumanska.debian.org
Sat Aug 19 14:21:05 UTC 2017
This is an automated email from the git hooks/post-receive script.
rouca pushed a commit to branch master
in repository node-acorn-jsx.
commit 65d09eac6eeeec5dda2a03db715e881892a9675d
Author: Ingvar Stepanyan <me at rreverser.com>
Date: Fri Jan 23 23:41:03 2015 +0200
Implement shorthand property assignment in ambiguous contexts.
Issue #181.
---
acorn.js | 138 ++++++++++++++++----------
test/tests-harmony.js | 265 ++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 348 insertions(+), 55 deletions(-)
diff --git a/acorn.js b/acorn.js
index d7fc369..01f7037 100644
--- a/acorn.js
+++ b/acorn.js
@@ -1468,7 +1468,7 @@
// Convert existing expression atom to assignable pattern
// if possible.
- function toAssignable(node, allowSpread, checkType) {
+ function toAssignable(node) {
if (options.ecmaVersion >= 6 && node) {
switch (node.type) {
case "Identifier":
@@ -1476,43 +1476,32 @@
case "ObjectPattern":
case "ArrayPattern":
case "AssignmentPattern":
- case "RestElement":
break;
case "ObjectExpression":
node.type = "ObjectPattern";
for (var i = 0; i < node.properties.length; i++) {
var prop = node.properties[i];
- if (prop.kind !== "init") unexpected(prop.key.start);
- toAssignable(prop.value, false, checkType);
+ if (prop.kind !== "init") raise(prop.key.start, "Object pattern can't contain getter or setter");
+ toAssignable(prop.value);
}
break;
case "ArrayExpression":
node.type = "ArrayPattern";
- toAssignableList(node.elements, checkType);
- break;
-
- case "SpreadElement":
- if (allowSpread) {
- node.type = "RestElement";
- toAssignable(node.argument, false, checkType);
- checkSpreadAssign(node.argument);
- } else {
- unexpected(node.start);
- }
+ toAssignableList(node.elements);
break;
case "AssignmentExpression":
if (node.operator === "=") {
node.type = "AssignmentPattern";
} else {
- unexpected(node.left.end);
+ raise(node.left.end, "Only '=' operator can be used for specifying default value.");
}
break;
default:
- if (checkType) unexpected(node.start);
+ raise(node.start, "Assigning to rvalue");
}
}
return node;
@@ -1520,19 +1509,33 @@
// Convert list of expression atoms to binding list.
- function toAssignableList(exprList, checkType) {
- for (var i = 0; i < exprList.length; i++) {
- toAssignable(exprList[i], i === exprList.length - 1, checkType);
+ function toAssignableList(exprList) {
+ if (exprList.length) {
+ for (var i = 0; i < exprList.length - 1; i++) {
+ toAssignable(exprList[i]);
+ }
+ var last = exprList[exprList.length - 1];
+ switch (last.type) {
+ case "RestElement":
+ break;
+ case "SpreadElement":
+ last.type = "RestElement";
+ toAssignable(last.argument);
+ checkSpreadAssign(last.argument);
+ break;
+ default:
+ toAssignable(last);
+ }
}
return exprList;
}
// Parses spread element.
- function parseSpread() {
+ function parseSpread(refShorthandDefaultPos) {
var node = startNode();
next();
- node.argument = parseMaybeAssign();
+ node.argument = parseMaybeAssign(refShorthandDefaultPos);
return finishNode(node, "SpreadElement");
}
@@ -1837,10 +1840,14 @@
return parseForIn(node, init);
return parseFor(node, init);
}
- var init = parseExpression(true);
+ var refShorthandDefaultPos = {start: 0};
+ var init = parseExpression(true, refShorthandDefaultPos);
if (tokType === _in || (options.ecmaVersion >= 6 && isContextual("of"))) {
+ toAssignable(init);
checkLVal(init);
return parseForIn(node, init);
+ } else if (refShorthandDefaultPos.start) {
+ unexpected(refShorthandDefaultPos.start);
}
return parseFor(node, init);
}
@@ -2069,17 +2076,20 @@
// and, *if* the syntactic construct they handle is present, wrap
// the AST node that the inner parser gave them in another node.
- // Parse a full expression. The arguments are used to forbid comma
- // sequences (in argument lists, array literals, or object literals)
- // or the `in` operator (in for loops initalization expressions).
+ // Parse a full expression. The optional arguments are used to
+ // forbid the `in` operator (in for loops initalization expressions)
+ // and provide reference for storing '=' operator inside shorthand
+ // property assignment in contexts where both object expression
+ // and object pattern might appear (so it's possible to raise
+ // delayed syntax error at correct position).
- function parseExpression(noIn) {
+ function parseExpression(noIn, refShorthandDefaultPos) {
var start = storeCurrentPos();
- var expr = parseMaybeAssign(noIn);
+ var expr = parseMaybeAssign(noIn, refShorthandDefaultPos);
if (tokType === _comma) {
var node = startNodeAt(start);
node.expressions = [expr];
- while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn));
+ while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn, refShorthandDefaultPos));
return finishNode(node, "SequenceExpression");
}
return expr;
@@ -2088,26 +2098,37 @@
// Parse an assignment expression. This includes applications of
// operators like `+=`.
- function parseMaybeAssign(noIn) {
+ function parseMaybeAssign(noIn, refShorthandDefaultPos) {
+ var failOnShorthandAssign;
+ if (!refShorthandDefaultPos) {
+ refShorthandDefaultPos = {start: 0};
+ failOnShorthandAssign = true;
+ } else {
+ failOnShorthandAssign = false;
+ }
var start = storeCurrentPos();
- var left = parseMaybeConditional(noIn);
+ var left = parseMaybeConditional(noIn, refShorthandDefaultPos);
if (tokType.isAssign) {
var node = startNodeAt(start);
node.operator = tokVal;
node.left = tokType === _eq ? toAssignable(left) : left;
+ refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly
checkLVal(left);
next();
node.right = parseMaybeAssign(noIn);
return finishNode(node, "AssignmentExpression");
+ } else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
+ unexpected(refShorthandDefaultPos.start);
}
return left;
}
// Parse a ternary conditional (`?:`) operator.
- function parseMaybeConditional(noIn) {
+ function parseMaybeConditional(noIn, refShorthandDefaultPos) {
var start = storeCurrentPos();
- var expr = parseExprOps(noIn);
+ var expr = parseExprOps(noIn, refShorthandDefaultPos);
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
if (eat(_question)) {
var node = startNodeAt(start);
node.test = expr;
@@ -2121,9 +2142,11 @@
// Start the precedence parser.
- function parseExprOps(noIn) {
+ function parseExprOps(noIn, refShorthandDefaultPos) {
var start = storeCurrentPos();
- return parseExprOp(parseMaybeUnary(), start, -1, noIn);
+ var expr = parseMaybeUnary(refShorthandDefaultPos);
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+ return parseExprOp(expr, start, -1, noIn);
}
// Parse binary operators with the operator precedence parsing
@@ -2152,13 +2175,14 @@
// Parse unary operators, both prefix and postfix.
- function parseMaybeUnary() {
+ function parseMaybeUnary(refShorthandDefaultPos) {
if (tokType.prefix) {
var node = startNode(), update = tokType.isUpdate;
node.operator = tokVal;
node.prefix = true;
next();
node.argument = parseMaybeUnary();
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) unexpected(refShorthandDefaultPos.start);
if (update) checkLVal(node.argument);
else if (strict && node.operator === "delete" &&
node.argument.type === "Identifier")
@@ -2166,7 +2190,8 @@
return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
}
var start = storeCurrentPos();
- var expr = parseExprSubscripts();
+ var expr = parseExprSubscripts(refShorthandDefaultPos);
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
while (tokType.postfix && !canInsertSemicolon()) {
var node = startNodeAt(start);
node.operator = tokVal;
@@ -2181,9 +2206,11 @@
// Parse call, dot, and `[]`-subscript expressions.
- function parseExprSubscripts() {
+ function parseExprSubscripts(refShorthandDefaultPos) {
var start = storeCurrentPos();
- return parseSubscripts(parseExprAtom(), start);
+ var expr = parseExprAtom(refShorthandDefaultPos);
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+ return parseSubscripts(expr, start);
}
function parseSubscripts(base, start, noCalls) {
@@ -2218,7 +2245,7 @@
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
// or `{}`.
- function parseExprAtom() {
+ function parseExprAtom(refShorthandDefaultPos) {
switch (tokType) {
case _this:
var node = startNode();
@@ -2268,11 +2295,11 @@
if (options.ecmaVersion >= 7 && tokType === _for) {
return parseComprehension(node, false);
}
- node.elements = parseExprList(_bracketR, true, true);
+ node.elements = parseExprList(_bracketR, true, true, refShorthandDefaultPos);
return finishNode(node, "ArrayExpression");
case _braceL:
- return parseObj();
+ return parseObj(false, refShorthandDefaultPos);
case _function:
var node = startNode();
@@ -2302,7 +2329,8 @@
return parseComprehension(startNodeAt(start), true);
}
- var innerStart = storeCurrentPos(), exprList = [], first = true, spreadStart, innerParenStart;
+ var innerStart = storeCurrentPos(), exprList = [], first = true;
+ var refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart;
while (tokType !== _parenR) {
first ? first = false : expect(_comma);
if (tokType === _ellipsis) {
@@ -2313,7 +2341,7 @@
if (tokType === _parenL && !innerParenStart) {
innerParenStart = tokStart;
}
- exprList.push(parseMaybeAssign());
+ exprList.push(parseMaybeAssign(false, refShorthandDefaultPos));
}
}
var innerEnd = storeCurrentPos();
@@ -2326,6 +2354,7 @@
if (!exprList.length) unexpected(lastStart);
if (spreadStart) unexpected(spreadStart);
+ if (refShorthandDefaultPos.start) unexpected(refShorthandDefaultPos.start);
if (exprList.length > 1) {
val = startNodeAt(innerStart);
@@ -2392,7 +2421,7 @@
// Parse an object literal or binding pattern.
- function parseObj(isPattern) {
+ function parseObj(isPattern, refShorthandDefaultPos) {
var node = startNode(), first = true, propHash = {};
node.properties = [];
next();
@@ -2414,7 +2443,7 @@
}
parsePropertyName(prop);
if (eat(_colon)) {
- prop.value = isPattern ? parseMaybeDefault(start) : parseMaybeAssign();
+ prop.value = isPattern ? parseMaybeDefault(start) : parseMaybeAssign(false, refShorthandDefaultPos);
prop.kind = "init";
} else if (options.ecmaVersion >= 6 && tokType === _parenL) {
if (isPattern) unexpected();
@@ -2430,7 +2459,15 @@
prop.value = parseMethod(false);
} else if (options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
prop.kind = "init";
- prop.value = isPattern ? parseMaybeDefault(start, prop.key) : prop.key;
+ if (isPattern) {
+ prop.value = parseMaybeDefault(start, prop.key);
+ } else if (tokType === _eq && refShorthandDefaultPos) {
+ if (!refShorthandDefaultPos.start)
+ refShorthandDefaultPos.start = tokStart;
+ prop.value = parseMaybeDefault(start, prop.key);
+ } else {
+ prop.value = prop.key;
+ }
prop.shorthand = true;
} else unexpected();
@@ -2583,7 +2620,7 @@
// nothing in between them to be parsed as `null` (which is needed
// for array literals).
- function parseExprList(close, allowTrailingComma, allowEmpty) {
+ function parseExprList(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {
var elts = [], first = true;
while (!eat(close)) {
if (!first) {
@@ -2594,7 +2631,10 @@
if (allowEmpty && tokType === _comma) {
elts.push(null);
} else {
- elts.push(tokType === _ellipsis ? parseSpread() : parseMaybeAssign());
+ if (tokType === _ellipsis)
+ elts.push(parseSpread(refShorthandDefaultPos));
+ else
+ elts.push(parseMaybeAssign(false, refShorthandDefaultPos));
}
}
return elts;
diff --git a/test/tests-harmony.js b/test/tests-harmony.js
index 9fc9dec..641f186 100644
--- a/test/tests-harmony.js
+++ b/test/tests-harmony.js
@@ -13745,7 +13745,7 @@ testFail("[2] = 42", "Assigning to rvalue (1:1)", {ecmaVersion: 6});
testFail("({ obj:20 }) = 42", "Assigning to rvalue (1:7)", {ecmaVersion: 6});
-testFail("( { get x() {} } ) = 0", "Unexpected token (1:8)", {ecmaVersion: 6});
+testFail("( { get x() {} } ) = 0", "Object pattern can't contain getter or setter (1:8)", {ecmaVersion: 6});
testFail("x \n is y", "Unexpected token (2:4)", {ecmaVersion: 6});
@@ -13801,9 +13801,9 @@ testFail("\"use strict\"; (a) => 00", "Invalid number (1:21)", {ecmaVersion: 6})
testFail("() <= 42", "Unexpected token (1:1)", {ecmaVersion: 6});
-testFail("(10) => 00", "Unexpected token (1:1)", {ecmaVersion: 6});
+testFail("(10) => 00", "Assigning to rvalue (1:1)", {ecmaVersion: 6});
-testFail("(10, 20) => 00", "Unexpected token (1:1)", {ecmaVersion: 6});
+testFail("(10, 20) => 00", "Assigning to rvalue (1:1)", {ecmaVersion: 6});
testFail("yield v", "Unexpected token (1:6)", {ecmaVersion: 6});
@@ -14037,7 +14037,7 @@ testFail("\"use strict\"; function x(a, ...[a]){}", "Argument name clash in stri
testFail("(...a, b) => {}", "Unexpected token (1:5)", {ecmaVersion: 6});
-testFail("([ 5 ]) => {}", "Unexpected token (1:3)", {ecmaVersion: 6});
+testFail("([ 5 ]) => {}", "Assigning to rvalue (1:3)", {ecmaVersion: 6});
testFail("({ 5 }) => {}", "Unexpected token (1:5)", {ecmaVersion: 6});
@@ -14045,7 +14045,7 @@ testFail("(...[ 5 ]) => {}", "Unexpected token (1:6)", {ecmaVersion: 6});
testFail("[...{ a }] = b", "Unexpected token (1:4)", {ecmaVersion: 6});
-testFail("[...a, b] = c", "Unexpected token (1:1)", {ecmaVersion: 6});
+testFail("[...a, b] = c", "Assigning to rvalue (1:1)", {ecmaVersion: 6});
testFail("({ t(eval) { \"use strict\"; } });", "Defining 'eval' in strict mode (1:5)", {ecmaVersion: 6});
@@ -14120,7 +14120,7 @@ testFail("\"use strict\"; (eval) => 42", "Defining 'eval' in strict mode (1:15)"
testFail("(eval) => { \"use strict\"; 42 }", "Defining 'eval' in strict mode (1:1)", {ecmaVersion: 6});
-testFail("({ get test() { } }) => 42", "Unexpected token (1:7)", {ecmaVersion: 6});
+testFail("({ get test() { } }) => 42", "Object pattern can't contain getter or setter (1:7)", {ecmaVersion: 6});
/* Regression tests */
@@ -14509,6 +14509,259 @@ test("var [localVar = defaultValue] = obj", {
loose: false
});
+test("({x = 0} = obj)", {
+ type: "Program",
+ range: [0, 15],
+ body: [{
+ type: "ExpressionStatement",
+ range: [0, 15],
+ expression: {
+ type: "AssignmentExpression",
+ range: [1, 14],
+ operator: "=",
+ left: {
+ type: "ObjectPattern",
+ range: [1, 8],
+ properties: [{
+ type: "Property",
+ range: [2, 7],
+ method: false,
+ shorthand: true,
+ computed: false,
+ key: {
+ type: "Identifier",
+ range: [2, 3],
+ name: "x"
+ },
+ kind: "init",
+ value: {
+ type: "AssignmentPattern",
+ range: [6, 7],
+ operator: "=",
+ left: {
+ type: "Identifier",
+ range: [2, 3],
+ name: "x"
+ },
+ right: {
+ type: "Literal",
+ range: [6, 7],
+ value: 0
+ }
+ }
+ }]
+ },
+ right: {
+ type: "Identifier",
+ range: [11, 14],
+ name: "obj"
+ }
+ }
+ }]
+}, {
+ ecmaVersion: 6,
+ ranges: true,
+ loose: false
+});
+
+test("({x = 0}) => x", {
+ type: "Program",
+ range: [0, 14],
+ body: [{
+ type: "ExpressionStatement",
+ range: [0, 14],
+ expression: {
+ type: "ArrowFunctionExpression",
+ range: [0, 14],
+ id: null,
+ generator: false,
+ expression: true,
+ params: [{
+ type: "ObjectPattern",
+ range: [1, 8],
+ properties: [{
+ type: "Property",
+ range: [2, 7],
+ method: false,
+ shorthand: true,
+ computed: false,
+ key: {
+ type: "Identifier",
+ range: [2, 3],
+ name: "x"
+ },
+ kind: "init",
+ value: {
+ type: "AssignmentPattern",
+ range: [6, 7],
+ operator: "=",
+ left: {
+ type: "Identifier",
+ range: [2, 3],
+ name: "x"
+ },
+ right: {
+ type: "Literal",
+ range: [6, 7],
+ value: 0
+ }
+ }
+ }]
+ }],
+ body: {
+ type: "Identifier",
+ range: [13, 14],
+ name: "x"
+ }
+ }
+ }]
+}, {
+ ecmaVersion: 6,
+ ranges: true,
+ loose: false
+});
+
+test("[a, {b: {c = 1}}] = arr", {
+ type: "Program",
+ range: [0, 23],
+ body: [{
+ type: "ExpressionStatement",
+ range: [0, 23],
+ expression: {
+ type: "AssignmentExpression",
+ range: [0, 23],
+ operator: "=",
+ left: {
+ type: "ArrayPattern",
+ range: [0, 17],
+ elements: [
+ {
+ type: "Identifier",
+ range: [1, 2],
+ name: "a"
+ },
+ {
+ type: "ObjectPattern",
+ range: [4, 16],
+ properties: [{
+ type: "Property",
+ range: [5, 15],
+ method: false,
+ shorthand: false,
+ computed: false,
+ key: {
+ type: "Identifier",
+ range: [5, 6],
+ name: "b"
+ },
+ value: {
+ type: "ObjectPattern",
+ range: [8, 15],
+ properties: [{
+ type: "Property",
+ range: [9, 14],
+ method: false,
+ shorthand: true,
+ computed: false,
+ key: {
+ type: "Identifier",
+ range: [9, 10],
+ name: "c"
+ },
+ kind: "init",
+ value: {
+ type: "AssignmentPattern",
+ range: [13, 14],
+ operator: "=",
+ left: {
+ type: "Identifier",
+ range: [9, 10],
+ name: "c"
+ },
+ right: {
+ type: "Literal",
+ range: [13, 14],
+ value: 1
+ }
+ }
+ }]
+ },
+ kind: "init"
+ }]
+ }
+ ]
+ },
+ right: {
+ type: "Identifier",
+ range: [20, 23],
+ name: "arr"
+ }
+ }
+ }]
+}, {
+ ecmaVersion: 6,
+ ranges: true,
+ loose: false
+});
+
+test("for ({x = 0} in arr);", {
+ type: "Program",
+ range: [0, 21],
+ body: [{
+ type: "ForInStatement",
+ range: [0, 21],
+ left: {
+ type: "ObjectPattern",
+ range: [5, 12],
+ properties: [{
+ type: "Property",
+ range: [6, 11],
+ method: false,
+ shorthand: true,
+ computed: false,
+ key: {
+ type: "Identifier",
+ range: [6, 7],
+ name: "x"
+ },
+ kind: "init",
+ value: {
+ type: "AssignmentPattern",
+ range: [10, 11],
+ operator: "=",
+ left: {
+ type: "Identifier",
+ range: [6, 7],
+ name: "x"
+ },
+ right: {
+ type: "Literal",
+ range: [10, 11],
+ value: 0
+ }
+ }
+ }]
+ },
+ right: {
+ type: "Identifier",
+ range: [16, 19],
+ name: "arr"
+ },
+ body: {
+ type: "EmptyStatement",
+ range: [20, 21]
+ }
+ }]
+}, {
+ ecmaVersion: 6,
+ ranges: true,
+ loose: false
+});
+
+testFail("obj = {x = 0}", "Unexpected token (1:9)", {ecmaVersion: 6});
+
+testFail("f({x = 0})", "Unexpected token (1:5)", {ecmaVersion: 6});
+
// https://github.com/marijnh/acorn/issues/191
test("try {} catch ({message}) {}", {
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-acorn-jsx.git
More information about the Pkg-javascript-commits
mailing list