From 648f3159cc22cabb42773b590d9305618224ae83 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 25 Jul 2014 16:14:11 +0300 Subject: [PATCH] Added `for (..of..)` support and guard against `for (let var=.. in|of ..)` loops. --- acorn.js | 32 ++-- test/tests-harmony.js | 10 +- test/tests.js | 383 ------------------------------------------ 3 files changed, 25 insertions(+), 400 deletions(-) diff --git a/acorn.js b/acorn.js index 25638b3da7..81c47556da 100644 --- a/acorn.js +++ b/acorn.js @@ -298,6 +298,7 @@ // we assign a variable name to it for quick comparing. var _in = {keyword: "in", binop: 7, beforeExpr: true}; + var _of = {keyword: "of", beforeExpr: true}; // Map keyword names to token types. @@ -312,7 +313,7 @@ "typeof": {keyword: "typeof", prefix: true, beforeExpr: true}, "void": {keyword: "void", prefix: true, beforeExpr: true}, "delete": {keyword: "delete", prefix: true, beforeExpr: true}, - "class": _class, "extends": _extends, "static": _static}; + "class": _class, "extends": _extends, "static": _static, "of": _of}; // Punctuation token types. Again, the `type` property is purely for debugging. @@ -431,7 +432,7 @@ var isEcma5AndLessKeyword = makePredicate(ecma5AndLessKeywords); - var isEcma6Keyword = makePredicate(ecma5AndLessKeywords + " let const class extends static"); + var isEcma6Keyword = makePredicate(ecma5AndLessKeywords + " let const class extends static of"); var isKeyword = isEcma5AndLessKeyword; @@ -1300,13 +1301,13 @@ return finishNode(node, "DoWhileStatement"); } - // Disambiguating between a `for` and a `for`/`in` loop is - // non-trivial. Basically, we have to parse the init `var` + // Disambiguating between a `for` and a `for`/`in` or `for`/`of` + // loop is non-trivial. Basically, we have to parse the init `var` // statement or expression, disallowing the `in` operator (see // the second parameter to `parseExpression`), and then check - // whether the next token is `in`. When there is no init part - // (semicolon immediately after the opening parenthesis), it is - // a regular `for` loop. + // whether the next token is `in` or `of`. When there is no init + // part (semicolon immediately after the opening parenthesis), it + // is a regular `for` loop. function parseForStatement(node) { next(); @@ -1314,16 +1315,20 @@ expect(_parenL); if (tokType === _semi) return parseFor(node, null); if (tokType === _var || tokType === _let) { - var init = startNode(), varKind = tokType.keyword; + var init = startNode(), varKind = tokType.keyword, isLet = tokType === _let; next(); parseVar(init, true, varKind); finishNode(init, "VariableDeclaration"); - if (init.declarations.length === 1 && eat(_in)) + if ((tokType === _in || tokType === _of) && init.declarations.length === 1 && + !(isLet && init.declarations[0].init)) return parseForIn(node, init); return parseFor(node, init); } var init = parseExpression(false, true); - if (eat(_in)) {checkLVal(init); return parseForIn(node, init);} + if (tokType === _in || tokType === _of) { + checkLVal(init); + return parseForIn(node, init); + } return parseFor(node, init); } @@ -1514,15 +1519,18 @@ return finishNode(node, "ForStatement"); } - // Parse a `for`/`in` loop. + // Parse a `for`/`in` and `for`/`of` loop, which are almost + // same from parser's perspective. function parseForIn(node, init) { + var type = tokType === _in ? "ForInStatement" : "ForOfStatement"; + next(); node.left = init; node.right = parseExpression(); expect(_parenR); node.body = parseStatement(); labels.pop(); - return finishNode(node, "ForInStatement"); + return finishNode(node, type); } // Parse a list of variable declarations. diff --git a/test/tests-harmony.js b/test/tests-harmony.js index c688adece4..862e19b879 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -15899,9 +15899,9 @@ testFail("({ obj:20 }) = 42", "Unexpected token (1:7)", {ecmaVersion: 6}); testFail("( { get x() {} } ) = 0", "Unexpected token (1:8)", {ecmaVersion: 6}); -testFail("x \n is y", "Unexpected token (2:5)", {ecmaVersion: 6}); +testFail("x \n is y", "Unexpected token (2:4)", {ecmaVersion: 6}); -testFail("x \n isnt y", "Unexpected token (2:7)", {ecmaVersion: 6}); +testFail("x \n isnt y", "Unexpected token (2:6)", {ecmaVersion: 6}); testFail("function default() {}", "Unexpected token (1:9)", {ecmaVersion: 6}); @@ -15921,11 +15921,11 @@ testFail("\"use strict\"; ({ v: eval }) = obj", "Assigning to eval in strict mod testFail("\"use strict\"; ({ v: arguments }) = obj", "Assigning to arguments in strict mode (1:20)", {ecmaVersion: 6}); -testFail("for (var i = function() { return 10 in [] } in list) process(x);", "Unexpected token (1:45)", {ecmaVersion: 6}); +testFail("for (var i = function() { return 10 in [] } in list) process(x);", "Unexpected token (1:44)", {ecmaVersion: 6}); -testFail("for (let x = 42 in list) process(x);", "Unexpected token (1:17)", {ecmaVersion: 6}); +testFail("for (let x = 42 in list) process(x);", "Unexpected token (1:16)", {ecmaVersion: 6}); -testFail("for (let x = 42 of list) process(x);", "Unexpected token (1:17)", {ecmaVersion: 6}); +testFail("for (let x = 42 of list) process(x);", "Unexpected token (1:16)", {ecmaVersion: 6}); testFail("module\n\"crypto\" {}", "Unexpected token (1:7)", {ecmaVersion: 6}); diff --git a/test/tests.js b/test/tests.js index 9a7793e78c..f7d9e0432a 100644 --- a/test/tests.js +++ b/test/tests.js @@ -28221,389 +28221,6 @@ test("for (let x in list) process(x);", { } }, {ecmaVersion: 6, locations: true}); -test("for (let x = 42 in list) process(x);", { - type: "Program", - body: [ - { - type: "ForInStatement", - left: { - type: "VariableDeclaration", - declarations: [ - { - type: "VariableDeclarator", - id: { - type: "Identifier", - name: "x", - loc: { - start: { - line: 1, - column: 9 - }, - end: { - line: 1, - column: 10 - } - } - }, - init: { - type: "Literal", - value: 42, - loc: { - start: { - line: 1, - column: 13 - }, - end: { - line: 1, - column: 15 - } - } - }, - loc: { - start: { - line: 1, - column: 9 - }, - end: { - line: 1, - column: 15 - } - } - } - ], - kind: "let", - loc: { - start: { - line: 1, - column: 5 - }, - end: { - line: 1, - column: 15 - } - } - }, - right: { - type: "Identifier", - name: "list", - loc: { - start: { - line: 1, - column: 19 - }, - end: { - line: 1, - column: 23 - } - } - }, - body: { - type: "ExpressionStatement", - expression: { - type: "CallExpression", - callee: { - type: "Identifier", - name: "process", - loc: { - start: { - line: 1, - column: 25 - }, - end: { - line: 1, - column: 32 - } - } - }, - arguments: [ - { - type: "Identifier", - name: "x", - loc: { - start: { - line: 1, - column: 33 - }, - end: { - line: 1, - column: 34 - } - } - } - ], - loc: { - start: { - line: 1, - column: 25 - }, - end: { - line: 1, - column: 35 - } - } - }, - loc: { - start: { - line: 1, - column: 25 - }, - end: { - line: 1, - column: 36 - } - } - }, - loc: { - start: { - line: 1, - column: 0 - }, - end: { - line: 1, - column: 36 - } - } - } - ], - loc: { - start: { - line: 1, - column: 0 - }, - end: { - line: 1, - column: 36 - } - } -}, {ecmaVersion: 6, locations: true}); - -test("for (let i = function() { return 10 in [] } in list) process(x);", { - type: "Program", - body: [ - { - type: "ForInStatement", - left: { - type: "VariableDeclaration", - declarations: [ - { - type: "VariableDeclarator", - id: { - type: "Identifier", - name: "i", - loc: { - start: { - line: 1, - column: 9 - }, - end: { - line: 1, - column: 10 - } - } - }, - init: { - type: "FunctionExpression", - id: null, - params: [], - body: { - type: "BlockStatement", - body: [ - { - type: "ReturnStatement", - argument: { - type: "BinaryExpression", - left: { - type: "Literal", - value: 10, - loc: { - start: { - line: 1, - column: 33 - }, - end: { - line: 1, - column: 35 - } - } - }, - operator: "in", - right: { - type: "ArrayExpression", - elements: [], - loc: { - start: { - line: 1, - column: 39 - }, - end: { - line: 1, - column: 41 - } - } - }, - loc: { - start: { - line: 1, - column: 33 - }, - end: { - line: 1, - column: 41 - } - } - }, - loc: { - start: { - line: 1, - column: 26 - }, - end: { - line: 1, - column: 41 - } - } - } - ], - loc: { - start: { - line: 1, - column: 24 - }, - end: { - line: 1, - column: 43 - } - } - }, - loc: { - start: { - line: 1, - column: 13 - }, - end: { - line: 1, - column: 43 - } - } - }, - loc: { - start: { - line: 1, - column: 9 - }, - end: { - line: 1, - column: 43 - } - } - } - ], - kind: "let", - loc: { - start: { - line: 1, - column: 5 - }, - end: { - line: 1, - column: 43 - } - } - }, - right: { - type: "Identifier", - name: "list", - loc: { - start: { - line: 1, - column: 47 - }, - end: { - line: 1, - column: 51 - } - } - }, - body: { - type: "ExpressionStatement", - expression: { - type: "CallExpression", - callee: { - type: "Identifier", - name: "process", - loc: { - start: { - line: 1, - column: 53 - }, - end: { - line: 1, - column: 60 - } - } - }, - arguments: [ - { - type: "Identifier", - name: "x", - loc: { - start: { - line: 1, - column: 61 - }, - end: { - line: 1, - column: 62 - } - } - } - ], - loc: { - start: { - line: 1, - column: 53 - }, - end: { - line: 1, - column: 63 - } - } - }, - loc: { - start: { - line: 1, - column: 53 - }, - end: { - line: 1, - column: 64 - } - } - }, - loc: { - start: { - line: 1, - column: 0 - }, - end: { - line: 1, - column: 64 - } - } - } - ], - loc: { - start: { - line: 1, - column: 0 - }, - end: { - line: 1, - column: 64 - } - } -}, {ecmaVersion: 6, locations: true}); - test("const x = 42", { type: "Program", body: [