Array Comprehensions as per latest ES6 drafts.

This commit is contained in:
Ingvar Stepanyan 2014-07-25 18:45:04 +03:00 committed by Marijn Haverbeke
parent dd37866fdb
commit bcc97e2d52
2 changed files with 78 additions and 434 deletions

View File

@ -1174,8 +1174,10 @@
break; break;
case "ArrayPattern": case "ArrayPattern":
for (var i = 0; i < expr.elements.length; i++) for (var i = 0; i < expr.elements.length; i++) {
checkLVal(expr.elements[i], isBinding); var elem = expr.elements[i];
if (elem) checkLVal(elem, isBinding);
}
break; break;
case "SpreadElement": case "SpreadElement":
@ -1731,10 +1733,11 @@
val = parseExpression(); val = parseExpression();
} }
expect(_parenR); expect(_parenR);
// if '=>' follows '(...)', convert contents to arguments
if (metParenL === oldParenL && eat(_arrow)) { if (metParenL === oldParenL && eat(_arrow)) {
val = parseArrowExpression(node, !val ? [] : val.type === "SequenceExpression" ? val.expressions : [val]); val = parseArrowExpression(node, !val ? [] : val.type === "SequenceExpression" ? val.expressions : [val]);
} else { } else {
// disallow '()' before everything but error // forbid '()' before everything but '=>'
if (!val) unexpected(lastStart); if (!val) unexpected(lastStart);
} }
val.start = tokStart1; val.start = tokStart1;
@ -1751,6 +1754,28 @@
case _bracketL: case _bracketL:
var node = startNode(); var node = startNode();
next(); 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);
expect(_of);
// `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");
}
node.elements = parseExprList(_bracketR, true, true); node.elements = parseExprList(_bracketR, true, true);
return finishNode(node, "ArrayExpression"); return finishNode(node, "ArrayExpression");

View File

