Added for (..of..) support and guard against for (let var=.. in|of ..) loops.

This commit is contained in:
Ingvar Stepanyan 2014-07-25 16:14:11 +03:00 committed by Marijn Haverbeke
parent 8b0be7cec9
commit 648f3159cc
3 changed files with 25 additions and 400 deletions

View File

@ -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.

View File

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

View File

@ -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: [