From acbef8557f2093e9149fbfbf1559b9f8bee56614 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Sun, 27 Jul 2014 04:24:26 +0300 Subject: [PATCH] Treat line ending after yield keyword as end of YieldExpression (as per http://people.mozilla.org/~jorendorff/es6-draft.html#sec-generator-function-definitions-runtime-semantics-evaluation). --- acorn.js | 9 ++++- index.html | 92 ++++++++++++++++++++++++------------------- test/tests-harmony.js | 80 +++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 42 deletions(-) diff --git a/acorn.js b/acorn.js index 68d6f82049..fcb3776156 100644 --- a/acorn.js +++ b/acorn.js @@ -2436,8 +2436,13 @@ function parseYield() { var node = startNode(); next(); - node.delegate = eat(_star); - node.argument = parseExpression(true); + if (eat(_semi) || canInsertSemicolon()) { + node.delegate = false; + node.argument = null; + } else { + node.delegate = eat(_star); + node.argument = parseExpression(true); + } return finishNode(node, "YieldExpression"); } diff --git a/index.html b/index.html index 33f1c3f494..0e18c66f52 100644 --- a/index.html +++ b/index.html @@ -936,20 +936,21 @@ try to bind the words eval or arguments.

}

Check if property name clashes with already added. Object/class getters and setters are not allowed to clash — either with each other or with an init property — and in -strict mode, init properties are also not allowed to be repeated.

  function checkPropClash(prop, defaultKind, propHash) {
+strict mode, init properties are also not allowed to be repeated.

  function checkPropClash(prop, propHash) {
     var key = prop.key, name;
     switch (key.type) {
       case "Identifier": name = key.name; break;
       case "Literal": name = String(key.value); break;
       default: return;
     }
-    var kind = prop.kind, other = propHash[name];
+    var kind = prop.kind || "init", other = propHash[name];
     if (other) {
-      var isGetSet = kind !== defaultKind;
-      if ((strict || isGetSet) && other[kind] || !(isGetSet ^ other[defaultKind]))
+      var isGetSet = kind !== "init";
+      if ((strict || isGetSet) && other[kind] || !(isGetSet ^ other.init))
         raise(key.start, "Redefinition of property");
     } else {
       other = propHash[name] = hash();
+      other.init = other.get = other.set = false;
     }
     other[kind] = true;
   }

Verify that a node is an lval — something that can be assigned @@ -1448,20 +1449,23 @@ or {}.

return finishNode(node, "Literal"); case _parenL: - var node = startNode(), tokStartLoc1 = tokStartLoc, tokStart1 = tokStart, val, exprList; - next(); - var oldParenL = ++metParenL; - if (tokType !== _parenR) { - val = parseExpression(); - exprList = val.type === "SequenceExpression" ? val.expressions : [val]; + var tokStartLoc1 = tokStartLoc, tokStart1 = tokStart, val, exprList; + next();

check whether this is generator comprehension or regular expression

      if (options.ecmaVersion >= 6 && tokType === _for) {
+        val = parseComprehension(startNode(), true);
       } else {
-        exprList = [];
-      }
-      expect(_parenR);

if '=>' follows '(...)', convert contents to arguments

      if (metParenL === oldParenL && eat(_arrow)) {
-        val = parseArrowExpression(node, exprList);
-      } else {

forbid '()' before everything but '=>'

        if (!val) unexpected(lastStart);

forbid '...' in sequence expressions

        if (options.ecmaVersion >= 6) {
-          for (var i = 0; i < exprList.length; i++) {
-            if (exprList[i].type === "SpreadElement") unexpected();
+        var oldParenL = ++metParenL;
+        if (tokType !== _parenR) {
+          val = parseExpression();
+          exprList = val.type === "SequenceExpression" ? val.expressions : [val];
+        } else {
+          exprList = [];
+        }
+        expect(_parenR);

if '=>' follows '(...)', convert contents to arguments

        if (metParenL === oldParenL && eat(_arrow)) {
+          val = parseArrowExpression(startNode(), exprList);
+        } else {

forbid '()' before everything but '=>'

          if (!val) unexpected(lastStart);

forbid '...' in sequence expressions

          if (options.ecmaVersion >= 6) {
+            for (var i = 0; i < exprList.length; i++) {
+              if (exprList[i].type === "SpreadElement") unexpected();
+            }
           }
         }
       }
@@ -1478,25 +1482,8 @@ or {}.

case _bracketL: var node = startNode(); - next();

check whether this is array comprehension or regular array

      if (options.ecmaVersion >= 6 && tokType === _for) {
-        node.blocks = [];
-        while (tokType === _for) {
-          var block = startNode();
-          next();
-          expect(_parenL);
-          block.left = toAssignable(parseExprAtom());
-          checkLVal(block.left, true);
-          if (tokType !== _name || tokVal !== "of") unexpected();
-          next();

of property is here for compatibility with Esprima's AST -which also supports deprecated [for (... in ...) expr]

          block.of = true;
-          block.right = parseExpression();
-          expect(_parenR);
-          node.blocks.push(finishNode(block, "ComprehensionBlock"));
-        }
-        node.filter = eat(_if) ? parseParenExpression() : null;
-        node.body = parseExpression();
-        expect(_bracketR);
-        return finishNode(node, "ComprehensionExpression");
+      next();

check whether this is array comprehension or regular array

      if (options.ecmaVersion >= 6 && tokType === _for) {
+        return parseComprehension(node, false);
       }
       node.elements = parseExprList(_bracketR, true, true);
       return finishNode(node, "ArrayExpression");
@@ -1598,7 +1585,7 @@ least, not without wrapping it in parentheses. Thus, it uses the

prop.shorthand = true; } else unexpected(); - checkPropClash(prop, "init", propHash); + checkPropClash(prop, propHash); node.properties.push(finishNode(prop, "Property")); } return finishNode(node, "ObjectExpression"); @@ -1754,7 +1741,7 @@ or arguments.

method.kind = ""; } method.value = parseMethod(isGenerator); - checkPropClash(method, "", method.static ? staticMethodHash : methodHash); + checkPropClash(method, method.static ? staticMethodHash : methodHash); classBody.body.push(finishNode(method, "MethodDefinition")); eat(_semi); } @@ -1903,9 +1890,34 @@ export { x, y as z } [from '...']

}