@ -3513,420 +3513,47 @@ test("x = { set method(val) v = val }", {
// Array Comprehension // Array Comprehension
test("[[x,b,c] for (x in []) for (b in []) if (b && c)]", { test("[for (x of array) x]", {
type: "Program", type: "Program",
body: [{ body: [{
type: "ExpressionStatement", type: "ExpressionStatement",
expression: { expression: {
type: "ComprehensionExpression", type: "ComprehensionExpression",
filter: { filter: null,
type: "LogicalExpression", blocks: [{
operator: "&&",
left: {
type: "Identifier",
name: "b",
range: [41, 42],
loc: {
start: {line: 1, column: 41},
end: {line: 1, column: 42}
}
},
right: {
type: "Identifier",
name: "c",
range: [46, 47],
loc: {
start: {line: 1, column: 46},
end: {line: 1, column: 47}
}
},
range: [41, 47],
loc: {
start: {line: 1, column: 41},
end: {line: 1, column: 47}
}
},
blocks: [
{
type: "ComprehensionBlock", type: "ComprehensionBlock",
left: { left: {
type: "Identifier", type: "Identifier",
name: "x", name: "x",
range: [14, 15],
loc: {
start: {line: 1, column: 14},
end: {line: 1, column: 15}
}
},
right: {
type: "ArrayExpression",
elements: [],
range: [19, 21],
loc: {
start: {line: 1, column: 19},
end: {line: 1, column: 21}
}
},
each: false,
range: [9, 22],
loc: {
start: {line: 1, column: 9},
end: {line: 1, column: 22}
},
of: false
},
{
type: "ComprehensionBlock",
left: {
type: "Identifier",
name: "b",
range: [28, 29],
loc: {
start: {line: 1, column: 28},
end: {line: 1, column: 29}
}
},
right: {
type: "ArrayExpression",
elements: [],
range: [33, 35],
loc: {
start: {line: 1, column: 33},
end: {line: 1, column: 35}
}
},
each: false,
range: [23, 36],
loc: {
start: {line: 1, column: 23},
end: {line: 1, column: 36}
},
of: false
}
],
body: {
type: "ArrayExpression",
elements: [
{
type: "Identifier",
name: "x",
range: [2, 3],
loc: {
start: {line: 1, column: 2},
end: {line: 1, column: 3}
}
},
{
type: "Identifier",
name: "b",
range: [4, 5],
loc: {
start: {line: 1, column: 4},
end: {line: 1, column: 5}
}
},
{
type: "Identifier",
name: "c",
range: [6, 7], range: [6, 7],
loc: { loc: {
start: {line: 1, column: 6}, start: {line: 1, column: 6},
end: {line: 1, column: 7} end: {line: 1, column: 7}
} }
}
],
range: [1, 8],
loc: {
start: {line: 1, column: 1},
end: {line: 1, column: 8}
}
},
range: [0, 49],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 49}
}
},
range: [0, 49],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 49}
}
}],
range: [0, 49],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 49}
}
}, {
ecmaVersion: 6,
ranges: true,
locations: true
});
test("[x for (a in [])]", {
type: "Program",
body: [{
type: "ExpressionStatement",
expression: {
type: "ComprehensionExpression",
filter: null,
blocks: [{
type: "ComprehensionBlock",
left: {
type: "Identifier",
name: "a",
range: [8, 9],
loc: {
start: {line: 1, column: 8},
end: {line: 1, column: 9}
}
},
right: {
type: "ArrayExpression",
elements: [],
range: [13, 15],
loc: {
start: {line: 1, column: 13},
end: {line: 1, column: 15}
}
},
each: false,
range: [3, 16],
loc: {
start: {line: 1, column: 3},
end: {line: 1, column: 16}
},
of: false
}],
body: {
type: "Identifier",
name: "x",
range: [1, 2],
loc: {
start: {line: 1, column: 1},
end: {line: 1, column: 2}
}
},
range: [0, 17],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 17}
}
},
range: [0, 17],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 17}
}
}],
range: [0, 17],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 17}
}
}, {
ecmaVersion: 6,
ranges: true,
locations: true
});
test("[1 for (x in [])]", {
type: "Program",
body: [{
type: "ExpressionStatement",
expression: {
type: "ComprehensionExpression",
filter: null,
blocks: [{
type: "ComprehensionBlock",
left: {
type: "Identifier",
name: "x",
range: [8, 9],
loc: {
start: {line: 1, column: 8},
end: {line: 1, column: 9}
}
},
right: {
type: "ArrayExpression",
elements: [],
range: [13, 15],
loc: {
start: {line: 1, column: 13},
end: {line: 1, column: 15}
}
},
each: false,
range: [3, 16],
loc: {
start: {line: 1, column: 3},
end: {line: 1, column: 16}
},
of: false
}],
body: {
type: "Literal",
value: 1,
raw: "1",
range: [1, 2],
loc: {
start: {line: 1, column: 1},
end: {line: 1, column: 2}
}
},
range: [0, 17],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 17}
}
},
range: [0, 17],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 17}
}
}],
range: [0, 17],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 17}
}
}, {
ecmaVersion: 6,
ranges: true,
locations: true
});
test("[(x,1) for (x in [])]", {
type: "Program",
body: [{
type: "ExpressionStatement",
expression: {
type: "ComprehensionExpression",
filter: null,
blocks: [{
type: "ComprehensionBlock",
left: {
type: "Identifier",
name: "x",
range: [12, 13],
loc: {
start: {line: 1, column: 12},
end: {line: 1, column: 13}
}
},
right: {
type: "ArrayExpression",
elements: [],
range: [17, 19],
loc: {
start: {line: 1, column: 17},
end: {line: 1, column: 19}
}
},
each: false,
range: [7, 20],
loc: {
start: {line: 1, column: 7},
end: {line: 1, column: 20}
},
of: false
}],
body: {
type: "SequenceExpression",
expressions: [
{
type: "Identifier",
name: "x",
range: [2, 3],
loc: {
start: {line: 1, column: 2},
end: {line: 1, column: 3}
}
},
{
type: "Literal",
value: 1,
raw: "1",
range: [4, 5],
loc: {
start: {line: 1, column: 4},
end: {line: 1, column: 5}
}
}
],
range: [2, 5],
loc: {
start: {line: 1, column: 2},
end: {line: 1, column: 5}
}
},
range: [0, 21],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 21}
}
},
range: [0, 21],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 21}
}
}],
range: [0, 21],
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 21}
}
}, {
ecmaVersion: 6,
ranges: true,
locations: true
});
test("[x for (x of array)]", {
type: "Program",
body: [{
type: "ExpressionStatement",
expression: {
type: "ComprehensionExpression",
filter: null,
blocks: [{
type: "ComprehensionBlock",
left: {
type: "Identifier",
name: "x",
range: [8, 9],
loc: {
start: {line: 1, column: 8},
end: {line: 1, column: 9}
}
}, },
right: { right: {
type: "Identifier", type: "Identifier",
name: "array", name: "array",
range: [13, 18], range: [11, 16],
loc: { loc: {
start: {line: 1, column: 13}, start: {line: 1, column: 11},
end: {line: 1, column: 18} end: {line: 1, column: 16}
} }
}, },
range: [3, 19], range: [1, 17],
loc: { loc: {
start: {line: 1, column: 3}, start: {line: 1, column: 1},
end: {line: 1, column: 19} end: {line: 1, column: 17}
}, },
of: true of: true
}], }],
body: { body: {
type: "Identifier", type: "Identifier",
name: "x", name: "x",
range: [1, 2], range: [18, 19],
loc: { loc: {
start: {line: 1, column: 1}, start: {line: 1, column: 18},
end: {line: 1, column: 2} end: {line: 1, column: 19}
} }
}, },
range: [0, 20], range: [0, 20],
@ -3952,7 +3579,7 @@ test("[x for (x of array)]", {
locations: true locations: true
}); });
test("[x for (x of array) for (y of array2) if (x === test)]", { test("[for (x of array) for (y of array2) if (x === test) x]", {
type: "Program", type: "Program",
body: [{ body: [{
type: "ExpressionStatement", type: "ExpressionStatement",
@ -3964,25 +3591,25 @@ test("[x for (x of array) for (y of array2) if (x === test)]", {
left: { left: {
type: "Identifier", type: "Identifier",
name: "x", name: "x",
range: [42, 43], range: [40, 41],
loc: { loc: {
start: {line: 1, column: 42}, start: {line: 1, column: 40},
end: {line: 1, column: 43} end: {line: 1, column: 41}
} }
}, },
right: { right: {
type: "Identifier", type: "Identifier",
name: "test", name: "test",
range: [48, 52], range: [46, 50],
loc: { loc: {
start: {line: 1, column: 48}, start: {line: 1, column: 46},
end: {line: 1, column: 52} end: {line: 1, column: 50}
} }
}, },
range: [42, 52], range: [40, 50],
loc: { loc: {
start: {line: 1, column: 42}, start: {line: 1, column: 40},
end: {line: 1, column: 52} end: {line: 1, column: 50}
} }
}, },
blocks: [ blocks: [
@ -3991,25 +3618,25 @@ test("[x for (x of array) for (y of array2) if (x === test)]", {
left: { left: {
type: "Identifier", type: "Identifier",
name: "x", name: "x",
range: [8, 9], range: [6, 7],
loc: { loc: {
start: {line: 1, column: 8}, start: {line: 1, column: 6},
end: {line: 1, column: 9} end: {line: 1, column: 7}
} }
}, },
right: { right: {
type: "Identifier", type: "Identifier",
name: "array", name: "array",
range: [13, 18], range: [11, 16],
loc: { loc: {
start: {line: 1, column: 13}, start: {line: 1, column: 11},
end: {line: 1, column: 18} end: {line: 1, column: 16}
} }
}, },
range: [3, 19], range: [1, 17],
loc: { loc: {
start: {line: 1, column: 3}, start: {line: 1, column: 1},
end: {line: 1, column: 19} end: {line: 1, column: 17}
}, },
of: true of: true
}, },
@ -4018,25 +3645,25 @@ test("[x for (x of array) for (y of array2) if (x === test)]", {
left: { left: {
type: "Identifier", type: "Identifier",
name: "y", name: "y",
range: [25, 26], range: [23, 24],
loc: { loc: {
start: {line: 1, column: 25}, start: {line: 1, column: 23},
end: {line: 1, column: 26} end: {line: 1, column: 24}
} }
}, },
right: { right: {
type: "Identifier", type: "Identifier",
name: "array2", name: "array2",
range: [30, 36], range: [28, 34],
loc: { loc: {
start: {line: 1, column: 30}, start: {line: 1, column: 28},
end: {line: 1, column: 36} end: {line: 1, column: 34}
} }
}, },
range: [20, 37], range: [18, 35],
loc: { loc: {
start: {line: 1, column: 20}, start: {line: 1, column: 18},
end: {line: 1, column: 37} end: {line: 1, column: 35}
}, },
of: true of: true
} }
@ -4044,10 +3671,10 @@ test("[x for (x of array) for (y of array2) if (x === test)]", {
body: { body: {
type: "Identifier", type: "Identifier",
name: "x", name: "x",
range: [1, 2], range: [52, 53],
loc: { loc: {
start: {line: 1, column: 1}, start: {line: 1, column: 52},
end: {line: 1, column: 2} end: {line: 1, column: 53}
} }
}, },
range: [0, 54], range: [0, 54],
@ -15921,8 +15548,6 @@ 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("\"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:44)", {ecmaVersion: 6});
testFail("for (let x = 42 in list) process(x);", "Unexpected token (1:16)", {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:16)", {ecmaVersion: 6}); testFail("for (let x = 42 of list) process(x);", "Unexpected token (1:16)", {ecmaVersion: 6});
@ -16194,23 +15819,17 @@ testFail("`hello ${10;test`", "Unexpected token (1:12)", {ecmaVersion: 6});
testFail("function a() 1 // expression closure is not supported", "Unexpected token (1:13)", {ecmaVersion: 6}); testFail("function a() 1 // expression closure is not supported", "Unexpected token (1:13)", {ecmaVersion: 6});
testFail("[a,b if (a)] // (a,b)", "Unexpected token (1:5)", {ecmaVersion: 6}); testFail("[for (let x of []) x]", "Unexpected token (1:6)", {ecmaVersion: 6});
testFail("for each (let x in {}) {};", "Unexpected token (1:4)", {ecmaVersion: 6}); testFail("[for (const x of []) x]", "Unexpected token (1:6)", {ecmaVersion: 6});
testFail("[x for (let x in [])]", "Unexpected token (1:21)", {ecmaVersion: 6}); testFail("[for (var x of []) x]", "Unexpected token (1:6)", {ecmaVersion: 6});
testFail("[x for (const x in [])]", "Unexpected token (1:23)", {ecmaVersion: 6}); testFail("[for (a in []) x] // (a,b) ", "Unexpected token (1:8)", {ecmaVersion: 6});
testFail("[x for (var x in [])]", "Unexpected token (1:21)", {ecmaVersion: 6}); testFail("var a = [if (x) x]", "Unexpected token (1:9)", {ecmaVersion: 6});
testFail("[a,b for (a in [])] // (a,b) ", "Unexpected token (1:5)", {ecmaVersion: 6}); testFail("[for (x of [])] // no expression", "Unexpected token (1:14)", {ecmaVersion: 6});
testFail("[x if (x)] // block required", "Unexpected token (1:11)", {ecmaVersion: 6});
testFail("var a = [x if (x)]", "Unexpected token (1:19)", {ecmaVersion: 6});
testFail("[for (x in [])] // no espression", "Unexpected token (1:16)", {ecmaVersion: 6});
testFail("({ \"chance\" }) = obj", "Unexpected token (1:12)", {ecmaVersion: 6}); testFail("({ \"chance\" }) = obj", "Unexpected token (1:12)", {ecmaVersion: 6});