[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