Parses yield expression inside generator.

  function parseYield() {
     var node = startNode();
     next();
-    node.delegate = eat(_star);
-    node.argument = parseExpression(true);
+    if (eat(_semi) || canInsertSemicolon()) {
+      node.delegate = false;
+      node.argument = null;
+    } else {
+      node.delegate = eat(_star);
+      node.argument = parseExpression(true);
+    }
     return finishNode(node, "YieldExpression");
+  }

Parses array and generator comprehensions.

  function parseComprehension(node, isGenerator) {
+    node.blocks = [];
+    while (tokType === _for) {
+      var block = startNode();
+      next();
+      expect(_parenL);
+      block.left = toAssignable(parseExprAtom());
+      checkLVal(block.left, true);
+      if (tokType !== _name || tokVal !== "of") unexpected();
+      next();

of property is here for compatibility with Esprima's AST +which also supports deprecated [for (... in ...) expr]

      block.of = true;
+      block.right = parseExpression();
+      expect(_parenR);
+      node.blocks.push(finishNode(block, "ComprehensionBlock"));
+    }
+    node.filter = eat(_if) ? parseParenExpression() : null;
+    node.body = parseExpression();
+    expect(isGenerator ? _parenR : _bracketR);
+    node.generator = isGenerator;
+    return finishNode(node, "ComprehensionExpression");
   }
 
 });
diff --git a/test/tests-harmony.js b/test/tests-harmony.js
index 98ec29f1a3..a6ec0f2c65 100644
--- a/test/tests-harmony.js
+++ b/test/tests-harmony.js
@@ -6138,6 +6138,86 @@ test("(function* () { yield v })", {
   locations: true
 });
 
+test("(function* () { yield\nv })", {
+  type: "Program",
+  body: [{
+    type: "ExpressionStatement",
+    expression: {
+      type: "FunctionExpression",
+      id: null,
+      params: [],
+      defaults: [],
+      body: {
+        type: "BlockStatement",
+        body: [
+          {
+            type: "ExpressionStatement",
+            expression: {
+              type: "YieldExpression",
+              argument: null,
+              delegate: false,
+              range: [16, 21],
+              loc: {
+                start: {line: 1, column: 16},
+                end: {line: 1, column: 21}
+              }
+            },
+            range: [16, 21],
+            loc: {
+              start: {line: 1, column: 16},
+              end: {line: 1, column: 21}
+            }
+          },
+          {
+            type: "ExpressionStatement",
+            expression: {
+              type: "Identifier",
+              name: "v",
+              range: [22, 23],
+              loc: {
+                start: {line: 2, column: 0},
+                end: {line: 2, column: 1}
+              }
+            },
+            range: [22, 23],
+            loc: {
+              start: {line: 2, column: 0},
+              end: {line: 2, column: 1}
+            }
+          }
+        ],
+        range: [14, 25],
+        loc: {
+          start: {line: 1, column: 14},
+          end: {line: 2, column: 3}
+        }
+      },
+      rest: null,
+      generator: true,
+      expression: false,
+      range: [0, 26],
+      loc: {
+        start: {line: 1, column: 0},
+        end: {line: 2, column: 4}
+      }
+    },
+    range: [0, 26],
+    loc: {
+      start: {line: 1, column: 0},
+      end: {line: 2, column: 4}
+    }
+  }],
+  range: [0, 26],
+  loc: {
+    start: {line: 1, column: 0},
+    end: {line: 2, column: 4}
+  }
+}, {
+  ecmaVersion: 6,
+  ranges: true,
+  locations: true
+});
+
 test("(function* () { yield *v })", {
   type: "Program",
   body: [{