From 722bc65e7937ce6557fdc08c290d15736b75ebb3 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 10 Mar 2015 02:27:24 +0200 Subject: [PATCH 01/43] [estree] Align Import specifiers. --- acorn.js | 21 +++++------- acorn_loose.js | 52 ++++++++++++++++++++++------- test/tests-harmony.js | 78 +++++++++++++++++++++++++------------------ 3 files changed, 95 insertions(+), 56 deletions(-) diff --git a/acorn.js b/acorn.js index add13940b2..01d54b4870 100644 --- a/acorn.js +++ b/acorn.js @@ -2785,20 +2785,18 @@ if (this.type === tt.name) { // import defaultObj, { x, y as z } from '...' var node = this.startNode(); - node.id = this.parseIdent(); - this.checkLVal(node.id, true); - node.name = null; - node['default'] = true; - nodes.push(this.finishNode(node, "ImportSpecifier")); + node.local = this.parseIdent(); + this.checkLVal(node.local, true); + nodes.push(this.finishNode(node, "ImportDefaultSpecifier")); if (!this.eat(tt.comma)) return nodes; } if (this.type === tt.star) { var node = this.startNode(); this.next(); this.expectContextual("as"); - node.name = this.parseIdent(); - this.checkLVal(node.name, true); - nodes.push(this.finishNode(node, "ImportBatchSpecifier")); + node.local = this.parseIdent(); + this.checkLVal(node.local, true); + nodes.push(this.finishNode(node, "ImportNamespaceSpecifier")); return nodes; } this.expect(tt.braceL); @@ -2809,10 +2807,9 @@ } else first = false; var node = this.startNode(); - node.id = this.parseIdent(true); - node.name = this.eatContextual("as") ? this.parseIdent() : null; - this.checkLVal(node.name || node.id, true); - node['default'] = false; + node.imported = this.parseIdent(true); + node.local = this.eatContextual("as") ? this.parseIdent() : node.imported; + this.checkLVal(node.local, true); nodes.push(this.finishNode(node, "ImportSpecifier")); } return nodes; diff --git a/acorn_loose.js b/acorn_loose.js index 67236abbb7..6edf3b1f0c 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -1058,7 +1058,7 @@ node.declaration = this.parseStatement(); } else { node.declaration = null; - this.parseSpecifierList(node, "Export"); + this.parseExportSpecifierList(node); } this.semicolon(); return this.finishNode(node, "ExportDeclaration"); @@ -1074,28 +1074,56 @@ } else { if (this.tok.type === tt.name && this.tok.value !== "from") { var elt = this.startNode(); - elt.id = this.parseIdent(); - elt.name = null; - elt['default'] = true; - this.finishNode(elt, "ImportSpecifier"); + elt.local = this.parseIdent(); + this.finishNode(elt, "ImportDefaultSpecifier"); this.eat(tt.comma); } - this.parseSpecifierList(node, "Import"); - var specs = node.specifiers; - for (var i = 0; i < specs.length; i++) specs[i]['default'] = false; + this.parseImportSpecifierList(node); if (elt) node.specifiers.unshift(elt); } this.semicolon(); return this.finishNode(node, "ImportDeclaration"); }; - lp.parseSpecifierList = function(node, prefix) { + lp.parseImportSpecifierList = function(node) { + var elts = node.specifiers = []; + if (this.tok.type === tt.star) { + var elt = this.startNode(); + this.next(); + if (this.eatContextual("as")) elt.local = this.parseIdent(); + elts.push(this.finishNode(elt, "ImportNamespaceSpecifier")); + } else { + var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart; + this.pushCx(); + this.eat(tt.braceL); + if (this.curLineStart > continuedLine) continuedLine = this.curLineStart; + while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) { + var elt = this.startNode(); + if (this.eat(tt.star)) { + if (this.eatContextual("as")) elt.local = this.parseIdent(); + this.finishNode(elt, "ImportNamespaceSpecifier"); + } else { + if (this.isContextual("from")) break; + elt.imported = this.parseIdent(); + elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported; + this.finishNode(elt, "ImportSpecifier"); + } + elts.push(elt); + this.eat(tt.comma); + } + this.eat(tt.braceR); + this.popCx(); + } + node.source = this.eatContextual("from") ? this.parseExprAtom() : null; + }; + + lp.parseExportSpecifierList = function(node) { var elts = node.specifiers = []; if (this.tok.type === tt.star) { var elt = this.startNode(); this.next(); if (this.eatContextual("as")) elt.name = this.parseIdent(); - elts.push(this.finishNode(elt, prefix + "BatchSpecifier")); + elts.push(this.finishNode(elt, "ExportBatchSpecifier")); } else { var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart; this.pushCx(); @@ -1105,12 +1133,12 @@ var elt = this.startNode(); if (this.eat(tt.star)) { if (this.eatContextual("as")) elt.name = this.parseIdent(); - this.finishNode(elt, prefix + "BatchSpecifier"); + this.finishNode(elt, "ExportBatchSpecifier"); } else { if (this.isContextual("from")) break; elt.id = this.parseIdent(); elt.name = this.eatContextual("as") ? this.parseIdent() : null; - this.finishNode(elt, prefix + "Specifier"); + this.finishNode(elt, "ExportSpecifier"); } elts.push(elt); this.eat(tt.comma); diff --git a/test/tests-harmony.js b/test/tests-harmony.js index 8e3dd24c13..48a613207e 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -5253,8 +5253,8 @@ test("import $ from \"jquery\"", { body: [{ type: "ImportDeclaration", specifiers: [{ - type: "ImportSpecifier", - id: { + type: "ImportDefaultSpecifier", + local: { type: "Identifier", name: "$", loc: { @@ -5262,7 +5262,6 @@ test("import $ from \"jquery\"", { end: {line: 1, column: 8} } }, - name: null, loc: { start: {line: 1, column: 7}, end: {line: 1, column: 8} @@ -5299,7 +5298,15 @@ test("import { encrypt, decrypt } from \"crypto\"", { specifiers: [ { type: "ImportSpecifier", - id: { + imported: { + type: "Identifier", + name: "encrypt", + loc: { + start: {line: 1, column: 9}, + end: {line: 1, column: 16} + } + }, + local: { type: "Identifier", name: "encrypt", loc: { @@ -5307,7 +5314,6 @@ test("import { encrypt, decrypt } from \"crypto\"", { end: {line: 1, column: 16} } }, - name: null, loc: { start: {line: 1, column: 9}, end: {line: 1, column: 16} @@ -5315,7 +5321,15 @@ test("import { encrypt, decrypt } from \"crypto\"", { }, { type: "ImportSpecifier", - id: { + imported: { + type: "Identifier", + name: "decrypt", + loc: { + start: {line: 1, column: 18}, + end: {line: 1, column: 25} + } + }, + local: { type: "Identifier", name: "decrypt", loc: { @@ -5323,7 +5337,6 @@ test("import { encrypt, decrypt } from \"crypto\"", { end: {line: 1, column: 25} } }, - name: null, loc: { start: {line: 1, column: 18}, end: {line: 1, column: 25} @@ -5360,7 +5373,7 @@ test("import { encrypt as enc } from \"crypto\"", { type: "ImportDeclaration", specifiers: [{ type: "ImportSpecifier", - id: { + imported: { type: "Identifier", name: "encrypt", loc: { @@ -5368,7 +5381,7 @@ test("import { encrypt as enc } from \"crypto\"", { end: {line: 1, column: 16} } }, - name: { + local: { type: "Identifier", name: "enc", loc: { @@ -5419,21 +5432,19 @@ test("import crypto, { decrypt, encrypt as enc } from \"crypto\"", { }, specifiers: [ { - type: "ImportSpecifier", + type: "ImportDefaultSpecifier", loc: { start: {line: 1, column: 7}, end: {line: 1, column: 13} }, - id: { + local: { type: "Identifier", loc: { start: {line: 1, column: 7}, end: {line: 1, column: 13} }, name: "crypto" - }, - name: null, - default: true + } }, { type: "ImportSpecifier", @@ -5441,7 +5452,7 @@ test("import crypto, { decrypt, encrypt as enc } from \"crypto\"", { start: {line: 1, column: 17}, end: {line: 1, column: 24} }, - id: { + imported: { type: "Identifier", loc: { start: {line: 1, column: 17}, @@ -5449,8 +5460,14 @@ test("import crypto, { decrypt, encrypt as enc } from \"crypto\"", { }, name: "decrypt" }, - name: null, - default: false + local: { + type: "Identifier", + loc: { + start: {line: 1, column: 17}, + end: {line: 1, column: 24} + }, + name: "decrypt" + } }, { type: "ImportSpecifier", @@ -5458,7 +5475,7 @@ test("import crypto, { decrypt, encrypt as enc } from \"crypto\"", { start: {line: 1, column: 26}, end: {line: 1, column: 40} }, - id: { + imported: { type: "Identifier", loc: { start: {line: 1, column: 26}, @@ -5466,15 +5483,14 @@ test("import crypto, { decrypt, encrypt as enc } from \"crypto\"", { }, name: "encrypt" }, - name: { + local: { type: "Identifier", loc: { start: {line: 1, column: 37}, end: {line: 1, column: 40} }, name: "enc" - }, - default: false + } } ], source: { @@ -5501,7 +5517,7 @@ test("import { null as nil } from \"bar\"", { type: "ImportDeclaration", specifiers: [{ type: "ImportSpecifier", - id: { + imported: { type: "Identifier", name: "null", loc: { @@ -5509,7 +5525,7 @@ test("import { null as nil } from \"bar\"", { end: {line: 1, column: 13} } }, - name: { + local: { type: "Identifier", name: "nil", loc: { @@ -5559,12 +5575,12 @@ test("import * as crypto from \"crypto\"", { end: {line: 1, column: 32} }, specifiers: [{ - type: "ImportBatchSpecifier", + type: "ImportNamespaceSpecifier", loc: { start: {line: 1, column: 7}, end: {line: 1, column: 18} }, - name: { + local: { type: "Identifier", loc: { start: {line: 1, column: 12}, @@ -14346,17 +14362,15 @@ test("import foo, * as bar from 'baz';", { type: "ImportDeclaration", specifiers: [ { - type: "ImportSpecifier", - id: { + type: "ImportDefaultSpecifier", + local: { type: "Identifier", name: "foo" - }, - name: null, - default: true + } }, { - type: "ImportBatchSpecifier", - name: { + type: "ImportNamespaceSpecifier", + local: { type: "Identifier", name: "bar" } From ff60ee4fdbe7d6cf1accdfba162adf3a5d43bcce Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 10 Mar 2015 10:37:00 +0200 Subject: [PATCH 02/43] [estree] Align `export ...` representations. --- acorn.js | 64 +++++++-------- acorn_loose.js | 74 +++++++++-------- test/tests-harmony.js | 179 ++++++++++++++++++++++-------------------- 3 files changed, 154 insertions(+), 163 deletions(-) diff --git a/acorn.js b/acorn.js index 01d54b4870..d50d0cf428 100644 --- a/acorn.js +++ b/acorn.js @@ -2694,15 +2694,13 @@ pp.parseExport = function(node) { this.next(); - // export var|const|let|function|class ...; - if (this.type === tt._var || this.type === tt._const || this.type === tt._let || this.type === tt._function || this.type === tt._class) { - node.declaration = this.parseStatement(true); - node['default'] = false; - node.specifiers = null; - node.source = null; - } else - // export default ...; - if (this.eat(tt._default)) { + // export * from '...'; + if (this.eat(tt.star)) { + this.expectContextual("from"); + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); + return this.finishNode(node, "ExportAllDeclaration"); + } + if (this.eat(tt._default)) { // export default ...; var expr = this.parseMaybeAssign(); if (expr.id) { switch (expr.type) { @@ -2711,51 +2709,43 @@ } } node.declaration = expr; - node['default'] = true; - node.specifiers = null; - node.source = null; this.semicolon(); - } else { - // export * from '...'; - // export { x, y as z } [from '...']; - var isBatch = this.type === tt.star; + return this.finishNode(node, "ExportDefaultDeclaration"); + } + // export var|const|let|function|class ...; + if (this.type.keyword) { + node.declaration = this.parseStatement(true); + node.specifiers = []; + node.source = null; + } else { // export { x, y as z } [from '...']; node.declaration = null; - node['default'] = false; node.specifiers = this.parseExportSpecifiers(); if (this.eatContextual("from")) { node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); } else { - if (isBatch) this.unexpected(); node.source = null; } this.semicolon(); } - return this.finishNode(node, "ExportDeclaration"); + return this.finishNode(node, "ExportNamedDeclaration"); }; // Parses a comma-separated list of module exports. pp.parseExportSpecifiers = function() { var nodes = [], first = true; - if (this.type === tt.star) { - // export * from '...' - var node = this.startNode(); - this.next(); - nodes.push(this.finishNode(node, "ExportBatchSpecifier")); - } else { - // export { x, y as z } [from '...'] - this.expect(tt.braceL); - while (!this.eat(tt.braceR)) { - if (!first) { - this.expect(tt.comma); - if (this.options.allowTrailingCommas && this.eat(tt.braceR)) break; - } else first = false; + // export { x, y as z } [from '...'] + this.expect(tt.braceL); + while (!this.eat(tt.braceR)) { + if (!first) { + this.expect(tt.comma); + if (this.options.allowTrailingCommas && this.eat(tt.braceR)) break; + } else first = false; - var node = this.startNode(); - node.id = this.parseIdent(this.type === tt._default); - node.name = this.eatContextual("as") ? this.parseIdent(true) : null; - nodes.push(this.finishNode(node, "ExportSpecifier")); - } + var node = this.startNode(); + node.local = this.parseIdent(this.type === tt._default); + node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local; + nodes.push(this.finishNode(node, "ExportSpecifier")); } return nodes; }; diff --git a/acorn_loose.js b/acorn_loose.js index 6edf3b1f0c..7d6cd8be84 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -1042,9 +1042,11 @@ lp.parseExport = function() { var node = this.startNode(); this.next(); - node['default'] = this.eat(tt._default); - node.specifiers = node.source = null; - if (node['default']) { + if (this.eat(tt.star)) { + node.source = this.eatContextual("from") ? this.parseExprAtom() : null; + return this.finishNode(node, "ExportAllDeclaration"); + } + if (this.eat(tt._default)) { var expr = this.parseMaybeAssign(); if (expr.id) { switch (expr.type) { @@ -1054,14 +1056,19 @@ } node.declaration = expr; this.semicolon(); - } else if (this.tok.type.keyword) { + return this.finishNode(node, "ExportDefaultDeclaration"); + } + if (this.tok.type.keyword) { node.declaration = this.parseStatement(); + node.specifiers = []; + node.source = null; } else { node.declaration = null; - this.parseExportSpecifierList(node); + node.specifiers = this.parseExportSpecifierList(); + node.source = this.eatContextual("from") ? this.parseExprAtom() : null; + this.semicolon(); } - this.semicolon(); - return this.finishNode(node, "ExportDeclaration"); + return this.finishNode(node, "ExportNamedDeclaration"); }; lp.parseImport = function() { @@ -1078,15 +1085,16 @@ this.finishNode(elt, "ImportDefaultSpecifier"); this.eat(tt.comma); } - this.parseImportSpecifierList(node); + node.specifiers = this.parseImportSpecifierList(); + node.source = this.eatContextual("from") ? this.parseExprAtom() : null; if (elt) node.specifiers.unshift(elt); } this.semicolon(); return this.finishNode(node, "ImportDeclaration"); }; - lp.parseImportSpecifierList = function(node) { - var elts = node.specifiers = []; + lp.parseImportSpecifierList = function() { + var elts = []; if (this.tok.type === tt.star) { var elt = this.startNode(); this.next(); @@ -1114,39 +1122,27 @@ this.eat(tt.braceR); this.popCx(); } - node.source = this.eatContextual("from") ? this.parseExprAtom() : null; + return elts; }; - lp.parseExportSpecifierList = function(node) { - var elts = node.specifiers = []; - if (this.tok.type === tt.star) { + lp.parseExportSpecifierList = function() { + var elts = []; + var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart; + this.pushCx(); + this.eat(tt.braceL); + if (this.curLineStart > continuedLine) continuedLine = this.curLineStart; + while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) { + if (this.isContextual("from")) break; var elt = this.startNode(); - this.next(); - if (this.eatContextual("as")) elt.name = this.parseIdent(); - elts.push(this.finishNode(elt, "ExportBatchSpecifier")); - } else { - var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart; - this.pushCx(); - this.eat(tt.braceL); - if (this.curLineStart > continuedLine) continuedLine = this.curLineStart; - while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) { - var elt = this.startNode(); - if (this.eat(tt.star)) { - if (this.eatContextual("as")) elt.name = this.parseIdent(); - this.finishNode(elt, "ExportBatchSpecifier"); - } else { - if (this.isContextual("from")) break; - elt.id = this.parseIdent(); - elt.name = this.eatContextual("as") ? this.parseIdent() : null; - this.finishNode(elt, "ExportSpecifier"); - } - elts.push(elt); - this.eat(tt.comma); - } - this.eat(tt.braceR); - this.popCx(); + elt.local = this.parseIdent(); + elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local; + this.finishNode(elt, "ExportSpecifier"); + elts.push(elt); + this.eat(tt.comma); } - node.source = this.eatContextual("from") ? this.parseExprAtom() : null; + this.eat(tt.braceR); + this.popCx(); + return elts; }; lp.parseExprList = function(close, allowEmpty) { diff --git a/test/tests-harmony.js b/test/tests-harmony.js index 48a613207e..c155978041 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -4453,7 +4453,7 @@ test("var {a:b} = {}", { test("export var document", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: { type: "VariableDeclaration", declarations: [{ @@ -4478,8 +4478,7 @@ test("export var document", { end: {line: 1, column: 19} } }, - default: false, - specifiers: null, + specifiers: [], source: null, loc: { start: {line: 1, column: 0}, @@ -4499,7 +4498,7 @@ test("export var document", { test("export var document = { }", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: { type: "VariableDeclaration", declarations: [{ @@ -4531,8 +4530,7 @@ test("export var document = { }", { end: {line: 1, column: 25} } }, - default: false, - specifiers: null, + specifiers: [], source: null, loc: { start: {line: 1, column: 0}, @@ -4552,7 +4550,7 @@ test("export var document = { }", { test("export let document", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: { type: "VariableDeclaration", declarations: [{ @@ -4577,8 +4575,7 @@ test("export let document", { end: {line: 1, column: 19} } }, - default: false, - specifiers: null, + specifiers: [], source: null, loc: { start: {line: 1, column: 0}, @@ -4598,7 +4595,7 @@ test("export let document", { test("export let document = { }", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: { type: "VariableDeclaration", declarations: [{ @@ -4630,8 +4627,7 @@ test("export let document = { }", { end: {line: 1, column: 25} } }, - default: false, - specifiers: null, + specifiers: [], source: null, loc: { start: {line: 1, column: 0}, @@ -4651,7 +4647,7 @@ test("export let document = { }", { test("export const document = { }", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: { type: "VariableDeclaration", declarations: [{ @@ -4683,8 +4679,7 @@ test("export const document = { }", { end: {line: 1, column: 27} } }, - default: false, - specifiers: null, + specifiers: [], source: null, loc: { start: {line: 1, column: 0}, @@ -4704,7 +4699,7 @@ test("export const document = { }", { test("export function parse() { }", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: { type: "FunctionDeclaration", id: { @@ -4731,8 +4726,7 @@ test("export function parse() { }", { end: {line: 1, column: 27} } }, - default: false, - specifiers: null, + specifiers: [], source: null, loc: { start: {line: 1, column: 0}, @@ -4752,7 +4746,7 @@ test("export function parse() { }", { test("export class Class {}", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: { type: "ClassDeclaration", id: { @@ -4777,8 +4771,7 @@ test("export class Class {}", { end: {line: 1, column: 21} } }, - default: false, - specifiers: null, + specifiers: [], source: null, loc: { start: {line: 1, column: 0}, @@ -4798,7 +4791,7 @@ test("export class Class {}", { test("export default 42", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportDefaultDeclaration", declaration: { type: "Literal", value: 42, @@ -4808,9 +4801,6 @@ test("export default 42", { end: {line: 1, column: 17} } }, - default: true, - specifiers: null, - source: null, loc: { start: {line: 1, column: 0}, end: {line: 1, column: 17} @@ -4830,7 +4820,7 @@ test("export default function () {}", { type: "Program", range: [0, 29], body: [{ - type: "ExportDeclaration", + type: "ExportDefaultDeclaration", range: [0, 29], declaration: { type: "FunctionExpression", @@ -4844,10 +4834,7 @@ test("export default function () {}", { range: [27, 29], body: [] } - }, - default: true, - specifiers: null, - source: null + } }] }, {ecmaVersion: 6, ranges: true}); @@ -4855,7 +4842,7 @@ test("export default function f() {}", { type: "Program", range: [0, 30], body: [{ - type: "ExportDeclaration", + type: "ExportDefaultDeclaration", range: [0, 30], declaration: { type: "FunctionDeclaration", @@ -4873,10 +4860,7 @@ test("export default function f() {}", { range: [28, 30], body: [] } - }, - default: true, - specifiers: null, - source: null + } }] }, {ecmaVersion: 6, ranges: true}); @@ -4884,7 +4868,7 @@ test("export default class {}", { type: "Program", range: [0, 23], body: [{ - type: "ExportDeclaration", + type: "ExportDefaultDeclaration", range: [0, 23], declaration: { type: "ClassExpression", @@ -4896,10 +4880,7 @@ test("export default class {}", { range: [21, 23], body: [] } - }, - default: true, - specifiers: null, - source: null + } }] }, {ecmaVersion: 6, ranges: true}); @@ -4907,7 +4888,7 @@ test("export default class A {}", { type: "Program", range: [0, 25], body: [{ - type: "ExportDeclaration", + type: "ExportDefaultDeclaration", range: [0, 25], declaration: { type: "ClassDeclaration", @@ -4923,10 +4904,7 @@ test("export default class A {}", { range: [23, 25], body: [] } - }, - default: true, - specifiers: null, - source: null + } }] }, {ecmaVersion: 6, ranges: true}); @@ -4935,15 +4913,7 @@ testFail("export *", "Unexpected token (1:8)", {ecmaVersion: 6}); test("export * from \"crypto\"", { type: "Program", body: [{ - type: "ExportDeclaration", - declaration: null, - specifiers: [{ - type: "ExportBatchSpecifier", - loc: { - start: {line: 1, column: 7}, - end: {line: 1, column: 8} - } - }], + type: "ExportAllDeclaration", source: { type: "Literal", value: "crypto", @@ -4971,11 +4941,19 @@ test("export * from \"crypto\"", { test("export { encrypt }", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: null, specifiers: [{ type: "ExportSpecifier", - id: { + exported: { + type: "Identifier", + name: "encrypt", + loc: { + start: {line: 1, column: 9}, + end: {line: 1, column: 16} + } + }, + local: { type: "Identifier", name: "encrypt", loc: { @@ -4983,7 +4961,6 @@ test("export { encrypt }", { end: {line: 1, column: 16} } }, - name: null, loc: { start: {line: 1, column: 9}, end: {line: 1, column: 16} @@ -5008,12 +4985,20 @@ test("export { encrypt }", { test("export { encrypt, decrypt }", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: null, specifiers: [ { type: "ExportSpecifier", - id: { + exported: { + type: "Identifier", + name: "encrypt", + loc: { + start: {line: 1, column: 9}, + end: {line: 1, column: 16} + } + }, + local: { type: "Identifier", name: "encrypt", loc: { @@ -5021,7 +5006,6 @@ test("export { encrypt, decrypt }", { end: {line: 1, column: 16} } }, - name: null, loc: { start: {line: 1, column: 9}, end: {line: 1, column: 16} @@ -5029,7 +5013,15 @@ test("export { encrypt, decrypt }", { }, { type: "ExportSpecifier", - id: { + exported: { + type: "Identifier", + name: "decrypt", + loc: { + start: {line: 1, column: 18}, + end: {line: 1, column: 25} + } + }, + local: { type: "Identifier", name: "decrypt", loc: { @@ -5037,7 +5029,6 @@ test("export { encrypt, decrypt }", { end: {line: 1, column: 25} } }, - name: null, loc: { start: {line: 1, column: 18}, end: {line: 1, column: 25} @@ -5063,19 +5054,11 @@ test("export { encrypt, decrypt }", { test("export { encrypt as default }", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: null, specifiers: [{ type: "ExportSpecifier", - id: { - type: "Identifier", - name: "encrypt", - loc: { - start: {line: 1, column: 9}, - end: {line: 1, column: 16} - } - }, - name: { + exported: { type: "Identifier", name: "default", loc: { @@ -5083,6 +5066,14 @@ test("export { encrypt as default }", { end: {line: 1, column: 27} } }, + local: { + type: "Identifier", + name: "encrypt", + loc: { + start: {line: 1, column: 9}, + end: {line: 1, column: 16} + } + }, loc: { start: {line: 1, column: 9}, end: {line: 1, column: 27} @@ -5107,12 +5098,20 @@ test("export { encrypt as default }", { test("export { encrypt, decrypt as dec }", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: null, specifiers: [ { type: "ExportSpecifier", - id: { + exported: { + type: "Identifier", + name: "encrypt", + loc: { + start: {line: 1, column: 9}, + end: {line: 1, column: 16} + } + }, + local: { type: "Identifier", name: "encrypt", loc: { @@ -5120,7 +5119,6 @@ test("export { encrypt, decrypt as dec }", { end: {line: 1, column: 16} } }, - name: null, loc: { start: {line: 1, column: 9}, end: {line: 1, column: 16} @@ -5128,15 +5126,7 @@ test("export { encrypt, decrypt as dec }", { }, { type: "ExportSpecifier", - id: { - type: "Identifier", - name: "decrypt", - loc: { - start: {line: 1, column: 18}, - end: {line: 1, column: 25} - } - }, - name: { + exported: { type: "Identifier", name: "dec", loc: { @@ -5144,6 +5134,14 @@ test("export { encrypt, decrypt as dec }", { end: {line: 1, column: 32} } }, + local: { + type: "Identifier", + name: "decrypt", + loc: { + start: {line: 1, column: 18}, + end: {line: 1, column: 25} + } + }, loc: { start: {line: 1, column: 18}, end: {line: 1, column: 32} @@ -5169,12 +5167,20 @@ test("export { encrypt, decrypt as dec }", { test("export { default } from \"other\"", { type: "Program", body: [{ - type: "ExportDeclaration", + type: "ExportNamedDeclaration", declaration: null, specifiers: [ { type: "ExportSpecifier", - id: { + exported: { + type: "Identifier", + name: "default", + loc: { + start: {line: 1, column: 9}, + end: {line: 1, column: 16} + } + }, + local: { type: "Identifier", name: "default", loc: { @@ -5182,7 +5188,6 @@ test("export { default } from \"other\"", { end: {line: 1, column: 16} } }, - name: null, loc: { start: {line: 1, column: 9}, end: {line: 1, column: 16} From 34050d3917aa8f1bd2e430015d692e2e60e338bd Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 10 Mar 2015 11:36:15 +0200 Subject: [PATCH 03/43] 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)"); From 9fb3a4f5ceae395e03a279f4673c8587f222f7a4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Mar 2015 21:40:12 +0100 Subject: [PATCH 04/43] Move as much as possible code in test driver out of try block --- test/driver.js | 63 +++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/test/driver.js b/test/driver.js index 6de53dc9e3..d8998b930b 100644 --- a/test/driver.js +++ b/test/driver.js @@ -17,43 +17,17 @@ for (var i = 0; i < tests.length; ++i) { var test = tests[i]; if (config.filter && !config.filter(test)) continue; + var testOpts = test.options || {locations: true}; + var expected = {}; + if (expected.onComment = testOpts.onComment) + testOpts.onComment = [] + if (expected.onToken = testOpts.onToken) + testOpts.onToken = []; + try { - var testOpts = test.options || {locations: true}; - var expected = {}; - if (expected.onComment = testOpts.onComment) { - testOpts.onComment = [] - } - if (expected.onToken = testOpts.onToken) { - testOpts.onToken = []; - } var ast = parse(test.code, testOpts); - if (test.error) { - if (config.loose) { - callback("ok", test.code); - } else { - callback("fail", test.code, "Expected error message: " + test.error + "\nBut parsing succeeded."); - } - } else if (test.assert) { - var error = test.assert(ast); - if (error) callback("fail", test.code, - "\n Assertion failed:\n " + error); - else callback("ok", test.code); - } else { - var mis = misMatch(test.ast, ast); - for (var name in expected) { - if (mis) break; - if (expected[name]) { - mis = misMatch(expected[name], testOpts[name]); - testOpts[name] = expected[name]; - } - } - if (mis) callback("fail", test.code, mis); - else callback("ok", test.code); - } } catch(e) { - if (!(e instanceof SyntaxError)) { - throw e; - } + if (!(e instanceof SyntaxError)) throw e; if (test.error) { if (e.message == test.error) callback("ok", test.code); else callback("fail", test.code, @@ -61,6 +35,27 @@ } else { callback("error", test.code, e.message || e.toString()); } + continue + } + + if (test.error) { + if (config.loose) callback("ok", test.code); + else callback("fail", test.code, "Expected error message: " + test.error + "\nBut parsing succeeded."); + } else if (test.assert) { + var error = test.assert(ast); + if (error) callback("fail", test.code, "\n Assertion failed:\n " + error); + else callback("ok", test.code); + } else { + var mis = misMatch(test.ast, ast); + for (var name in expected) { + if (mis) break; + if (expected[name]) { + mis = misMatch(expected[name], testOpts[name]); + testOpts[name] = expected[name]; + } + } + if (mis) callback("fail", test.code, mis); + else callback("ok", test.code); } } }; From ba750b253b26662b69b2aa932b116ffc56b92ee6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Mar 2015 21:57:40 +0100 Subject: [PATCH 05/43] Replace strictSemicolons and allowTrailingCommas with onInsertedSemicolon and onTrailingComma --- README.md | 12 ++++++----- acorn.js | 56 +++++++++++++++++++++++++++++++++++---------------- test/tests.js | 19 +++++++++++++++++ 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 0b0d841c34..1a8afd8e86 100644 --- a/README.md +++ b/README.md @@ -54,12 +54,14 @@ object referring to that same position. either 3, 5, or 6. This influences support for strict mode, the set of reserved words, and support for new syntax features. Default is 5. -- **strictSemicolons**: If `true`, prevents the parser from doing - automatic semicolon insertion, and statements that do not end with - a semicolon will generate an error. Defaults to `false`. +- **onInsertedSemicolon**: If given a callback, that callback will be + called whenever a missing semicolon is inserted by the parser. The + callback will be given the character offset of the point where the + semicolon is inserted as argument, and if `locations` is on, also a + `{line, column}` object representing this position. -- **allowTrailingCommas**: If `false`, the parser will not allow - trailing commas in array and object literals. Default is `true`. +- **onTrailingComma**: Like `onInsertedSemicolon`, but for trailing + commas. - **forbidReserved**: If `true`, using a reserved word will generate an error. Defaults to `false`. When given the value `"everywhere"`, diff --git a/acorn.js b/acorn.js index 570813e3f1..8eb6feb988 100644 --- a/acorn.js +++ b/acorn.js @@ -51,12 +51,15 @@ // mode, the set of reserved words, support for getters and // setters and other features. ecmaVersion: 5, - // Turn on `strictSemicolons` to prevent the parser from doing - // automatic semicolon insertion. - strictSemicolons: false, - // When `allowTrailingCommas` is false, the parser will not allow - // trailing commas in array and object literals. - allowTrailingCommas: true, + // `onInsertedSemicolon` can be a callback that will be called + // when a semicolon is automatically inserted. It will be passed + // th position of the comma as an offset, and if `locations` is + // enabled, it is given the location as a `{line, column}` object + // as second argument. + onInsertedSemicolon: null, + // `onTrailingComma` is similar to `onInsertedSemicolon`, but for + // trailing commas. + onTrailingComma: null, // By default, reserved words are not enforced. Enable // `forbidReserved` to enforce them. When this option has the // value "everywhere", reserved words and keywords can also not be @@ -566,7 +569,7 @@ this.startLoc = this.endLoc = null; // Position information for the previous token - this.lastTokEndLoc = null; + this.lastTokEndLoc = this.lastTokStartLoc = null; this.lastTokStart = this.lastTokEnd = this.pos; // The context stack is used to superficially track syntactic @@ -611,6 +614,7 @@ this.lastTokEnd = this.end; this.lastTokStart = this.start; this.lastTokEndLoc = this.endLoc; + this.lastTokStartLoc = this.startLoc; this.nextToken(); }; @@ -1453,15 +1457,33 @@ // Test whether a semicolon can be inserted at the current position. pp.canInsertSemicolon = function() { - return !this.options.strictSemicolons && - (this.type === tt.eof || this.type === tt.braceR || newline.test(this.input.slice(this.lastTokEnd, this.start))); + return this.type === tt.eof || + this.type === tt.braceR || + newline.test(this.input.slice(this.lastTokEnd, this.start)); + }; + + pp.insertSemicolon = function() { + if (this.canInsertSemicolon()) { + if (this.options.onInsertedSemicolon) + this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc) + return true; + } }; // Consume a semicolon, or, failing that, see if we are allowed to // pretend that there is a semicolon at this position. pp.semicolon = function() { - if (!this.eat(tt.semi) && !this.canInsertSemicolon()) this.unexpected(); + if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected(); + }; + + pp.afterTrailingComma = function(tokType) { + if (this.type == tokType) { + if (this.options.onTrailingComma) + this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); + this.next(); + return true; + } }; // Expect a token of a given type. If found, consume it, otherwise, @@ -1800,7 +1822,7 @@ pp.parseBreakContinueStatement = function(node, keyword) { var isBreak = keyword == "break"; this.next(); - if (this.eat(tt.semi) || this.canInsertSemicolon()) node.label = null; + if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null; else if (this.type !== tt.name) this.unexpected(); else { node.label = this.parseIdent(); @@ -1897,7 +1919,7 @@ // optional arguments, we eagerly look for a semicolon or the // possibility to insert one. - if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null; + if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null; else { node.argument = this.parseExpression(); this.semicolon(); } return this.finishNode(node, "ReturnStatement"); }; @@ -2459,7 +2481,7 @@ while (!this.eat(tt.braceR)) { if (!first) { this.expect(tt.comma); - if (this.options.allowTrailingCommas && this.eat(tt.braceR)) break; + if (this.afterTrailingComma(tt.braceR)) break; } else first = false; var prop = this.startNode(), isGenerator, start; @@ -2657,7 +2679,7 @@ while (!this.eat(close)) { if (!first) { this.expect(tt.comma); - if (allowTrailingComma && this.options.allowTrailingCommas && this.eat(close)) break; + if (allowTrailingComma && this.afterTrailingComma(close)) break; } else first = false; if (allowEmpty && this.type === tt.comma) { @@ -2745,7 +2767,7 @@ while (!this.eat(tt.braceR)) { if (!first) { this.expect(tt.comma); - if (this.options.allowTrailingCommas && this.eat(tt.braceR)) break; + if (this.afterTrailingComma(tt.braceR)) break; } else first = false; var node = this.startNode(); @@ -2799,7 +2821,7 @@ while (!this.eat(tt.braceR)) { if (!first) { this.expect(tt.comma); - if (this.options.allowTrailingCommas && this.eat(tt.braceR)) break; + if (this.afterTrailingComma(tt.braceR)) break; } else first = false; var node = this.startNode(); @@ -2816,7 +2838,7 @@ pp.parseYield = function() { var node = this.startNode(); this.next(); - if (this.eat(tt.semi) || this.canInsertSemicolon()) { + if (this.eat(tt.semi) || this.insertSemicolon()) { node.delegate = false; node.argument = null; } else { diff --git a/test/tests.js b/test/tests.js index c54e04ad47..f3d499dd11 100644 --- a/test/tests.js +++ b/test/tests.js @@ -28946,3 +28946,22 @@ test("function f() {} / 1 /", { } ] }); + +var semicolons = [] +testAssert("var x\nreturn\n10", function() { + var result = semicolons.join(" "); + semicolons.length = 0; + if (result != "5 12 15") + return "Unexpected result for onInsertedSemicolon: " + result; +}, {onInsertedSemicolon: function(pos) { semicolons.push(pos); }, + allowReturnOutsideFunction: true, + loose: false}) + +var trailingCommas = [] +testAssert("[1,2,] + {foo: 1,}", function() { + var result = trailingCommas.join(" "); + trailingCommas.length = 0; + if (result != "4 16") + return "Unexpected result for onTrailingComma: " + result; +}, {onTrailingComma: function(pos) { trailingCommas.push(pos); }, + loose: false}) From 4735ef53ef19914b290af0dd9eaafe2917c4d4df Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Mar 2015 22:00:45 +0100 Subject: [PATCH 06/43] Replace the forbidReserved option with an allowReserved option For consistency with similar options --- README.md | 4 ++-- acorn.js | 12 ++++++------ test/tests-harmony.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1a8afd8e86..b685917d11 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ object referring to that same position. - **onTrailingComma**: Like `onInsertedSemicolon`, but for trailing commas. -- **forbidReserved**: If `true`, using a reserved word will generate - an error. Defaults to `false`. When given the value `"everywhere"`, +- **allowReserved**: If `false`, using a reserved word will generate + an error. Defaults to `true`. When given the value `"never"`, reserved words and keywords can also not be used as property names (as in Internet Explorer's old parser). diff --git a/acorn.js b/acorn.js index 8eb6feb988..24740de6b5 100644 --- a/acorn.js +++ b/acorn.js @@ -60,11 +60,11 @@ // `onTrailingComma` is similar to `onInsertedSemicolon`, but for // trailing commas. onTrailingComma: null, - // By default, reserved words are not enforced. Enable - // `forbidReserved` to enforce them. When this option has the - // value "everywhere", reserved words and keywords can also not be + // By default, reserved words are not enforced. Disable + // `allowReserved` to enforce them. When this option has the + // value "never", reserved words and keywords can also not be // used as property names. - forbidReserved: false, + allowReserved: true, // When enabled, a return at the top level is not considered an // error. allowReturnOutsideFunction: false, @@ -2700,10 +2700,10 @@ pp.parseIdent = function(liberal) { var node = this.startNode(); - if (liberal && this.options.forbidReserved == "everywhere") liberal = false; + if (liberal && this.options.allowReserved == "never") liberal = false; if (this.type === tt.name) { if (!liberal && - (this.options.forbidReserved && + (!this.options.allowReserved && (this.options.ecmaVersion === 3 ? isReservedWord3 : isReservedWord5)(this.value) || this.strict && isStrictReservedWord(this.value)) && this.input.slice(this.start, this.end).indexOf("\\") == -1) diff --git a/test/tests-harmony.js b/test/tests-harmony.js index 552b98dc3c..288b74a8ef 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -13871,7 +13871,7 @@ testFail("function hello() {'use strict'; ({ i: 10, s(eval) { } }); }", "Definin testFail("function a() { \"use strict\"; ({ b(t, t) { } }); }", "Argument name clash in strict mode (1:37)", {ecmaVersion: 6}); -testFail("var super", "The keyword 'super' is reserved (1:4)", {ecmaVersion: 6, forbidReserved: true}); +testFail("var super", "The keyword 'super' is reserved (1:4)", {ecmaVersion: 6, allowReserved: false}); testFail("var default", "Unexpected token (1:4)", {ecmaVersion: 6}); From 811d19ab76eeba5146393167b53c48e7a0b70f8d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Mar 2015 22:26:31 +0100 Subject: [PATCH 07/43] Allow any number of digits when killing \u{} sequences in regexps --- acorn.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acorn.js b/acorn.js index 24740de6b5..3156eaf4c6 100644 --- a/acorn.js +++ b/acorn.js @@ -1082,7 +1082,7 @@ // ASCII symbol to avoid throwing on regular expressions that // are only valid in combination with the `/u` flag. tmp = tmp - .replace(/\\u\{([0-9a-fA-F]{5,6})\}/g, "x") + .replace(/\\u\{([0-9a-fA-F]+)\}/g, "x") .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x"); } } From 16a342082a6009563cf3f2c0380495035d26e84c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Mar 2015 22:39:00 +0100 Subject: [PATCH 08/43] Allow trailing commas in array patterns Closes #975 --- acorn.js | 20 +++++--- test/tests-harmony.js | 103 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 7 deletions(-) diff --git a/acorn.js b/acorn.js index 3156eaf4c6..65905a3703 100644 --- a/acorn.js +++ b/acorn.js @@ -1605,7 +1605,7 @@ case tt.bracketL: var node = this.startNode(); this.next(); - node.elements = this.parseBindingList(tt.bracketR, true); + node.elements = this.parseBindingList(tt.bracketR, true, true); return this.finishNode(node, "ArrayPattern"); case tt.braceL: @@ -1616,16 +1616,22 @@ } }; - pp.parseBindingList = function(close, allowEmpty) { + pp.parseBindingList = function(close, allowEmpty, allowTrailingComma) { var elts = [], first = true; while (!this.eat(close)) { - first ? first = false : this.expect(tt.comma); - if (this.type === tt.ellipsis) { + if (first) first = false; + else this.expect(tt.comma); + if (allowEmpty && this.type === tt.comma) { + elts.push(null); + } else if (allowTrailingComma && this.afterTrailingComma(close)) { + break; + } else if (this.type === tt.ellipsis) { elts.push(this.parseRest()); this.expect(close); break; + } else { + elts.push(this.parseMaybeDefault()); } - elts.push(allowEmpty && this.type === tt.comma ? null : this.parseMaybeDefault()); } return elts; }; @@ -2567,7 +2573,7 @@ node.id = this.parseIdent(); } this.expect(tt.parenL); - node.params = this.parseBindingList(tt.parenR, false); + node.params = this.parseBindingList(tt.parenR, false, false); this.parseFunctionBody(node, allowExpressionBody); return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); }; @@ -2578,7 +2584,7 @@ var node = this.startNode(); this.initFunction(node); this.expect(tt.parenL); - node.params = this.parseBindingList(tt.parenR, false); + node.params = this.parseBindingList(tt.parenR, false, false); var allowExpressionBody; if (this.options.ecmaVersion >= 6) { node.generator = isGenerator; diff --git a/test/tests-harmony.js b/test/tests-harmony.js index 288b74a8ef..3fde855cfb 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -15263,6 +15263,109 @@ test("var _\\u{104A6} = 10;", { ] }, {ecmaVersion: 6}); +test("let [x,] = [1]", { + "start": 0, + "body": [ + { + "start": 0, + "declarations": [ + { + "start": 4, + "id": { + "start": 4, + "elements": [ + { + "start": 5, + "name": "x", + "type": "Identifier", + "end": 6 + } + ], + "type": "ArrayPattern", + "end": 8 + }, + "init": { + "start": 11, + "elements": [ + { + "start": 12, + "value": 1, + "raw": "1", + "type": "Literal", + "end": 13 + } + ], + "type": "ArrayExpression", + "end": 14 + }, + "type": "VariableDeclarator", + "end": 14 + } + ], + "kind": "let", + "type": "VariableDeclaration", + "end": 14 + } + ], + "type": "Program", + "end": 14 +}, {ecmaVersion: 6}); + +test("let {x} = y", { + "start": 0, + "body": [ + { + "start": 0, + "declarations": [ + { + "start": 4, + "id": { + "start": 4, + "properties": [ + { + "start": 5, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "start": 5, + "name": "x", + "type": "Identifier", + "end": 6 + }, + "kind": "init", + "value": { + "start": 5, + "name": "x", + "type": "Identifier", + "end": 6 + }, + "type": "Property", + "end": 6 + } + ], + "type": "ObjectPattern", + "end": 7 + }, + "init": { + "start": 10, + "name": "y", + "type": "Identifier", + "end": 11 + }, + "type": "VariableDeclarator", + "end": 11 + } + ], + "kind": "let", + "type": "VariableDeclaration", + "end": 11 + } + ], + "type": "Program", + "end": 11 +}, {ecmaVersion: 6}) + testFail("var _đ–«µ = 11;", "Unexpected character 'đ–«µ' (1:5)", {ecmaVersion: 6}); testFail("var đ« ž_ = 12;", "Unexpected character 'đ« ž' (1:4)", {ecmaVersion: 6}); testFail("var đ« ť_ = 10;", "Unexpected character 'đ« ť' (1:4)", {ecmaVersion: 5}); From 57bc9b1b0a09133b91e714e3605a9cc302da76e1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Mar 2015 23:11:33 +0100 Subject: [PATCH 09/43] Move parsing of yield into parseMaybeAssign Issue #219 --- acorn.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/acorn.js b/acorn.js index 65905a3703..c246fa8f37 100644 --- a/acorn.js +++ b/acorn.js @@ -2156,6 +2156,8 @@ // operators like `+=`. pp.parseMaybeAssign = function(noIn, refShorthandDefaultPos) { + if (this.type == tt._yield && this.inGenerator) return this.parseYield(); + var failOnShorthandAssign; if (!refShorthandDefaultPos) { refShorthandDefaultPos = {start: 0}; @@ -2310,7 +2312,7 @@ return this.finishNode(node, "ThisExpression"); case tt._yield: - if (this.inGenerator) return this.parseYield(); + if (this.inGenerator) unexpected(); case tt.name: var start = this.currentPos(); @@ -2844,7 +2846,7 @@ pp.parseYield = function() { var node = this.startNode(); this.next(); - if (this.eat(tt.semi) || this.insertSemicolon()) { + if (this.type == tt.semi || this.canInsertSemicolon()) { node.delegate = false; node.argument = null; } else { From 19ef4ef69cce3bdb46632df34afd837b9673904d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 17 Mar 2015 12:30:12 +0100 Subject: [PATCH 10/43] Require semicolon after 'import * from ...' --- acorn.js | 1 + 1 file changed, 1 insertion(+) diff --git a/acorn.js b/acorn.js index c246fa8f37..f82b9753fd 100644 --- a/acorn.js +++ b/acorn.js @@ -2734,6 +2734,7 @@ if (this.eat(tt.star)) { this.expectContextual("from"); node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); + this.semicolon(); return this.finishNode(node, "ExportAllDeclaration"); } if (this.eat(tt._default)) { // export default ...; From 876e774bfe8d656571e6bfdcf3b78d31a2ee9fa1 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 17 Mar 2015 22:33:46 +0200 Subject: [PATCH 11/43] [estree] Align method kinds. [loose] Split parseObj and parseClass. --- acorn.js | 19 ++++--- acorn_loose.js | 127 ++++++++++++++++++++++++------------------ test/tests-harmony.js | 68 +++++++++++++++------- 3 files changed, 133 insertions(+), 81 deletions(-) diff --git a/acorn.js b/acorn.js index f82b9753fd..5389e3dbbd 100644 --- a/acorn.js +++ b/acorn.js @@ -2661,13 +2661,18 @@ } else { method['static'] = false; } - if (this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && - (method.key.name === "get" || method.key.name === "set")) { - if (isGenerator) this.unexpected(); - method.kind = method.key.name; - this.parsePropertyName(method); - } else { - method.kind = ""; + method.kind = "method"; + if (!method.computed && !isGenerator) { + if (method.key.type === "Identifier") { + if (this.type !== tt.parenL && (method.key.name === "get" || method.key.name === "set")) { + method.kind = method.key.name; + this.parsePropertyName(method); + } else if (!method['static'] && method.key.name === "constructor") { + method.kind = "constructor"; + } + } else if (!method['static'] && method.key.type === "Literal" && method.key.value === "constructor") { + method.kind = "constructor"; + } } method.value = this.parseMethod(isGenerator); classBody.body.push(this.finishNode(method, "MethodDefinition")); diff --git a/acorn_loose.js b/acorn_loose.js index c936b4f0e9..94b5919bb6 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -456,7 +456,7 @@ return this.finishNode(node, "EmptyStatement"); case tt._class: - return this.parseObj(true, true); + return this.parseClass(true); case tt._import: return this.parseImport(); @@ -751,7 +751,7 @@ return this.parseObj(); case tt._class: - return this.parseObj(true); + return this.parseClass(); case tt._function: var node = this.startNode(); @@ -828,58 +828,29 @@ return this.finishNode(node, "TemplateLiteral"); }; - lp.parseObj = function(isClass, isStatement) { + lp.parseObj = function() { var node = this.startNode(); - if (isClass) { - this.next(); - if (this.tok.type === tt.name) node.id = this.parseIdent(); - else if (isStatement) node.id = this.dummyIdent(); - else node.id = null; - node.superClass = this.eat(tt._extends) ? this.parseExpression() : null; - node.body = this.startNode(); - node.body.body = []; - } else { - node.properties = []; - } + node.properties = []; this.pushCx(); var indent = this.curIndent + 1, line = this.curLineStart; this.eat(tt.braceL); if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart; } while (!this.closes(tt.braceR, indent, line)) { - if (isClass && this.semicolon()) continue; var prop = this.startNode(), isGenerator, start; if (this.options.ecmaVersion >= 6) { - if (isClass) { - prop['static'] = false; - } else { - start = this.storeCurrentPos(); - prop.method = false; - prop.shorthand = false; - } + start = this.storeCurrentPos(); + prop.method = false; + prop.shorthand = false; isGenerator = this.eat(tt.star); } this.parsePropertyName(prop); if (isDummy(prop.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue; } - if (isClass) { - if (prop.key.type === "Identifier" && !prop.computed && prop.key.name === "static" && - (this.tok.type != tt.parenL && this.tok.type != tt.braceL)) { - prop['static'] = true; - isGenerator = this.eat(tt.star); - this.parsePropertyName(prop); - } else { - prop['static'] = false; - } - } - if (!isClass && this.eat(tt.colon)) { + if (this.eat(tt.colon)) { prop.kind = "init"; prop.value = this.parseMaybeAssign(); } else if (this.options.ecmaVersion >= 6 && (this.tok.type === tt.parenL || this.tok.type === tt.braceL)) { - if (isClass) { - prop.kind = ""; - } else { - prop.kind = "init"; - prop.method = true; - } + prop.kind = "init"; + prop.method = true; prop.value = this.parseMethod(isGenerator); } else if (this.options.ecmaVersion >= 5 && prop.key.type === "Identifier" && !prop.computed && (prop.key.name === "get" || prop.key.name === "set") && @@ -887,9 +858,6 @@ prop.kind = prop.key.name; this.parsePropertyName(prop); prop.value = this.parseMethod(false); - } else if (isClass) { - prop.kind = ""; - prop.value = this.parseMethod(isGenerator); } else { prop.kind = "init"; if (this.options.ecmaVersion >= 6) { @@ -907,13 +875,8 @@ } prop.shorthand = true; } - - if (isClass) { - node.body.body.push(this.finishNode(prop, "MethodDefinition")); - } else { - node.properties.push(this.finishNode(prop, "Property")); - this.eat(tt.comma); - } + node.properties.push(this.finishNode(prop, "Property")); + this.eat(tt.comma); } this.popCx(); if (!this.eat(tt.braceR)) { @@ -922,13 +885,67 @@ this.last.end = this.tok.start; if (this.options.locations) this.last.loc.end = this.tok.loc.start; } - if (isClass) { - this.semicolon(); - this.finishNode(node.body, "ClassBody"); - return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); - } else { - return this.finishNode(node, "ObjectExpression"); + return this.finishNode(node, "ObjectExpression"); + }; + + lp.parseClass = function(isStatement) { + var node = this.startNode(); + this.next(); + if (this.tok.type === tt.name) node.id = this.parseIdent(); + else if (isStatement) node.id = this.dummyIdent(); + else node.id = null; + node.superClass = this.eat(tt._extends) ? this.parseExpression() : null; + node.body = this.startNode(); + node.body.body = []; + this.pushCx(); + var indent = this.curIndent + 1, line = this.curLineStart; + this.eat(tt.braceL); + if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart; } + while (!this.closes(tt.braceR, indent, line)) { + if (this.semicolon()) continue; + var method = this.startNode(), isGenerator, start; + if (this.options.ecmaVersion >= 6) { + method['static'] = false; + isGenerator = this.eat(tt.star); + } + this.parsePropertyName(method); + if (isDummy(method.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue; } + if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" && + (this.tok.type != tt.parenL && this.tok.type != tt.braceL)) { + method['static'] = true; + isGenerator = this.eat(tt.star); + this.parsePropertyName(method); + } else { + method['static'] = false; + } + if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" && + !method.computed && (method.key.name === "get" || method.key.name === "set") && + this.tok.type !== tt.parenL && this.tok.type !== tt.braceL) { + method.kind = method.key.name; + this.parsePropertyName(method); + method.value = this.parseMethod(false); + } else { + if (!method.computed && !method['static'] && !isGenerator && ( + method.key.type === "Identifier" && method.key.name === "constructor" || + method.key.type === "Literal" && method.key.value === "constructor")) { + method.kind = "constructor"; + } else { + method.kind = "method"; + } + method.value = this.parseMethod(isGenerator); + } + node.body.body.push(this.finishNode(method, "MethodDefinition")); } + this.popCx(); + if (!this.eat(tt.braceR)) { + // If there is no closing brace, make the node span to the start + // of the next token (this is useful for Tern) + this.last.end = this.tok.start; + if (this.options.locations) this.last.loc.end = this.tok.loc.start; + } + this.semicolon(); + this.finishNode(node.body, "ClassBody"); + return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); }; lp.parsePropertyName = function(prop) { diff --git a/test/tests-harmony.js b/test/tests-harmony.js index 3fde855cfb..d9699915fb 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -6560,7 +6560,7 @@ test("class A {get() {}}", { end: {line: 1, column: 17} } }, - kind: "", + kind: "method", static: false, loc: { start: {line: 1, column: 9}, @@ -6632,7 +6632,7 @@ test("class A { static get() {}}", { end: {line: 1, column: 25} } }, - kind: "", + kind: "method", static: true, loc: { start: {line: 1, column: 10}, @@ -7027,7 +7027,7 @@ test("class A {set(v) {};}", { end: {line: 1, column: 18} } }, - kind: "", + kind: "method", static: false, loc: { start: {line: 1, column: 9}, @@ -7106,7 +7106,7 @@ test("class A { static set(v) {};}", { end: {line: 1, column: 26} } }, - kind: "", + kind: "method", static: true, loc: { start: {line: 1, column: 10}, @@ -7207,7 +7207,7 @@ test("class A {*gen(v) { yield v; }}", { end: {line: 1, column: 29} } }, - kind: "", + kind: "method", static: false, loc: { start: {line: 1, column: 9}, @@ -7308,7 +7308,7 @@ test("class A { static *gen(v) { yield v; }}", { end: {line: 1, column: 37} } }, - kind: "", + kind: "method", static: true, loc: { start: {line: 1, column: 10}, @@ -7421,7 +7421,7 @@ test("\"use strict\"; (class A {constructor() { super() }})", { end: {line: 1, column: 49} } }, - kind: "", + kind: "constructor", static: false, loc: { start: {line: 1, column: 24}, @@ -7454,6 +7454,36 @@ test("\"use strict\"; (class A {constructor() { super() }})", { locations: true }); +test("class A {'constructor'() {}}", { + type: "Program", + body: [{ + type: "ClassDeclaration", + id: {type: "Identifier", name: "A"}, + superClass: null, + body: { + type: "ClassBody", + body: [{ + type: "MethodDefinition", + computed: false, + key: {type: "Literal", value: "constructor"}, + static: false, + kind: "constructor", + value: { + type: "FunctionExpression", + id: null, + generator: false, + expression: false, + params: [], + body: { + type: "BlockStatement", + body: [] + } + } + }] + } + }] +}, {ecmaVersion: 6}); + test("class A {static foo() {}}", { type: "Program", body: [{ @@ -7499,7 +7529,7 @@ test("class A {static foo() {}}", { end: {line: 1, column: 24} } }, - kind: "", + kind: "method", static: true, loc: { start: {line: 1, column: 9}, @@ -7572,7 +7602,7 @@ test("class A {foo() {} static bar() {}}", { end: {line: 1, column: 17} } }, - kind: "", + kind: "method", static: false, loc: { start: {line: 1, column: 9}, @@ -7609,7 +7639,7 @@ test("class A {foo() {} static bar() {}}", { end: {line: 1, column: 33} } }, - kind: "", + kind: "method", static: true, loc: { start: {line: 1, column: 18}, @@ -7723,7 +7753,7 @@ test("\"use strict\"; (class A { static constructor() { super() }})", { end: {line: 1, column: 57} } }, - kind: "", + kind: "method", static: true, loc: { start: {line: 1, column: 25}, @@ -7802,7 +7832,7 @@ test("class A { foo() {} bar() {}}", { end: {line: 1, column: 18} } }, - kind: "", + kind: "method", static: false, loc: { start: {line: 1, column: 10}, @@ -7839,7 +7869,7 @@ test("class A { foo() {} bar() {}}", { end: {line: 1, column: 27} } }, - kind: "", + kind: "method", static: false, loc: { start: {line: 1, column: 19}, @@ -8450,7 +8480,7 @@ test("class A { static [foo]() {} }", { }, name: "foo" }, - kind: "", + kind: "method", value: { type: "FunctionExpression", loc: { @@ -8717,7 +8747,7 @@ test("class A { foo() {} get foo() {} }",{ }, name: "foo" }, - kind: "", + kind: "method", value: { type: "FunctionExpression", loc: { @@ -9582,7 +9612,7 @@ test("class A {[x]() {}}", { }, name: "x" }, - kind: "", + kind: "method", value: { type: "FunctionExpression", loc: { @@ -10324,7 +10354,7 @@ test("(class {f({x} = {x: 10}) {}})", { end: {line: 1, column: 27} } }, - kind: "", + kind: "method", static: false, loc: { start: {line: 1, column: 8}, @@ -14947,7 +14977,7 @@ test("class A { static() {} }", { name: "static" }, static: false, - kind: "", + kind: "method", value: { type: "FunctionExpression", range: [16, 21], @@ -15044,7 +15074,7 @@ test("class A { *static() {} }", { name: "static" }, static: false, - kind: "", + kind: "method", value: { type: "FunctionExpression", range: [17, 22], From 024a98431d54c4b88dc209d2270a70350dca32be Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 17 Mar 2015 23:01:42 +0200 Subject: [PATCH 12/43] [estree] Add SuperExpression. (we're almost there) --- acorn.js | 5 +++++ acorn_loose.js | 5 +++++ test/tests-harmony.js | 6 ++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/acorn.js b/acorn.js index 5389e3dbbd..5c0412a068 100644 --- a/acorn.js +++ b/acorn.js @@ -2315,6 +2315,11 @@ if (this.inGenerator) unexpected(); case tt.name: + if (this.value === "super") { + var node = this.startNode(); + this.next(); + return this.finishNode(node, "SuperExpression"); + } var start = this.currentPos(); var id = this.parseIdent(this.type !== tt.name); if (!this.canInsertSemicolon() && this.eat(tt.arrow)) { diff --git a/acorn_loose.js b/acorn_loose.js index 94b5919bb6..62500963c5 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -699,6 +699,11 @@ return this.finishNode(node, "ThisExpression"); case tt.name: + if (this.tok.value === "super") { + var node = this.startNode(); + this.next(); + return this.finishNode(node, "SuperExpression"); + } var start = this.storeCurrentPos(); var id = this.parseIdent(); return this.eat(tt.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id; diff --git a/test/tests-harmony.js b/test/tests-harmony.js index d9699915fb..c81fca1423 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -7391,8 +7391,7 @@ test("\"use strict\"; (class A {constructor() { super() }})", { expression: { type: "CallExpression", callee: { - type: "Identifier", - name: "super", + type: "SuperExpression", loc: { start: {line: 1, column: 40}, end: {line: 1, column: 45} @@ -7723,8 +7722,7 @@ test("\"use strict\"; (class A { static constructor() { super() }})", { expression: { type: "CallExpression", callee: { - type: "Identifier", - name: "super", + type: "SuperExpression", loc: { start: {line: 1, column: 48}, end: {line: 1, column: 53} From 0473c368e615e94bf0d3d9c23c98349f7ee9f8c6 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 18 Mar 2015 13:42:49 +0200 Subject: [PATCH 13/43] [es6][estree] Add support for `sourceType: script|module` modes. + Fix list of keywords and reserved words in ES6. --- README.md | 3 +++ acorn.js | 42 ++++++++++++++++++++++++------------- acorn_loose.js | 12 +++++------ test/tests-harmony.js | 49 +++++++++++++++++++++++++++++++++---------- 4 files changed, 75 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index b685917d11..3ab7882203 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ object referring to that same position. either 3, 5, or 6. This influences support for strict mode, the set of reserved words, and support for new syntax features. Default is 5. +- **sourceType**: Indicate the mode the code should be parsed in. Can be + either `"script"` or `"module"`. + - **onInsertedSemicolon**: If given a callback, that callback will be called whenever a missing semicolon is inserted by the parser. The callback will be given the character offset of the point where the diff --git a/acorn.js b/acorn.js index 5c0412a068..89a8818415 100644 --- a/acorn.js +++ b/acorn.js @@ -51,6 +51,8 @@ // mode, the set of reserved words, support for getters and // setters and other features. ecmaVersion: 5, + // Source type ("script" or "module") for different semantics + sourceType: "script", // `onInsertedSemicolon` can be a callback that will be called // when a semicolon is automatically inserted. It will be passed // th position of the comma as an offset, and if `locations` is @@ -342,6 +344,7 @@ kw("with"); kw("new", beforeExpr); kw("this"); + kw("super"); kw("class"); kw("extends", beforeExpr); kw("export"); @@ -412,6 +415,10 @@ var isReservedWord5 = makePredicate("class enum extends super const export import"); + // ECMAScript 6 reserved words. + + var isReservedWord6 = makePredicate("enum await"); + // The additional reserved words in strict mode. var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield"); @@ -426,7 +433,7 @@ var isEcma5AndLessKeyword = makePredicate(ecma5AndLessKeywords); - var isEcma6Keyword = makePredicate(ecma5AndLessKeywords + " let const class extends export import yield"); + var isEcma6Keyword = makePredicate(ecma5AndLessKeywords + " let const class extends export import yield super"); // ## Character categories @@ -543,6 +550,7 @@ this.loadPlugins(this.options.plugins); this.sourceFile = this.options.sourceFile || null; this.isKeyword = this.options.ecmaVersion >= 6 ? isEcma6Keyword : isEcma5AndLessKeyword; + this.isReservedWord = this.options.ecmaVersion === 3 ? isReservedWord3 : this.options.ecmaVersion === 5 ? isReservedWord5 : isReservedWord6; this.input = String(input); // Set up token state @@ -578,9 +586,11 @@ this.context = [tc.b_stat]; this.exprAllowed = true; - // Flags to track whether we are in strict mode, a function, a - // generator. - this.strict = this.inFunction = this.inGenerator = false; + // Figure out if it's a module code. + this.strict = this.inModule = this.options.sourceType === "module"; + + // Flags to track whether we are in a function, a generator. + this.inFunction = this.inGenerator = false; // Labels in scope. this.labels = []; @@ -946,6 +956,7 @@ } if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && this.input.charCodeAt(this.pos + 3) == 45) { + if (this.inModule) unexpected(); // `` line comment + this.skipLineComment(3); + this.skipSpace(); + return this.nextToken(); + } + return this.finishOp(tt.incDec, 2); + } + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.plusMin, 1); +}; + +pp.readToken_lt_gt = function(code) { // '<>' + var next = this.input.charCodeAt(this.pos + 1); + var size = 1; + if (next === code) { + size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; + if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); + return this.finishOp(tt.bitShift, size); + } + if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && + this.input.charCodeAt(this.pos + 3) == 45) { + if (this.inModule) unexpected(); + // `` line comment + this.skipLineComment(3); + this.skipSpace(); + return this.nextToken(); + } + return this.finishOp(tt.incDec, 2); + } + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.plusMin, 1); +}; + +pp.readToken_lt_gt = function(code) { // '<>' + var next = this.input.charCodeAt(this.pos + 1); + var size = 1; + if (next === code) { + size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; + if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); + return this.finishOp(tt.bitShift, size); + } + if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && + this.input.charCodeAt(this.pos + 3) == 45) { + if (this.inModule) unexpected(); + // `` line comment - this.skipLineComment(3); - this.skipSpace(); - return this.nextToken(); - } - return this.finishOp(tt.incDec, 2); - } - if (next === 61) return this.finishOp(tt.assign, 2); - return this.finishOp(tt.plusMin, 1); -}; - -pp.readToken_lt_gt = function(code) { // '<>' - var next = this.input.charCodeAt(this.pos + 1); - var size = 1; - if (next === code) { - size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; - if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); - return this.finishOp(tt.bitShift, size); - } - if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && - this.input.charCodeAt(this.pos + 3) == 45) { - if (this.inModule) unexpected(); - // `` line comment - this.skipLineComment(3); - this.skipSpace(); - return this.nextToken(); - } - return this.finishOp(tt.incDec, 2); - } - if (next === 61) return this.finishOp(tt.assign, 2); - return this.finishOp(tt.plusMin, 1); - }; - - pp.readToken_lt_gt = function(code) { // '<>' - var next = this.input.charCodeAt(this.pos + 1); - var size = 1; - if (next === code) { - size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; - if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); - return this.finishOp(tt.bitShift, size); - } - if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && - this.input.charCodeAt(this.pos + 3) == 45) { - if (this.inModule) unexpected(); - // `` line comment - this.skipLineComment(3); - this.skipSpace(); - return this.nextToken(); + this.skipLineComment(3) + this.skipSpace() + return this.nextToken() } - return this.finishOp(tt.incDec, 2); + return this.finishOp(tt.incDec, 2) } - if (next === 61) return this.finishOp(tt.assign, 2); - return this.finishOp(tt.plusMin, 1); -}; + if (next === 61) return this.finishOp(tt.assign, 2) + return this.finishOp(tt.plusMin, 1) +} pp.readToken_lt_gt = function(code) { // '<>' - var next = this.input.charCodeAt(this.pos + 1); - var size = 1; + let next = this.input.charCodeAt(this.pos + 1) + let size = 1 if (next === code) { - size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; - if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); - return this.finishOp(tt.bitShift, size); + size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2 + if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1) + return this.finishOp(tt.bitShift, size) } if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && this.input.charCodeAt(this.pos + 3) == 45) { - if (this.inModule) unexpected(); + if (this.inModule) unexpected() // `