diff --git a/acorn.js b/acorn.js index e81920972f..68d6f82049 100644 --- a/acorn.js +++ b/acorn.js @@ -1893,26 +1893,31 @@ return finishNode(node, "Literal"); case _parenL: - var node = startNode(), tokStartLoc1 = tokStartLoc, tokStart1 = tokStart, val, exprList; + var tokStartLoc1 = tokStartLoc, tokStart1 = tokStart, val, exprList; next(); - var oldParenL = ++metParenL; - if (tokType !== _parenR) { - val = parseExpression(); - exprList = val.type === "SequenceExpression" ? val.expressions : [val]; + // 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(); + } } } } @@ -1932,26 +1937,7 @@ 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"); + return parseComprehension(node, false); } node.elements = parseExprList(_bracketR, true, true); return finishNode(node, "ArrayExpression"); @@ -2455,4 +2441,30 @@ 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 fc522c8710..98ec29f1a3 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -3513,7 +3513,7 @@ test("x = { set method(val) v = val }", { locations: true }); -// Array Comprehension +// Array and Generator Comprehension test("[for (x of array) x]", { type: "Program", @@ -3558,6 +3558,7 @@ test("[for (x of array) x]", { end: {line: 1, column: 19} } }, + generator: false, range: [0, 20], loc: { start: {line: 1, column: 0}, @@ -3679,6 +3680,129 @@ test("[for (x of array) for (y of array2) if (x === test) x]", { end: {line: 1, column: 53} } }, + generator: false, + range: [0, 54], + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 54} + } + }, + range: [0, 54], + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 54} + } + }], + range: [0, 54], + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 54} + } +}, { + ecmaVersion: 6, + ranges: true, + locations: true +}); + +test("(for (x of array) for (y of array2) if (x === test) x)", { + type: "Program", + body: [{ + type: "ExpressionStatement", + expression: { + type: "ComprehensionExpression", + filter: { + type: "BinaryExpression", + operator: "===", + left: { + type: "Identifier", + name: "x", + range: [40, 41], + loc: { + start: {line: 1, column: 40}, + end: {line: 1, column: 41} + } + }, + right: { + type: "Identifier", + name: "test", + range: [46, 50], + loc: { + start: {line: 1, column: 46}, + end: {line: 1, column: 50} + } + }, + range: [40, 50], + loc: { + start: {line: 1, column: 40}, + end: {line: 1, column: 50} + } + }, + blocks: [ + { + type: "ComprehensionBlock", + left: { + type: "Identifier", + name: "x", + range: [6, 7], + loc: { + start: {line: 1, column: 6}, + end: {line: 1, column: 7} + } + }, + right: { + type: "Identifier", + name: "array", + range: [11, 16], + loc: { + start: {line: 1, column: 11}, + end: {line: 1, column: 16} + } + }, + range: [1, 17], + loc: { + start: {line: 1, column: 1}, + end: {line: 1, column: 17} + }, + of: true + }, + { + type: "ComprehensionBlock", + left: { + type: "Identifier", + name: "y", + range: [23, 24], + loc: { + start: {line: 1, column: 23}, + end: {line: 1, column: 24} + } + }, + right: { + type: "Identifier", + name: "array2", + range: [28, 34], + loc: { + start: {line: 1, column: 28}, + end: {line: 1, column: 34} + } + }, + range: [18, 35], + loc: { + start: {line: 1, column: 18}, + end: {line: 1, column: 35} + }, + of: true + } + ], + body: { + type: "Identifier", + name: "x", + range: [52, 53], + loc: { + start: {line: 1, column: 52}, + end: {line: 1, column: 53} + } + }, + generator: true, range: [0, 54], loc: { start: {line: 1, column: 0}, @@ -3882,6 +4006,7 @@ test("[for ([,x] of array) for ({[start.x]: x, [start.y]: y} of array2) x]", { end: {line: 1, column: 67} } }, + generator: false, range: [0, 68], loc: { start: {line: 1, column: 0},