Added Generator Comprehension support.

This commit is contained in:
Ingvar Stepanyan 2014-07-27 03:54:04 +03:00 committed by Marijn Haverbeke
parent 1c548e7848
commit 9d552efe45
2 changed files with 176 additions and 39 deletions

View File

@ -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");
}
});

View File

@ -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},