From 34050d3917aa8f1bd2e430015d692e2e60e338bd Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 10 Mar 2015 11:36:15 +0200 Subject: [PATCH] Support for `for (const ...)`. Fixes #213. Also changes API to pass token type to `parseVar` to reduce string comparison ops. --- acorn.js | 18 ++++++++++------ acorn_loose.js | 2 +- test/tests-harmony.js | 49 +++++++++++++++++++++++++++++++++++++++++++ test/tests.js | 34 +++++++++++++++++++++++++++++- 4 files changed, 95 insertions(+), 8 deletions(-) diff --git a/acorn.js b/acorn.js index d50d0cf428..570813e3f1 100644 --- a/acorn.js +++ b/acorn.js @@ -1773,7 +1773,7 @@ case tt._throw: return this.parseThrowStatement(node); case tt._try: return this.parseTryStatement(node); case tt._let: case tt._const: if (!declaration) this.unexpected(); // NOTE: falls through to _var - case tt._var: return this.parseVarStatement(node, starttype.keyword); + case tt._var: return this.parseVarStatement(node, starttype); case tt._while: return this.parseWhileStatement(node); case tt._with: return this.parseWithStatement(node); case tt.braceL: return this.parseBlock(); // no point creating a function for this @@ -1853,13 +1853,13 @@ this.labels.push(loopLabel); this.expect(tt.parenL); if (this.type === tt.semi) return this.parseFor(node, null); - if (this.type === tt._var || this.type === tt._let) { - var init = this.startNode(), varKind = this.type.keyword, isLet = this.type === tt._let; + if (this.type === tt._var || this.type === tt._let || this.type === tt._const) { + var init = this.startNode(), varKind = this.type; this.next(); this.parseVar(init, true, varKind); this.finishNode(init, "VariableDeclaration"); if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 && - !(isLet && init.declarations[0].init)) + !(varKind !== tt._var && init.declarations[0].init)) return this.parseForIn(node, init); return this.parseFor(node, init); } @@ -2079,12 +2079,18 @@ pp.parseVar = function(node, noIn, kind) { node.declarations = []; - node.kind = kind; + node.kind = kind.keyword; for (;;) { var decl = this.startNode(); decl.id = this.parseBindingAtom(); this.checkLVal(decl.id, true); - decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : (kind === tt._const.keyword ? this.unexpected() : null); + if (this.eat(tt.eq)) { + decl.init = this.parseMaybeAssign(noIn); + } else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) { + this.unexpected(); + } else { + decl.init = null; + } node.declarations.push(this.finishNode(decl, "VariableDeclarator")); if (!this.eat(tt.comma)) break; } diff --git a/acorn_loose.js b/acorn_loose.js index 7d6cd8be84..c936b4f0e9 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -346,7 +346,7 @@ this.pushCx(); this.expect(tt.parenL); if (this.tok.type === tt.semi) return this.parseFor(node, null); - if (this.tok.type === tt._var || this.tok.type === tt._let) { + if (this.tok.type === tt._var || this.tok.type === tt._let || this.tok.type === tt._const) { var init = this.parseVar(true); if (init.declarations.length === 1 && (this.tok.type === tt._in || this.isContextual("of"))) { return this.parseForIn(node, init); diff --git a/test/tests-harmony.js b/test/tests-harmony.js index c155978041..552b98dc3c 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -14970,6 +14970,55 @@ test("class A { static() {} }", { locations: true }); +// https://github.com/marijnh/acorn/issues/213 + +test("for (const x of list) process(x);", { + type: "Program", + body: [{ + type: "ForOfStatement", + left: { + type: "VariableDeclaration", + declarations: [{ + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + range: [11, 12] + }, + init: null, + range: [11, 12] + }], + kind: "const", + range: [5, 12] + }, + right: { + type: "Identifier", + name: "list", + range: [16, 20] + }, + body: { + type: "ExpressionStatement", + expression: { + type: "CallExpression", + callee: { + type: "Identifier", + name: "process", + range: [22, 29] + }, + arguments: [{ + type: "Identifier", + name: "x", + range: [30, 31] + }], + range: [22, 32] + }, + range: [22, 33] + }, + range: [0, 33] + }], + range: [0, 33] +}, {ecmaVersion: 6, ranges: true}); + test("class A { *static() {} }", { type: "Program", range: [0, 24], diff --git a/test/tests.js b/test/tests.js index 92f23f2ac4..c54e04ad47 100644 --- a/test/tests.js +++ b/test/tests.js @@ -28682,7 +28682,39 @@ test("const x = 14, y = 3, z = 1977", { testFail("const a;", "Unexpected token (1:7)", {ecmaVersion: 6}); -testFail("for(const x = 0;;);", "Unexpected token (1:4)", {ecmaVersion: 6}); +test("for(const x = 0;;);", { + type: "Program", + body: [{ + type: "ForStatement", + init: { + type: "VariableDeclaration", + declarations: [{ + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + range: [10, 11] + }, + init: { + type: "Literal", + value: 0, + range: [14, 15] + }, + range: [10, 15] + }], + kind: "const", + range: [4, 15] + }, + test: null, + update: null, + body: { + type: "EmptyStatement", + range: [18, 19] + }, + range: [0, 19] + }], + range: [0, 19] +}, {ecmaVersion: 6, ranges: true}); testFail("for(x of a);", "Unexpected token (1:6)");