diff --git a/AUTHORS b/AUTHORS deleted file mode 100755 index 799a063354..0000000000 --- a/AUTHORS +++ /dev/null @@ -1,32 +0,0 @@ -List of Acorn contributors. Updated before every release. - -Alistair Braidwood -Aparajita Fishman -Arian Stolwijk -Artem Govorov -Brandon Mills -Charles Hughes -Conrad Irwin -David Bonnet -impinball -Ingvar Stepanyan -Jiaxing Wang -Johannes Herr -Jürg Lehni -keeyipchan -krator -Marijn Haverbeke -Martin Carlberg -Mathias Bynens -Mathieu 'p01' Henri -Max Schaefer -Mihai Bazon -Mike Rennie -Oskar Schöldström -Paul Harper -Peter Rust -PlNG -r-e-d -Rich Harris -Sebastian McKenzie -zsjforcn diff --git a/LICENSE b/LICENSE deleted file mode 100755 index d4c7fc5838..0000000000 --- a/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2012-2014 by various contributors (see AUTHORS) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..d8da1c161c --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +

+ babylon +

+ +

+ Babylon is a streaming parser for Babel. +

diff --git a/index.js b/index.js deleted file mode 100644 index a9c3b90793..0000000000 --- a/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./src/index"; -import "./plugins/flow"; - -import inject from "acorn-jsx/inject"; -import * as acorn from "./src/index"; -inject(acorn); diff --git a/package.json b/package.json old mode 100755 new mode 100644 index 4067160963..767cde522a --- a/package.json +++ b/package.json @@ -1,44 +1,12 @@ { - "name": "acorn", - "description": "ECMAScript parser", - "homepage": "https://github.com/marijnh/acorn", - "main": "index.js", - "version": "1.0.0", - "engines": { - "node": ">=0.4.0" - }, - "maintainers": [ - { - "name": "Marijn Haverbeke", - "email": "marijnh@gmail.com", - "web": "http://marijnhaverbeke.nl" - }, - { - "name": "Ingvar Stepanyan", - "email": "me@rreverser.com", - "web": "http://rreverser.com/" - } - ], - "repository": { - "type": "git", - "url": "https://github.com/marijnh/acorn.git" - }, - "licenses": [ - { - "type": "MIT", - "url": "https://raw.githubusercontent.com/marijnh/acorn/master/LICENSE" - } - ], - "scripts": { - "test": "node test/run.js", - "prepublish": "node bin/prepublish.sh" - }, - "bin": { - "acorn": "./bin/acorn" - }, - "devDependencies": { - "babelify": "^5.0.4", - "browserify": "^9.0.3", - "unicode-7.0.0": "~0.1.5" + "name": "babylon", + "description": "", + "author": "Sebastian McKenzie ", + "homepage": "https://babeljs.io/", + "license": "MIT", + "repository": "babel/babel", + "main": "lib/index.js", + "dependencies": { + "bluebird": "^2.9.33" } } diff --git a/plugins/flow.js b/plugins/flow.js deleted file mode 100644 index 34919438f4..0000000000 --- a/plugins/flow.js +++ /dev/null @@ -1,830 +0,0 @@ -var acorn = require("../src/index") - -var pp = acorn.Parser.prototype -var tt = acorn.tokTypes - -pp.isRelational = function (op) { - return this.type === tt.relational && this.value === op -} - -pp.expectRelational = function (op) { - if (this.isRelational(op)) { - this.next() - } else { - this.unexpected() - } -} - -pp.flow_parseTypeInitialiser = function (tok) { - var oldInType = this.inType - this.inType = true - this.expect(tok || tt.colon) - var type = this.flow_parseType() - this.inType = oldInType - return type; -} - -pp.flow_parseDeclareClass = function (node) { - this.next() - this.flow_parseInterfaceish(node, true) - return this.finishNode(node, "DeclareClass") -} - -pp.flow_parseDeclareFunction = function (node) { - this.next() - - var id = node.id = this.parseIdent() - - var typeNode = this.startNode() - var typeContainer = this.startNode() - - if (this.isRelational("<")) { - typeNode.typeParameters = this.flow_parseTypeParameterDeclaration() - } else { - typeNode.typeParameters = null - } - - this.expect(tt.parenL) - var tmp = this.flow_parseFunctionTypeParams() - typeNode.params = tmp.params - typeNode.rest = tmp.rest - this.expect(tt.parenR) - typeNode.returnType = this.flow_parseTypeInitialiser() - - typeContainer.typeAnnotation = this.finishNode(typeNode, "FunctionTypeAnnotation") - id.typeAnnotation = this.finishNode(typeContainer, "TypeAnnotation") - - this.finishNode(id, id.type) - - this.semicolon() - - return this.finishNode(node, "DeclareFunction") -} - -pp.flow_parseDeclare = function (node) { - if (this.type === tt._class) { - return this.flow_parseDeclareClass(node) - } else if (this.type === tt._function) { - return this.flow_parseDeclareFunction(node) - } else if (this.type === tt._var) { - return this.flow_parseDeclareVariable(node) - } else if (this.isContextual("module")) { - return this.flow_parseDeclareModule(node) - } else { - this.unexpected() - } -} - -pp.flow_parseDeclareVariable = function (node) { - this.next() - node.id = this.flow_parseTypeAnnotatableIdentifier() - this.semicolon() - return this.finishNode(node, "DeclareVariable") -} - -pp.flow_parseDeclareModule = function (node) { - this.next() - - if (this.type === tt.string) { - node.id = this.parseExprAtom() - } else { - node.id = this.parseIdent() - } - - var bodyNode = node.body = this.startNode() - var body = bodyNode.body = [] - this.expect(tt.braceL) - while (this.type !== tt.braceR) { - var node2 = this.startNode() - - // todo: declare check - this.next() - - body.push(this.flow_parseDeclare(node2)) - } - this.expect(tt.braceR) - - this.finishNode(bodyNode, "BlockStatement") - return this.finishNode(node, "DeclareModule") -} - - -// Interfaces - -pp.flow_parseInterfaceish = function (node, allowStatic) { - node.id = this.parseIdent() - - if (this.isRelational("<")) { - node.typeParameters = this.flow_parseTypeParameterDeclaration() - } else { - node.typeParameters = null - } - - node.extends = [] - - if (this.eat(tt._extends)) { - do { - node.extends.push(this.flow_parseInterfaceExtends()) - } while(this.eat(tt.comma)) - } - - node.body = this.flow_parseObjectType(allowStatic) -} - -pp.flow_parseInterfaceExtends = function () { - var node = this.startNode() - - node.id = this.parseIdent() - if (this.isRelational("<")) { - node.typeParameters = this.flow_parseTypeParameterInstantiation() - } else { - node.typeParameters = null - } - - return this.finishNode(node, "InterfaceExtends") -} - -pp.flow_parseInterface = function (node) { - this.flow_parseInterfaceish(node, false) - return this.finishNode(node, "InterfaceDeclaration") -} - -// Type aliases - -pp.flow_parseTypeAlias = function (node) { - node.id = this.parseIdent() - - if (this.isRelational("<")) { - node.typeParameters = this.flow_parseTypeParameterDeclaration() - } else { - node.typeParameters = null - } - - node.right = this.flow_parseTypeInitialiser(tt.eq) - this.semicolon() - - return this.finishNode(node, "TypeAlias") -} - -// Type annotations - -pp.flow_parseTypeParameterDeclaration = function () { - var node = this.startNode() - node.params = [] - - this.expectRelational("<") - while (!this.isRelational(">")) { - node.params.push(this.flow_parseTypeAnnotatableIdentifier()) - if (!this.isRelational(">")) { - this.expect(tt.comma) - } - } - this.expectRelational(">") - - return this.finishNode(node, "TypeParameterDeclaration") -} - -pp.flow_parseTypeParameterInstantiation = function () { - var node = this.startNode(), oldInType = this.inType - node.params = [] - - this.inType = true - - this.expectRelational("<") - while (!this.isRelational(">")) { - node.params.push(this.flow_parseType()) - if (!this.isRelational(">")) { - this.expect(tt.comma) - } - } - this.expectRelational(">") - - this.inType = oldInType - - return this.finishNode(node, "TypeParameterInstantiation") -} - -pp.flow_parseObjectPropertyKey = function () { - return (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true) -} - -pp.flow_parseObjectTypeIndexer = function (node, isStatic) { - node.static = isStatic - - this.expect(tt.bracketL) - node.id = this.flow_parseObjectPropertyKey() - node.key = this.flow_parseTypeInitialiser() - this.expect(tt.bracketR) - node.value = this.flow_parseTypeInitialiser() - - this.flow_objectTypeSemicolon() - return this.finishNode(node, "ObjectTypeIndexer") -} - -pp.flow_parseObjectTypeMethodish = function (node) { - node.params = [] - node.rest = null - node.typeParameters = null - - if (this.isRelational("<")) { - node.typeParameters = this.flow_parseTypeParameterDeclaration() - } - - this.expect(tt.parenL) - while (this.type === tt.name) { - node.params.push(this.flow_parseFunctionTypeParam()) - if (this.type !== tt.parenR) { - this.expect(tt.comma) - } - } - - if (this.eat(tt.ellipsis)) { - node.rest = this.flow_parseFunctionTypeParam() - } - this.expect(tt.parenR) - node.returnType = this.flow_parseTypeInitialiser() - - return this.finishNode(node, "FunctionTypeAnnotation") -} - -pp.flow_parseObjectTypeMethod = function (start, isStatic, key) { - var node = this.startNodeAt(start) - node.value = this.flow_parseObjectTypeMethodish(this.startNodeAt(start)) - node.static = isStatic - node.key = key - node.optional = false - this.flow_objectTypeSemicolon() - return this.finishNode(node, "ObjectTypeProperty") -} - -pp.flow_parseObjectTypeCallProperty = function (node, isStatic) { - var valueNode = this.startNode() - node.static = isStatic - node.value = this.flow_parseObjectTypeMethodish(valueNode) - this.flow_objectTypeSemicolon() - return this.finishNode(node, "ObjectTypeCallProperty") -} - -pp.flow_parseObjectType = function (allowStatic) { - var nodeStart = this.startNode() - var node - var optional = false - var property - var propertyKey - var propertyTypeAnnotation - var token - var isStatic - - nodeStart.callProperties = [] - nodeStart.properties = [] - nodeStart.indexers = [] - - this.expect(tt.braceL) - - while (this.type !== tt.braceR) { - var start = this.markPosition() - node = this.startNode() - if (allowStatic && this.isContextual("static")) { - this.next() - isStatic = true - } - - if (this.type === tt.bracketL) { - nodeStart.indexers.push(this.flow_parseObjectTypeIndexer(node, isStatic)) - } else if (this.type === tt.parenL || this.isRelational("<")) { - nodeStart.callProperties.push(this.flow_parseObjectTypeCallProperty(node, allowStatic)) - } else { - if (isStatic && this.type === tt.colon) { - propertyKey = this.parseIdent() - } else { - propertyKey = this.flow_parseObjectPropertyKey() - } - if (this.isRelational("<") || this.type === tt.parenL) { - // This is a method property - nodeStart.properties.push(this.flow_parseObjectTypeMethod(start, isStatic, propertyKey)) - } else { - if (this.eat(tt.question)) { - optional = true - } - node.key = propertyKey - node.value = this.flow_parseTypeInitialiser() - node.optional = optional - node.static = isStatic - this.flow_objectTypeSemicolon() - nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty")) - } - } - } - - this.expect(tt.braceR) - - return this.finishNode(nodeStart, "ObjectTypeAnnotation") -} - -pp.flow_objectTypeSemicolon = function () { - if (!this.eat(tt.semi) && !this.eat(tt.comma) && this.type !== tt.braceR) { - this.unexpected() - } -} - -pp.flow_parseGenericType = function (start, id) { - var node = this.startNodeAt(start) - - node.typeParameters = null - node.id = id - - while (this.eat(tt.dot)) { - var node2 = this.startNodeAt(start) - node2.qualification = node.id - node2.id = this.parseIdent() - node.id = this.finishNode(node2, "QualifiedTypeIdentifier") - } - - if (this.isRelational("<")) { - node.typeParameters = this.flow_parseTypeParameterInstantiation() - } - - return this.finishNode(node, "GenericTypeAnnotation") -} - -pp.flow_parseTypeofType = function () { - var node = this.startNode() - this.expect(tt._typeof) - node.argument = this.flow_parsePrimaryType() - return this.finishNode(node, "TypeofTypeAnnotation") -} - -pp.flow_parseTupleType = function () { - var node = this.startNode() - node.types = [] - this.expect(tt.bracketL) - // We allow trailing commas - while (this.pos < this.input.length && this.type !== tt.bracketR) { - node.types.push(this.flow_parseType()) - if (this.type === tt.bracketR) break - this.expect(tt.comma) - } - this.expect(tt.bracketR) - return this.finishNode(node, "TupleTypeAnnotation") -} - -pp.flow_parseFunctionTypeParam = function () { - var optional = false - var node = this.startNode() - node.name = this.parseIdent() - if (this.eat(tt.question)) { - optional = true - } - node.optional = optional - node.typeAnnotation = this.flow_parseTypeInitialiser() - return this.finishNode(node, "FunctionTypeParam") -} - -pp.flow_parseFunctionTypeParams = function () { - var ret = { params: [], rest: null } - while (this.type === tt.name) { - ret.params.push(this.flow_parseFunctionTypeParam()) - if (this.type !== tt.parenR) { - this.expect(tt.comma) - } - } - if (this.eat(tt.ellipsis)) { - ret.rest = this.flow_parseFunctionTypeParam() - } - return ret -} - -pp.flow_identToTypeAnnotation = function (start, node, id) { - switch (id.name) { - case "any": - return this.finishNode(node, "AnyTypeAnnotation") - - case "void": - return this.finishNode(node, "VoidTypeAnnotation") - - case "bool": - case "boolean": - return this.finishNode(node, "BooleanTypeAnnotation") - - case "mixed": - return this.finishNode(node, "MixedTypeAnnotation") - - case "number": - return this.finishNode(node, "NumberTypeAnnotation") - - case "string": - return this.finishNode(node, "StringTypeAnnotation") - - default: - return this.flow_parseGenericType(start, id) - } -} - -// The parsing of types roughly parallels the parsing of expressions, and -// primary types are kind of like primary expressions...they're the -// primitives with which other types are constructed. -pp.flow_parsePrimaryType = function () { - var typeIdentifier = null - var params = null - var returnType = null - var start = this.markPosition() - var node = this.startNode() - var rest = null - var tmp - var typeParameters - var token - var type - var isGroupedType = false - - switch (this.type) { - case tt.name: - return this.flow_identToTypeAnnotation(start, node, this.parseIdent()) - - case tt.braceL: - return this.flow_parseObjectType() - - case tt.bracketL: - return this.flow_parseTupleType() - - case tt.relational: - if (this.value === "<") { - node.typeParameters = this.flow_parseTypeParameterDeclaration() - this.expect(tt.parenL) - tmp = this.flow_parseFunctionTypeParams() - node.params = tmp.params - node.rest = tmp.rest - this.expect(tt.parenR) - - this.expect(tt.arrow) - - node.returnType = this.flow_parseType() - - return this.finishNode(node, "FunctionTypeAnnotation") - } - - case tt.parenL: - this.next() - - // Check to see if this is actually a grouped type - if (this.type !== tt.parenR && this.type !== tt.ellipsis) { - if (this.type === tt.name) { - var token = this.lookahead().type - isGroupedType = token !== tt.question && token !== tt.colon - } else { - isGroupedType = true - } - } - - if (isGroupedType) { - type = this.flow_parseType() - this.expect(tt.parenR) - - // If we see a => next then someone was probably confused about - // function types, so we can provide a better error message - if (this.eat(tt.arrow)) { - this.raise(node, - "Unexpected token =>. It looks like " + - "you are trying to write a function type, but you ended up " + - "writing a grouped type followed by an =>, which is a syntax " + - "error. Remember, function type parameters are named so function " + - "types look like (name1: type1, name2: type2) => returnType. You " + - "probably wrote (type1) => returnType" - ) - } - - return type - } - - tmp = this.flow_parseFunctionTypeParams() - node.params = tmp.params - node.rest = tmp.rest - - this.expect(tt.parenR) - - this.expect(tt.arrow) - - node.returnType = this.flow_parseType() - node.typeParameters = null - - return this.finishNode(node, "FunctionTypeAnnotation") - - case tt.string: - node.value = this.value - node.raw = this.input.slice(this.start, this.end) - this.next() - return this.finishNode(node, "StringLiteralTypeAnnotation") - - default: - if (this.type.keyword === "typeof") { - return this.flow_parseTypeofType() - } - } - - this.unexpected() -} - -pp.flow_parsePostfixType = function () { - var node = this.startNode() - var type = node.elementType = this.flow_parsePrimaryType() - if (this.type === tt.bracketL) { - this.expect(tt.bracketL) - this.expect(tt.bracketR) - return this.finishNode(node, "ArrayTypeAnnotation") - } - return type -} - -pp.flow_parsePrefixType = function () { - var node = this.startNode() - if (this.eat(tt.question)) { - node.typeAnnotation = this.flow_parsePrefixType() - return this.finishNode(node, "NullableTypeAnnotation") - } - return this.flow_parsePostfixType() -} - -pp.flow_parseIntersectionType = function () { - var node = this.startNode() - var type = this.flow_parsePrefixType() - node.types = [type] - while (this.eat(tt.bitwiseAND)) { - node.types.push(this.flow_parsePrefixType()) - } - return node.types.length === 1 ? type : this.finishNode(node, "IntersectionTypeAnnotation") -} - -pp.flow_parseUnionType = function () { - var node = this.startNode() - var type = this.flow_parseIntersectionType() - node.types = [type] - while (this.eat(tt.bitwiseOR)) { - node.types.push(this.flow_parseIntersectionType()) - } - return node.types.length === 1 ? type : this.finishNode(node, "UnionTypeAnnotation") -} - -pp.flow_parseType = function () { - var oldInType = this.inType - this.inType = true - var type = this.flow_parseUnionType() - this.inType = oldInType - return type -} - -pp.flow_parseTypeAnnotation = function () { - var node = this.startNode() - node.typeAnnotation = this.flow_parseTypeInitialiser() - return this.finishNode(node, "TypeAnnotation") -} - -pp.flow_parseTypeAnnotatableIdentifier = function (requireTypeAnnotation, canBeOptionalParam) { - var node = this.startNode() - var ident = this.parseIdent() - var isOptionalParam = false - - if (canBeOptionalParam && this.eat(tt.question)) { - this.expect(tt.question) - isOptionalParam = true - } - - if (requireTypeAnnotation || this.type === tt.colon) { - ident.typeAnnotation = this.flow_parseTypeAnnotation() - this.finishNode(ident, ident.type) - } - - if (isOptionalParam) { - ident.optional = true - this.finishNode(ident, ident.type) - } - - return ident -} - -acorn.plugins.flow = function (instance) { - // function name(): string {} - instance.extend("parseFunctionBody", function (inner) { - return function (node, allowExpression) { - if (this.type === tt.colon) { - node.returnType = this.flow_parseTypeAnnotation() - } - - return inner.call(this, node, allowExpression) - } - }) - - instance.extend("parseStatement", function (inner) { - return function(declaration, topLevel) { - // strict mode handling of `interface` since it's a reserved word - if (this.strict && this.type === tt.name && this.value === "interface") { - var node = this.startNode() - this.next() - return this.flow_parseInterface(node) - } else { - return inner.call(this, declaration, topLevel) - } - } - }) - - instance.extend("parseExpressionStatement", function (inner) { - return function (node, expr) { - if (expr.type === "Identifier") { - if (expr.name === "declare") { - if (this.type === tt._class || this.type === tt.name || this.type === tt._function || this.type === tt._var) { - return this.flow_parseDeclare(node) - } - } else if (this.type === tt.name) { - if (expr.name === "interface") { - return this.flow_parseInterface(node) - } else if (expr.name === "type") { - return this.flow_parseTypeAlias(node) - } - } - } - - return inner.call(this, node, expr) - } - }) - - instance.extend("shouldParseExportDeclaration", function (inner) { - return function () { - return this.isContextual("type") || inner.call(this) - } - }) - - instance.extend("parseParenItem", function (inner) { - return function (node, start) { - if (this.type === tt.colon) { - var typeCastNode = this.startNodeAt(start) - typeCastNode.expression = node - typeCastNode.typeAnnotation = this.flow_parseTypeAnnotation() - return this.finishNode(typeCastNode, "TypeCastExpression") - } else { - return node - } - } - }) - - instance.extend("parseClassId", function (inner) { - return function (node, isStatement) { - inner.call(this, node, isStatement) - if (this.isRelational("<")) { - node.typeParameters = this.flow_parseTypeParameterDeclaration() - } - } - }) - - // don't consider `void` to be a keyword as then it'll use the void token type - // and set startExpr - instance.extend("isKeyword", function (inner) { - return function(name) { - if (this.inType && name === "void") { - return false - } else { - return inner.call(this, name) - } - } - }) - - instance.extend("readToken", function (inner) { - return function(code) { - if (this.inType && (code === 62 || code === 60)) { - return this.finishOp(tt.relational, 1) - } else { - return inner.call(this, code) - } - } - }) - - instance.extend("jsx_readToken", function (inner) { - return function () { - if (!this.inType) return inner.call(this) - } - }) - - instance.extend("parseParenArrowList", function (inner) { - return function (start, exprList, isAsync) { - for (var i = 0; i < exprList.length; i++) { - var listItem = exprList[i] - if (listItem.type === "TypeCastExpression") { - var expr = listItem.expression - expr.typeAnnotation = listItem.typeAnnotation - exprList[i] = expr - } - } - return inner.call(this, start, exprList, isAsync) - } - }) - - instance.extend("parseClassProperty", function (inner) { - return function (node) { - if (this.type === tt.colon) { - node.typeAnnotation = this.flow_parseTypeAnnotation() - } - return inner.call(this, node) - } - }) - instance.extend("isClassProperty", function (inner) { - return function () { - return this.type === tt.colon || inner.call(this) - } - }) - - instance.extend("parseClassMethod", function (inner) { - return function (classBody, method, isGenerator, isAsync) { - var typeParameters - if (this.isRelational("<")) { - typeParameters = this.flow_parseTypeParameterDeclaration() - } - method.value = this.parseMethod(isGenerator, isAsync) - method.value.typeParameters = typeParameters - classBody.body.push(this.finishNode(method, "MethodDefinition")) - } - }) - - instance.extend("parseClassSuper", function (inner) { - return function (node, isStatement) { - inner.call(this, node, isStatement) - if (node.superClass && this.isRelational("<")) { - node.superTypeParameters = this.flow_parseTypeParameterInstantiation() - } - if (this.isContextual("implements")) { - this.next() - var implemented = node.implements = [] - do { - var node = this.startNode() - node.id = this.parseIdent() - if (this.isRelational("<")) { - node.typeParameters = this.flow_parseTypeParameterInstantiation() - } else { - node.typeParameters = null - } - implemented.push(this.finishNode(node, "ClassImplements")) - } while(this.eat(tt.comma)) - } - } - }) - - instance.extend("parseObjPropValue", function (inner) { - return function (prop) { - var typeParameters - if (this.isRelational("<")) { - typeParameters = this.flow_parseTypeParameterDeclaration() - if (this.type !== tt.parenL) this.unexpected() - } - inner.apply(this, arguments) - prop.value.typeParameters = typeParameters - } - }) - - instance.extend("parseAssignableListItemTypes", function (inner) { - return function (param) { - if (this.eat(tt.question)) { - param.optional = true - } - if (this.type === tt.colon) { - param.typeAnnotation = this.flow_parseTypeAnnotation() - } - this.finishNode(param, param.type) - return param - } - }) - - instance.extend("parseImportSpecifiers", function (inner) { - return function (node) { - node.isType = false - if (this.isContextual("type")) { - var start = this.markPosition() - var typeId = this.parseIdent() - if ((this.type === tt.name && this.value !== "from") || this.type === tt.braceL || this.type === tt.star) { - node.isType = true - } else { - node.specifiers.push(this.parseImportSpecifierDefault(typeId, start)) - if (this.isContextual("from")) return - this.eat(tt.comma) - } - } - inner.call(this, node) - } - }) - - // function foo() {} - instance.extend("parseFunctionParams", function (inner) { - return function (node) { - if (this.isRelational("<")) { - node.typeParameters = this.flow_parseTypeParameterDeclaration() - } - inner.call(this, node) - } - }) - - // var foo: string = bar - instance.extend("parseVarHead", function (inner) { - return function (decl) { - inner.call(this, decl) - if (this.type === tt.colon) { - decl.id.typeAnnotation = this.flow_parseTypeAnnotation() - this.finishNode(decl.id, decl.id.type) - } - } - }) -} diff --git a/src/expression.js b/src/expression.js index ba94cd5249..2126431a17 100755 --- a/src/expression.js +++ b/src/expression.js @@ -16,56 +16,56 @@ // // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser -import {types as tt} from "./tokentype" -import {Parser} from "./state" -import {reservedWords} from "./identifier" -import {has} from "./util" +import {types as tt} from "./tokentype"; +import {Parser} from "./state"; +import {reservedWords} from "./identifier"; +import {has} from "./util"; -const pp = Parser.prototype +const pp = Parser.prototype; // Check if property name clashes with already added. // Object/class getters and setters are not allowed to clash — // either with each other or with an init property — and in // strict mode, init properties are also not allowed to be repeated. -pp.checkPropClash = function(prop, propHash) { +pp.checkPropClash = function (prop, propHash) { if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) - return - let key = prop.key, name + return; + let key = prop.key, name; switch (key.type) { - case "Identifier": name = key.name; break - case "Literal": name = String(key.value); break - default: return + case "Identifier": name = key.name; break; + case "Literal": name = String(key.value); break; + default: return; } - let kind = prop.kind + let kind = prop.kind; if (this.options.ecmaVersion >= 6) { if (name === "__proto__" && kind === "init") { if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property"); - propHash.proto = true + propHash.proto = true; } - return + return; } - let other - if (has(propHash, name)) { - other = propHash[name] - let isGetSet = kind !== "init" + let other; + if (propHash[name]) { + other = propHash[name]; + let isGetSet = kind !== "init"; if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init)) - this.raise(key.start, "Redefinition of property") + this.raise(key.start, "Redefinition of property"); } else { other = propHash[name] = { init: false, get: false, set: false - } + }; } - other[kind] = true -} + other[kind] = true; +}; // ### Expression parsing // These nest, from the most general expression type at the top to // 'atomic', nondivisible expression types at the bottom. Most of -// the functions will simply let the function(s) below them parse, +// the functions will simply let the function (s) below them parse, // and, *if* the syntactic construct they handle is present, wrap // the AST node that the inner parser gave them in another node. @@ -76,87 +76,91 @@ pp.checkPropClash = function(prop, propHash) { // and object pattern might appear (so it's possible to raise // delayed syntax error at correct position). -pp.parseExpression = function(noIn, refShorthandDefaultPos) { - let start = this.markPosition() - let expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos) +pp.parseExpression = function (noIn, refShorthandDefaultPos) { + let startPos = this.start, startLoc = this.startLoc; + let expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos); if (this.type === tt.comma) { - let node = this.startNodeAt(start) - node.expressions = [expr] - while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos)) - return this.finishNode(node, "SequenceExpression") + let node = this.startNodeAt(startPos, startLoc); + node.expressions = [expr]; + while (this.eat(tt.comma)) { + node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos)); + } + return this.finishNode(node, "SequenceExpression"); } - return expr -} + return expr; +}; // Parse an assignment expression. This includes applications of // operators like `+=`. -pp.parseMaybeAssign = function(noIn, refShorthandDefaultPos, afterLeftParse) { - if (this.type == tt._yield && this.inGenerator) return this.parseYield() - - let failOnShorthandAssign - if (!refShorthandDefaultPos) { - refShorthandDefaultPos = {start: 0} - failOnShorthandAssign = true - } else { - failOnShorthandAssign = false +pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse) { + if (this.type === tt._yield && this.inGenerator) { + return this.parseYield(); } - let start = this.markPosition() - if (this.type == tt.parenL || this.type == tt.name) - this.potentialArrowAt = this.start - let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos) - if (afterLeftParse) left = afterLeftParse.call(this, left, start) + + let failOnShorthandAssign; + if (!refShorthandDefaultPos) { + refShorthandDefaultPos = {start: 0}; + failOnShorthandAssign = true; + } else { + failOnShorthandAssign = false; + } + let startPos = this.start, startLoc = this.startLoc; + if (this.type === tt.parenL || this.type === tt.name) + this.potentialArrowAt = this.start; + let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos); + if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc); if (this.type.isAssign) { - let node = this.startNodeAt(start) - node.operator = this.value - node.left = this.type === tt.eq ? this.toAssignable(left) : left - refShorthandDefaultPos.start = 0 // reset because shorthand default was used correctly - this.checkLVal(left) + let node = this.startNodeAt(startPos, startLoc); + node.operator = this.value; + node.left = this.type === tt.eq ? this.toAssignable(left) : left; + refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly + this.checkLVal(left); if (left.parenthesizedExpression) { - let errorMsg + let errorMsg; if (left.type === "ObjectPattern") { - errorMsg = "`({a}) = 0` use `({a} = 0)`" + errorMsg = "`({a}) = 0` use `({a} = 0)`"; } else if (left.type === "ArrayPattern") { - errorMsg = "`([a]) = 0` use `([a] = 0)`" + errorMsg = "`([a]) = 0` use `([a] = 0)`"; } if (errorMsg) { - this.raise(left.start, `You're trying to assign to a parenthesized expression, eg. instead of ${errorMsg}`) + this.raise(left.start, `You're trying to assign to a parenthesized expression, eg. instead of ${errorMsg}`); } } - this.next() - node.right = this.parseMaybeAssign(noIn) - return this.finishNode(node, "AssignmentExpression") + this.next(); + node.right = this.parseMaybeAssign(noIn); + return this.finishNode(node, "AssignmentExpression"); } else if (failOnShorthandAssign && refShorthandDefaultPos.start) { - this.unexpected(refShorthandDefaultPos.start) + this.unexpected(refShorthandDefaultPos.start); } - return left -} + return left; +}; // Parse a ternary conditional (`?:`) operator. -pp.parseMaybeConditional = function(noIn, refShorthandDefaultPos) { - let start = this.markPosition() - let expr = this.parseExprOps(noIn, refShorthandDefaultPos) - if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr +pp.parseMaybeConditional = function (noIn, refShorthandDefaultPos) { + let startPos = this.start, startLoc = this.startLoc; + let expr = this.parseExprOps(noIn, refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; if (this.eat(tt.question)) { - let node = this.startNodeAt(start) - node.test = expr - node.consequent = this.parseMaybeAssign() - this.expect(tt.colon) - node.alternate = this.parseMaybeAssign(noIn) - return this.finishNode(node, "ConditionalExpression") + let node = this.startNodeAt(startPos, startLoc); + node.test = expr; + node.consequent = this.parseMaybeAssign(); + this.expect(tt.colon); + node.alternate = this.parseMaybeAssign(noIn); + return this.finishNode(node, "ConditionalExpression"); } - return expr -} + return expr; +}; // Start the precedence parser. -pp.parseExprOps = function(noIn, refShorthandDefaultPos) { - let start = this.markPosition() - let expr = this.parseMaybeUnary(refShorthandDefaultPos) - if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr - return this.parseExprOp(expr, start, -1, noIn) -} +pp.parseExprOps = function (noIn, refShorthandDefaultPos) { + let startPos = this.start, startLoc = this.startLoc; + let expr = this.parseMaybeUnary(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + return this.parseExprOp(expr, startPos, startLoc, -1, noIn); +}; // Parse binary operators with the operator precedence parsing // algorithm. `left` is the left-hand side of the operator. @@ -164,141 +168,148 @@ pp.parseExprOps = function(noIn, refShorthandDefaultPos) { // defer further parser to one of its callers when it encounters an // operator that has a lower precedence than the set it is parsing. -pp.parseExprOp = function(left, leftStart, minPrec, noIn) { - let prec = this.type.binop +pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) { + let prec = this.type.binop; if (prec != null && (!noIn || this.type !== tt._in)) { if (prec > minPrec) { - let node = this.startNodeAt(leftStart) - node.left = left - node.operator = this.value - let op = this.type - this.next() - let start = this.markPosition() - node.right = this.parseExprOp(this.parseMaybeUnary(), start, op.rightAssociative ? (prec - 1) : prec, noIn) - this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression") - return this.parseExprOp(node, leftStart, minPrec, noIn) + let node = this.startNodeAt(leftStartPos, leftStartLoc); + node.left = left; + node.operator = this.value; + let op = this.type; + this.next(); + let startPos = this.start, startLoc = this.startLoc; + node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, op.rightAssociative ? prec - 1 : prec, noIn); + this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression"); + return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn); } } - return left -} + return left; +}; // Parse unary operators, both prefix and postfix. -pp.parseMaybeUnary = function(refShorthandDefaultPos) { +pp.parseMaybeUnary = function (refShorthandDefaultPos) { if (this.type.prefix) { - let node = this.startNode(), update = this.type === tt.incDec - node.operator = this.value - node.prefix = true - this.next() - node.argument = this.parseMaybeUnary() - if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start) - if (update) this.checkLVal(node.argument) + let node = this.startNode(), update = this.type === tt.incDec; + node.operator = this.value; + node.prefix = true; + this.next(); + node.argument = this.parseMaybeUnary(); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); + if (update) this.checkLVal(node.argument); else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") - this.raise(node.start, "Deleting local variable in strict mode") - return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression") + this.raise(node.start, "Deleting local variable in strict mode"); + return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); } - let start = this.markPosition() - let expr = this.parseExprSubscripts(refShorthandDefaultPos) - if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr + let startPos = this.start, startLoc = this.startLoc; + let expr = this.parseExprSubscripts(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; while (this.type.postfix && !this.canInsertSemicolon()) { - let node = this.startNodeAt(start) - node.operator = this.value - node.prefix = false - node.argument = expr - this.checkLVal(expr) - this.next() - expr = this.finishNode(node, "UpdateExpression") + let node = this.startNodeAt(startPos, startLoc); + node.operator = this.value; + node.prefix = false; + node.argument = expr; + this.checkLVal(expr); + this.next(); + expr = this.finishNode(node, "UpdateExpression"); } - return expr -} + return expr; +}; // Parse call, dot, and `[]`-subscript expressions. -pp.parseExprSubscripts = function(refShorthandDefaultPos) { - let start = this.markPosition() - let expr = this.parseExprAtom(refShorthandDefaultPos) - if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr - return this.parseSubscripts(expr, start) -} +pp.parseExprSubscripts = function (refShorthandDefaultPos) { + let startPos = this.start, startLoc = this.startLoc; + let expr = this.parseExprAtom(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) { + return expr; + } else { + return this.parseSubscripts(expr, startPos, startLoc); + } +}; -pp.parseSubscripts = function(base, start, noCalls) { - if (!noCalls && this.eat(tt.doubleColon)) { - let node = this.startNodeAt(start) - node.object = base - node.callee = this.parseNoCallExpr() - return this.parseSubscripts(this.finishNode(node, "BindExpression"), start, noCalls) - } else if (this.eat(tt.dot)) { - let node = this.startNodeAt(start) - node.object = base - node.property = this.parseIdent(true) - node.computed = false - return this.parseSubscripts(this.finishNode(node, "MemberExpression"), start, noCalls) - } else if (this.eat(tt.bracketL)) { - let node = this.startNodeAt(start) - node.object = base - node.property = this.parseExpression() - node.computed = true - this.expect(tt.bracketR) - return this.parseSubscripts(this.finishNode(node, "MemberExpression"), start, noCalls) - } else if (!noCalls && this.eat(tt.parenL)) { - let node = this.startNodeAt(start) - node.callee = base - node.arguments = this.parseExprList(tt.parenR, this.options.features["es7.trailingFunctionCommas"]) - return this.parseSubscripts(this.finishNode(node, "CallExpression"), start, noCalls) - } else if (this.type === tt.backQuote) { - let node = this.startNodeAt(start) - node.tag = base - node.quasi = this.parseTemplate() - return this.parseSubscripts(this.finishNode(node, "TaggedTemplateExpression"), start, noCalls) - } return base -} +pp.parseSubscripts = function(base, startPos, startLoc, noCalls) { + for (;;) { + if (!noCalls && this.eat(tt.doubleColon)) { + let node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.callee = this.parseNoCallExpr(); + return this.parseSubscripts(this.finishNode(node, "BindExpression"), startPos, startLoc, noCalls); + } else if (this.eat(tt.dot)) { + let node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.property = this.parseIdent(true); + node.computed = false; + base = this.finishNode(node, "MemberExpression"); + } else if (this.eat(tt.bracketL)) { + let node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.property = this.parseExpression(); + node.computed = true; + this.expect(tt.bracketR); + base = this.finishNode(node, "MemberExpression"); + } else if (!noCalls && this.eat(tt.parenL)) { + let node = this.startNodeAt(startPos, startLoc); + node.callee = base; + node.arguments = this.parseExprList(tt.parenR, this.options.features["es7.trailingFunctionCommas"]); + base = this.finishNode(node, "CallExpression"); + } else if (this.type === tt.backQuote) { + let node = this.startNodeAt(startPos, startLoc); + node.tag = base; + node.quasi = this.parseTemplate(); + base = this.finishNode(node, "TaggedTemplateExpression"); + } else { + return base; + } + } +}; // Parse a no-call expression (like argument of `new` or `::` operators). -pp.parseNoCallExpr = function() { - let start = this.markPosition() - return this.parseSubscripts(this.parseExprAtom(), start, true) -} +pp.parseNoCallExpr = function () { + let startPos = this.start, startLoc = this.startLoc; + return this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true); +}; // Parse an atomic expression — either a single token that is an // expression, an expression started by a keyword like `function` or // `new`, or an expression wrapped in punctuation like `()`, `[]`, // or `{}`. -pp.parseExprAtom = function(refShorthandDefaultPos) { - let node, canBeArrow = this.potentialArrowAt == this.start +pp.parseExprAtom = function (refShorthandDefaultPos) { + let node, canBeArrow = this.potentialArrowAt === this.start; switch (this.type) { case tt._super: if (!this.inFunction) - this.raise(this.start, "'super' outside of function or class") + this.raise(this.start, "'super' outside of function or class"); case tt._this: - let type = this.type === tt._this ? "ThisExpression" : "Super" - node = this.startNode() - this.next() - return this.finishNode(node, type) + let type = this.type === tt._this ? "ThisExpression" : "Super"; + node = this.startNode(); + this.next(); + return this.finishNode(node, type); case tt._yield: - if (this.inGenerator) this.unexpected() + if (this.inGenerator) this.unexpected(); case tt._do: if (this.options.features["es7.doExpressions"]) { - let node = this.startNode() - this.next() - var oldInFunction = this.inFunction - var oldLabels = this.labels - this.labels = [] - this.inFunction = false - node.body = this.parseBlock() - this.inFunction = oldInFunction - this.labels = oldLabels - return this.finishNode(node, "DoExpression") + let node = this.startNode(); + this.next(); + var oldInFunction = this.inFunction; + var oldLabels = this.labels; + this.labels = []; + this.inFunction = false; + node.body = this.parseBlock(); + this.inFunction = oldInFunction; + this.labels = oldLabels; + return this.finishNode(node, "DoExpression"); } case tt.name: - let start = this.markPosition() - node = this.startNode() - let id = this.parseIdent(this.type !== tt.name) + let startPos = this.start, startLoc = this.startLoc; + node = this.startNode(); + let id = this.parseIdent(true); // if (this.options.features["es7.asyncFunctions"]) { @@ -306,441 +317,453 @@ pp.parseExprAtom = function(refShorthandDefaultPos) { if (id.name === "async" && !this.canInsertSemicolon()) { // arrow functions if (this.type === tt.parenL) { - let expr = this.parseParenAndDistinguishExpression(start, true, true) + let expr = this.parseParenAndDistinguishExpression(startPos, startLoc, true, true); if (expr && expr.type === "ArrowFunctionExpression") { - return expr + return expr; } else { - node.callee = id + node.callee = id; if (!expr) { - node.arguments = [] + node.arguments = []; } else if (expr.type === "SequenceExpression") { - node.arguments = expr.expressions + node.arguments = expr.expressions; } else { - node.arguments = [expr] + node.arguments = [expr]; } - return this.parseSubscripts(this.finishNode(node, "CallExpression"), start) + return this.parseSubscripts(this.finishNode(node, "CallExpression"), startPos, startLoc); } } else if (this.type === tt.name) { - id = this.parseIdent() - this.expect(tt.arrow) - return this.parseArrowExpression(node, [id], true) + id = this.parseIdent(); + this.expect(tt.arrow); + return this.parseArrowExpression(node, [id], true); } // normal functions if (this.type === tt._function && !this.canInsertSemicolon()) { - this.next() - return this.parseFunction(node, false, false, true) + this.next(); + return this.parseFunction(node, false, false, true); } } else if (id.name === "await") { - if (this.inAsync) return this.parseAwait(node) + if (this.inAsync) return this.parseAwait(node); } } // if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) - return this.parseArrowExpression(this.startNodeAt(start), [id]) - return id + return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id]); + + return id; case tt.regexp: - let value = this.value - node = this.parseLiteral(value.value) - node.regex = {pattern: value.pattern, flags: value.flags} - return node + let value = this.value; + node = this.parseLiteral(value.value); + node.regex = {pattern: value.pattern, flags: value.flags}; + return node; case tt.num: case tt.string: - return this.parseLiteral(this.value) + return this.parseLiteral(this.value); case tt._null: case tt._true: case tt._false: - node = this.startNode() - node.value = this.type === tt._null ? null : this.type === tt._true - node.raw = this.type.keyword - this.next() - return this.finishNode(node, "Literal") + node = this.startNode(); + node.value = this.type === tt._null ? null : this.type === tt._true; + node.raw = this.type.keyword; + this.next(); + return this.finishNode(node, "Literal"); case tt.parenL: - return this.parseParenAndDistinguishExpression(null, null, canBeArrow) + return this.parseParenAndDistinguishExpression(null, null, canBeArrow); case tt.bracketL: - node = this.startNode() - this.next() + node = this.startNode(); + this.next(); // check whether this is array comprehension or regular array if ((this.options.ecmaVersion >= 7 || this.options.features["es7.comprehensions"]) && this.type === tt._for) { - return this.parseComprehension(node, false) + return this.parseComprehension(node, false); } - node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos) - return this.finishNode(node, "ArrayExpression") + node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos); + return this.finishNode(node, "ArrayExpression"); case tt.braceL: - return this.parseObj(false, refShorthandDefaultPos) + return this.parseObj(false, refShorthandDefaultPos); case tt._function: - node = this.startNode() - this.next() - return this.parseFunction(node, false) + node = this.startNode(); + this.next(); + return this.parseFunction(node, false); case tt.at: - this.parseDecorators() + this.parseDecorators(); case tt._class: - node = this.startNode() - this.takeDecorators(node) - return this.parseClass(node, false) + node = this.startNode(); + this.takeDecorators(node); + return this.parseClass(node, false); case tt._new: - return this.parseNew() + return this.parseNew(); case tt.backQuote: - return this.parseTemplate() + return this.parseTemplate(); case tt.doubleColon: - node = this.startNode() - this.next() - node.object = null - let callee = node.callee = this.parseNoCallExpr() - if (callee.type !== "MemberExpression") - this.raise(callee.start, "Binding should be performed on object property.") - return this.finishNode(node, "BindExpression") - - default: - this.unexpected() - } -} - -pp.parseLiteral = function(value) { - let node = this.startNode() - node.value = value - node.raw = this.input.slice(this.start, this.end) - this.next() - return this.finishNode(node, "Literal") -} - -pp.parseParenExpression = function() { - this.expect(tt.parenL) - let val = this.parseExpression() - this.expect(tt.parenR) - return val -} - -pp.parseParenAndDistinguishExpression = function(start, isAsync, canBeArrow) { - start = start || this.markPosition() - let val - if (this.options.ecmaVersion >= 6) { - this.next() - - if ((this.options.features["es7.comprehensions"] || this.options.ecmaVersion >= 7) && this.type === tt._for) { - return this.parseComprehension(this.startNodeAt(start), true) + node = this.startNode(); + this.next(); + node.object = null; + let callee = node.callee = this.parseNoCallExpr(); + if (callee.type === "MemberExpression") { + return this.finishNode(node, "BindExpression"); + } else { + this.raise(callee.start, "Binding should be performed on object property."); } - let innerStart = this.markPosition(), exprList = [], first = true - let refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart, optionalCommaStart + default: + this.unexpected(); + } +}; + +pp.parseLiteral = function (value) { + let node = this.startNode(); + node.value = value; + node.raw = this.input.slice(this.start, this.end); + this.next(); + return this.finishNode(node, "Literal"); +}; + +pp.parseParenExpression = function () { + this.expect(tt.parenL); + let val = this.parseExpression(); + this.expect(tt.parenR); + return val; +}; + +pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow, isAsync) { + startPos = startPos || this.start; + startLoc = startLoc || this.startLoc; + let val; + if (this.options.ecmaVersion >= 6) { + this.next(); + + if ((this.options.features["es7.comprehensions"] || this.options.ecmaVersion >= 7) && this.type === tt._for) { + return this.parseComprehension(this.startNodeAt(startPos, startLoc), true); + } + + let innerStartPos = this.start, innerStartLoc = this.startLoc; + let exprList = [], first = true; + let refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart, optionalCommaStart; while (this.type !== tt.parenR) { if (first) { first = false; } else { - this.expect(tt.comma) + this.expect(tt.comma); if (this.type === tt.parenR && this.options.features["es7.trailingFunctionCommas"]) { - optionalCommaStart = this.start - break + optionalCommaStart = this.start; + break; } } if (this.type === tt.ellipsis) { - let spreadNodeStart = this.markPosition() - spreadStart = this.start - exprList.push(this.parseParenItem(this.parseRest(), spreadNodeStart)) - break + let spreadNodeStartPos = this.start, spreadNodeStartLoc = this.startLoc; + spreadStart = this.start; + exprList.push(this.parseParenItem(this.parseRest(), spreadNodeStartLoc, spreadNodeStartPos)); + break; } else { if (this.type === tt.parenL && !innerParenStart) { - innerParenStart = this.start + innerParenStart = this.start; } - exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem)) + exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem)); } } - let innerEnd = this.markPosition() - this.expect(tt.parenR) + let innerEndPos = this.start, innerEndLoc = this.startLoc; + this.expect(tt.parenR); if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) { - if (innerParenStart) this.unexpected(innerParenStart) - return this.parseParenArrowList(start, exprList, isAsync) + if (innerParenStart) this.unexpected(innerParenStart); + return this.parseParenArrowList(startPos, startLoc, exprList, isAsync); } if (!exprList.length) { if (isAsync) { - return + return; } else { - this.unexpected(this.lastTokStart) + this.unexpected(this.lastTokStart); } } - if (optionalCommaStart) this.unexpected(optionalCommaStart) - if (spreadStart) this.unexpected(spreadStart) - if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start) + if (optionalCommaStart) this.unexpected(optionalCommaStart); + if (spreadStart) this.unexpected(spreadStart); + if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); if (exprList.length > 1) { - val = this.startNodeAt(innerStart) - val.expressions = exprList - this.finishNodeAt(val, "SequenceExpression", innerEnd) + val = this.startNodeAt(innerStartPos, innerStartLoc); + val.expressions = exprList; + this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc); } else { - val = exprList[0] + val = exprList[0]; } } else { - val = this.parseParenExpression() + val = this.parseParenExpression(); } - if (this.options.preserveParens) { - let par = this.startNodeAt(start) - par.expression = val - return this.finishNode(par, "ParenthesizedExpression") - } else { - val.parenthesizedExpression = true - return val - } -} + val.parenthesizedExpression = true; + return val; +}; -pp.parseParenArrowList = function(start, exprList, isAsync) { - return this.parseArrowExpression(this.startNodeAt(start), exprList, isAsync) -} +pp.parseParenArrowList = function (startPos, startLoc, exprList, isAsync) { + return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, isAsync); +}; -pp.parseParenItem = function(node, start) { - return node -} +pp.parseParenItem = function (node) { + return node; +}; // New's precedence is slightly tricky. It must allow its argument // to be a `[]` or dot subscript expression, but not a call — at // least, not without wrapping it in parentheses. Thus, it uses the -const empty = [] +pp.parseNew = function () { + let node = this.startNode(); + let meta = this.parseIdent(true); -pp.parseNew = function() { - let node = this.startNode() - let meta = this.parseIdent(true) if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) { - node.meta = meta - node.property = this.parseIdent(true) - if (node.property.name !== "target") - this.raise(node.property.start, "The only valid meta property for new is new.target") - return this.finishNode(node, "MetaProperty") + node.meta = meta; + node.property = this.parseIdent(true); + + if (node.property.name !== "target") { + this.raise(node.property.start, "The only valid meta property for new is new.target"); + } + + return this.finishNode(node, "MetaProperty"); } - node.callee = this.parseNoCallExpr() - if (this.eat(tt.parenL)) node.arguments = this.parseExprList( - tt.parenR, - this.options.features["es7.trailingFunctionCommas"] - ) - else node.arguments = empty - return this.finishNode(node, "NewExpression") -} + + node.callee = this.parseNoCallExpr(); + + if (this.eat(tt.parenL)) { + node.arguments = this.parseExprList(tt.parenR, this.options.features["es7.trailingFunctionCommas"]); + } else { + node.arguments = []; + } + + return this.finishNode(node, "NewExpression"); +}; // Parse template expression. -pp.parseTemplateElement = function() { - let elem = this.startNode() +pp.parseTemplateElement = function () { + let elem = this.startNode(); elem.value = { - raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, '\n'), + raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"), cooked: this.value - } - this.next() - elem.tail = this.type === tt.backQuote - return this.finishNode(elem, "TemplateElement") -} + }; + this.next(); + elem.tail = this.type === tt.backQuote; + return this.finishNode(elem, "TemplateElement"); +}; -pp.parseTemplate = function() { - let node = this.startNode() - this.next() - node.expressions = [] - let curElt = this.parseTemplateElement() - node.quasis = [curElt] +pp.parseTemplate = function () { + let node = this.startNode(); + this.next(); + node.expressions = []; + let curElt = this.parseTemplateElement(); + node.quasis = [curElt]; while (!curElt.tail) { - this.expect(tt.dollarBraceL) - node.expressions.push(this.parseExpression()) - this.expect(tt.braceR) - node.quasis.push(curElt = this.parseTemplateElement()) + this.expect(tt.dollarBraceL); + node.expressions.push(this.parseExpression()); + this.expect(tt.braceR); + node.quasis.push(curElt = this.parseTemplateElement()); } - this.next() - return this.finishNode(node, "TemplateLiteral") -} + this.next(); + return this.finishNode(node, "TemplateLiteral"); +}; // Parse an object literal or binding pattern. -pp.parseObj = function(isPattern, refShorthandDefaultPos) { - let node = this.startNode(), first = true, propHash = {} - node.properties = [] - let decorators = [] - this.next() +pp.parseObj = function (isPattern, refShorthandDefaultPos) { + let node = this.startNode(), first = true, propHash = Object.create(null); + node.properties = []; + let decorators = []; + this.next(); while (!this.eat(tt.braceR)) { - if (!first) { - this.expect(tt.comma) - if (this.afterTrailingComma(tt.braceR)) break - } else first = false - while (this.type === tt.at) { - decorators.push(this.parseDecorator()) + if (first) { + first = false; + } else { + this.expect(tt.comma); + if (this.afterTrailingComma(tt.braceR)) break; } - let prop = this.startNode(), isGenerator = false, isAsync = false, start + + while (this.type === tt.at) { + decorators.push(this.parseDecorator()); + } + + let prop = this.startNode(), isGenerator = false, isAsync = false, startPos, startLoc; if (decorators.length) { - prop.decorators = decorators - decorators = [] + prop.decorators = decorators; + decorators = []; } if (this.options.features["es7.objectRestSpread"] && this.type === tt.ellipsis) { - prop = this.parseSpread() - prop.type = "SpreadProperty" - node.properties.push(prop) - continue + prop = this.parseSpread(); + prop.type = "SpreadProperty"; + node.properties.push(prop); + continue; } if (this.options.ecmaVersion >= 6) { - prop.method = false - prop.shorthand = false - if (isPattern || refShorthandDefaultPos) - start = this.markPosition() + prop.method = false; + prop.shorthand = false; + if (isPattern || refShorthandDefaultPos) { + startPos = this.start; + startLoc = this.startLoc; + } if (!isPattern) - isGenerator = this.eat(tt.star) + isGenerator = this.eat(tt.star); } if (this.options.features["es7.asyncFunctions"] && this.isContextual("async")) { - if (isGenerator || isPattern) this.unexpected() - var asyncId = this.parseIdent() + if (isGenerator || isPattern) this.unexpected(); + var asyncId = this.parseIdent(); if (this.type === tt.colon || this.type === tt.parenL) { - prop.key = asyncId + prop.key = asyncId; } else { - isAsync = true - this.parsePropertyName(prop) + isAsync = true; + this.parsePropertyName(prop); } } else { - this.parsePropertyName(prop) + this.parsePropertyName(prop); } - this.parseObjPropValue(prop, start, isGenerator, isAsync, isPattern, refShorthandDefaultPos); - this.checkPropClash(prop, propHash) - node.properties.push(this.finishNode(prop, "Property")) + this.parseObjPropValue(prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos); + this.checkPropClash(prop, propHash); + node.properties.push(this.finishNode(prop, "Property")); } if (decorators.length) { this.raise(this.start, "You have trailing decorators with no property"); } - return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression") -} + return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression"); +}; -pp.parseObjPropValue = function (prop, start, isGenerator, isAsync, isPattern, refShorthandDefaultPos) { +pp.parseObjPropValue = function (prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos) { if (this.eat(tt.colon)) { - prop.value = isPattern ? this.parseMaybeDefault() : this.parseMaybeAssign(false, refShorthandDefaultPos) - prop.kind = "init" + prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos); + prop.kind = "init"; } else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) { - if (isPattern) this.unexpected() - prop.kind = "init" - prop.method = true - prop.value = this.parseMethod(isGenerator, isAsync) + if (isPattern) this.unexpected(); + prop.kind = "init"; + prop.method = true; + prop.value = this.parseMethod(isGenerator, isAsync); } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && - (this.type != tt.comma && this.type != tt.braceR)) { - if (isGenerator || isAsync || isPattern) this.unexpected() - prop.kind = prop.key.name - this.parsePropertyName(prop) - prop.value = this.parseMethod(false) - let paramCount = prop.kind === "get" ? 0 : 1 + (this.type !== tt.comma && this.type !== tt.braceR)) { + if (isGenerator || isAsync || isPattern) this.unexpected(); + prop.kind = prop.key.name; + this.parsePropertyName(prop); + prop.value = this.parseMethod(false); + let paramCount = prop.kind === "get" ? 0 : 1; if (prop.value.params.length !== paramCount) { - let start = prop.value.start + let start = prop.value.start; if (prop.kind === "get") this.raise(start, "getter should have no params"); else - this.raise(start, "setter should have exactly one param") + this.raise(start, "setter should have exactly one param"); } } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") { - prop.kind = "init" + prop.kind = "init"; if (isPattern) { if (this.isKeyword(prop.key.name) || (this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) || (!this.options.allowReserved && this.isReservedWord(prop.key.name))) - this.raise(prop.key.start, "Binding " + prop.key.name) - prop.value = this.parseMaybeDefault(start, prop.key) + this.raise(prop.key.start, "Binding " + prop.key.name); + prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); } else if (this.type === tt.eq && refShorthandDefaultPos) { if (!refShorthandDefaultPos.start) - refShorthandDefaultPos.start = this.start - prop.value = this.parseMaybeDefault(start, prop.key) + refShorthandDefaultPos.start = this.start; + prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); } else { - prop.value = prop.key + prop.value = prop.key; } - prop.shorthand = true - } else this.unexpected() -} + prop.shorthand = true; + } else { + this.unexpected(); + } +}; -pp.parsePropertyName = function(prop) { +pp.parsePropertyName = function (prop) { if (this.options.ecmaVersion >= 6) { if (this.eat(tt.bracketL)) { - prop.computed = true - prop.key = this.parseMaybeAssign() - this.expect(tt.bracketR) - return prop.key + prop.computed = true; + prop.key = this.parseMaybeAssign(); + this.expect(tt.bracketR); + return prop.key; } else { - prop.computed = false + prop.computed = false; } } - return prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true) -} + return prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true); +}; // Initialize empty function node. -pp.initFunction = function(node, isAsync) { - node.id = null +pp.initFunction = function (node, isAsync) { + node.id = null; if (this.options.ecmaVersion >= 6) { - node.generator = false - node.expression = false + node.generator = false; + node.expression = false; } if (this.options.features["es7.asyncFunctions"]) { - node.async = !!isAsync + node.async = !!isAsync; } -} +}; // Parse object or class method. -pp.parseMethod = function(isGenerator, isAsync) { - let node = this.startNode() - this.initFunction(node, isAsync) - this.expect(tt.parenL) - node.params = this.parseBindingList(tt.parenR, false, this.options.features["es7.trailingFunctionCommas"]) +pp.parseMethod = function (isGenerator, isAsync) { + let node = this.startNode(); + this.initFunction(node, isAsync); + this.expect(tt.parenL); + node.params = this.parseBindingList(tt.parenR, false, this.options.features["es7.trailingFunctionCommas"]); if (this.options.ecmaVersion >= 6) { - node.generator = isGenerator + node.generator = isGenerator; } - this.parseFunctionBody(node) - return this.finishNode(node, "FunctionExpression") -} + this.parseFunctionBody(node); + return this.finishNode(node, "FunctionExpression"); +}; // Parse arrow function expression with given parameters. -pp.parseArrowExpression = function(node, params, isAsync) { - this.initFunction(node, isAsync) - node.params = this.toAssignableList(params, true) - this.parseFunctionBody(node, true) - return this.finishNode(node, "ArrowFunctionExpression") -} +pp.parseArrowExpression = function (node, params, isAsync) { + this.initFunction(node, isAsync); + node.params = this.toAssignableList(params, true); + this.parseFunctionBody(node, true); + return this.finishNode(node, "ArrowFunctionExpression"); +}; // Parse function body and check parameters. -pp.parseFunctionBody = function(node, allowExpression) { - let isExpression = allowExpression && this.type !== tt.braceL +pp.parseFunctionBody = function (node, allowExpression) { + let isExpression = allowExpression && this.type !== tt.braceL; - var oldInAsync = this.inAsync - this.inAsync = node.async + var oldInAsync = this.inAsync; + this.inAsync = node.async; if (isExpression) { - node.body = this.parseMaybeAssign() - node.expression = true + node.body = this.parseMaybeAssign(); + node.expression = true; } else { // Start a new scope with regard to labels and the `inFunction` // flag (restore them to their old value afterwards). - let oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels - this.inFunction = true; this.inGenerator = node.generator; this.labels = [] - node.body = this.parseBlock(true) - node.expression = false - this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels + let oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels; + this.inFunction = true; this.inGenerator = node.generator; this.labels = []; + node.body = this.parseBlock(true); + node.expression = false; + this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels; } - this.inAsync = oldInAsync + this.inAsync = oldInAsync; // If this is a strict mode function, verify that argument names // are not repeated, and it does not try to bind the words `eval` // or `arguments`. if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) { - let nameHash = {}, oldStrict = this.strict - this.strict = true + let nameHash = Object.create(null), oldStrict = this.strict; + this.strict = true; if (node.id) - this.checkLVal(node.id, true) + this.checkLVal(node.id, true); for (let i = 0; i < node.params.length; i++) - this.checkLVal(node.params[i], true, nameHash) - this.strict = oldStrict + this.checkLVal(node.params[i], true, nameHash); + this.strict = oldStrict; } -} +}; // Parses a comma-separated list of expressions, and returns them as // an array. `close` is the token type that ends the list, and @@ -748,94 +771,97 @@ pp.parseFunctionBody = function(node, allowExpression) { // nothing in between them to be parsed as `null` (which is needed // for array literals). -pp.parseExprList = function(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) { - let elts = [], first = true +pp.parseExprList = function (close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) { + let elts = [], first = true; while (!this.eat(close)) { - if (!first) { - this.expect(tt.comma) - if (allowTrailingComma && this.afterTrailingComma(close)) break - } else first = false - - if (allowEmpty && this.type === tt.comma) { - elts.push(null) + if (first) { + first = false; } else { - if (this.type === tt.ellipsis) - elts.push(this.parseSpread(refShorthandDefaultPos)) - else - elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos)) + this.expect(tt.comma); + if (allowTrailingComma && this.afterTrailingComma(close)) break; } + + let elt; + if (allowEmpty && this.type === tt.comma) { + elt = null; + } else if (this.type === tt.ellipsis) { + elt = this.parseSpread(refShorthandDefaultPos); + } else { + elt = this.parseMaybeAssign(false, refShorthandDefaultPos); + } + elts.push(elt); } - return elts -} + return elts; +}; // Parse the next token as an identifier. If `liberal` is true (used // when parsing properties), it will also convert keywords into // identifiers. -pp.parseIdent = function(liberal) { - let node = this.startNode() - if (liberal && this.options.allowReserved == "never") liberal = false +pp.parseIdent = function (liberal) { + let node = this.startNode(); + if (liberal && this.options.allowReserved === "never") liberal = false; if (this.type === tt.name) { if (!liberal && ((!this.options.allowReserved && this.isReservedWord(this.value)) || (this.strict && reservedWords.strict(this.value)) && (this.options.ecmaVersion >= 6 || - this.input.slice(this.start, this.end).indexOf("\\") == -1))) - this.raise(this.start, "The keyword '" + this.value + "' is reserved") - node.name = this.value + this.input.slice(this.start, this.end).indexOf("\\") === -1))) + this.raise(this.start, "The keyword '" + this.value + "' is reserved"); + node.name = this.value; } else if (liberal && this.type.keyword) { - node.name = this.type.keyword + node.name = this.type.keyword; } else { - this.unexpected() + this.unexpected(); } - this.next() - return this.finishNode(node, "Identifier") -} + this.next(); + return this.finishNode(node, "Identifier"); +}; // Parses await expression inside async function. pp.parseAwait = function (node) { if (this.eat(tt.semi) || this.canInsertSemicolon()) { - this.unexpected() + this.unexpected(); } - node.all = this.eat(tt.star) - node.argument = this.parseMaybeUnary() - return this.finishNode(node, "AwaitExpression") + node.all = this.eat(tt.star); + node.argument = this.parseMaybeUnary(); + return this.finishNode(node, "AwaitExpression"); }; // Parses yield expression inside generator. -pp.parseYield = function() { - let node = this.startNode() - this.next() - if (this.type == tt.semi || this.canInsertSemicolon() || (this.type != tt.star && !this.type.startsExpr)) { - node.delegate = false - node.argument = null +pp.parseYield = function () { + let node = this.startNode(); + this.next(); + if (this.type === tt.semi || this.canInsertSemicolon() || (this.type !== tt.star && !this.type.startsExpr)) { + node.delegate = false; + node.argument = null; } else { - node.delegate = this.eat(tt.star) - node.argument = this.parseMaybeAssign() + node.delegate = this.eat(tt.star); + node.argument = this.parseMaybeAssign(); } - return this.finishNode(node, "YieldExpression") -} + return this.finishNode(node, "YieldExpression"); +}; // Parses array and generator comprehensions. -pp.parseComprehension = function(node, isGenerator) { - node.blocks = [] +pp.parseComprehension = function (node, isGenerator) { + node.blocks = []; while (this.type === tt._for) { - let block = this.startNode() - this.next() - this.expect(tt.parenL) - block.left = this.parseBindingAtom() - this.checkLVal(block.left, true) - this.expectContextual("of") - block.right = this.parseExpression() - this.expect(tt.parenR) - node.blocks.push(this.finishNode(block, "ComprehensionBlock")) + let block = this.startNode(); + this.next(); + this.expect(tt.parenL); + block.left = this.parseBindingAtom(); + this.checkLVal(block.left, true); + this.expectContextual("of"); + block.right = this.parseExpression(); + this.expect(tt.parenR); + node.blocks.push(this.finishNode(block, "ComprehensionBlock")); } - node.filter = this.eat(tt._if) ? this.parseParenExpression() : null - node.body = this.parseExpression() - this.expect(isGenerator ? tt.parenR : tt.bracketR) - node.generator = isGenerator - return this.finishNode(node, "ComprehensionExpression") -} + node.filter = this.eat(tt._if) ? this.parseParenExpression() : null; + node.body = this.parseExpression(); + this.expect(isGenerator ? tt.parenR : tt.bracketR); + node.generator = isGenerator; + return this.finishNode(node, "ComprehensionExpression"); +}; diff --git a/src/identifier.js b/src/identifier.js index 11713b05d6..a656c92320 100755 --- a/src/identifier.js +++ b/src/identifier.js @@ -8,10 +8,10 @@ // It starts by sorting the words by length. function makePredicate(words) { - words = words.split(" ") - return function(str) { - return words.indexOf(str) >= 0 - } + words = words.split(" "); + return function (str) { + return words.indexOf(str) >= 0; + }; } // Reserved word lists for various dialects of the language @@ -22,16 +22,16 @@ export const reservedWords = { 6: makePredicate("enum await"), strict: makePredicate("implements interface let package private protected public static yield"), strictBind: makePredicate("eval arguments") -} +}; // And the keywords -var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this" +var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"; export const keywords = { 5: makePredicate(ecma5AndLessKeywords), 6: makePredicate(ecma5AndLessKeywords + " let const class extends export import yield super") -} +}; // ## Character categories @@ -41,57 +41,75 @@ export const keywords = { // code point above 128. // Generated by `tools/generate-identifier-regex.js`. -let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b2\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc" -let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2d\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f" +let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b2\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; +let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2d\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; -const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]") -const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]") +const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); +const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); -nonASCIIidentifierStartChars = nonASCIIidentifierChars = null +nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; // These are a run-length and offset encoded representation of the // >0xffff code points that are a valid part of identifiers. The // offset starts at 0x10000, and each pair of numbers represents an // offset to the next range, and then a size of the range. They were // generated by tools/generate-identifier-regex.js -var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541] -var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239] +var astralIdentifierStartCodes = [ + 0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 17, 26, 6, 37, 11, 29, 3, 35, 5, + 7, 2, 4, 43, 157, 99, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, + 98, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 26, 45, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, + 17, 111, 72, 955, 52, 76, 44, 33, 24, 27, 35, 42, 34, 4, 0, 13, 47, 15, 3, 22, 0, 38, 17, 2, 24, 133, 46, 39, 7, + 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 32, 4, 287, 47, 21, 1, 2, 0, 185, 46, 82, 47, 21, 0, 60, 42, 502, 63, 32, 0, + 449, 56, 1288, 920, 104, 110, 2962, 1070, 13266, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 881, 68, 12, + 0, 67, 12, 16481, 1, 3071, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, + 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, + 24, 2, 30, 2, 24, 2, 7, 4149, 196, 1340, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, + 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, + 4, 2, 16, 4421, 42710, 42, 4148, 12, 221, 16355, 541 +]; +var astralIdentifierCodes = [ + 509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 1306, 2, 54, 14, 32, 9, 16, 3, 46, 10, + 54, 9, 7, 2, 37, 13, 2, 9, 52, 0, 13, 2, 49, 13, 16, 9, 83, 11, 168, 11, 6, 9, 8, 2, 57, 0, 2, 6, 3, 1, 3, 2, 10, + 0, 11, 1, 3, 6, 4, 4, 316, 19, 13, 9, 214, 6, 3, 8, 112, 16, 16, 9, 82, 12, 9, 9, 535, 9, 20855, 9, 135, 4, 60, 6, + 26, 9, 1016, 45, 17, 3, 19723, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 4305, 6, 792618, 239 +]; // This has a complexity linear to the value of the code. The // assumption is that looking up astral identifier characters is // rare. function isInAstralSet(code, set) { - let pos = 0x10000 + let pos = 0x10000; for (let i = 0; i < set.length; i += 2) { - pos += set[i] - if (pos > code) return false - pos += set[i + 1] - if (pos >= code) return true + pos += set[i]; + if (pos > code) return false; + + pos += set[i + 1]; + if (pos >= code) return true; } } // Test whether a given character code starts an identifier. export function isIdentifierStart(code, astral) { - if (code < 65) return code === 36 - if (code < 91) return true - if (code < 97) return code === 95 - if (code < 123) return true - if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) - if (astral === false) return false - return isInAstralSet(code, astralIdentifierStartCodes) + if (code < 65) return code === 36; + if (code < 91) return true; + if (code < 97) return code === 95; + if (code < 123) return true; + if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); + if (astral === false) return false; + return isInAstralSet(code, astralIdentifierStartCodes); } // Test whether a given character is part of an identifier. export function isIdentifierChar(code, astral) { - if (code < 48) return code === 36 - if (code < 58) return true - if (code < 65) return false - if (code < 91) return true - if (code < 97) return code === 95 - if (code < 123) return true - if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) - if (astral === false) return false - return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes) + if (code < 48) return code === 36; + if (code < 58) return true; + if (code < 65) return false; + if (code < 91) return true; + if (code < 97) return code === 95; + if (code < 123) return true; + if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); + if (astral === false) return false; + return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes); } diff --git a/src/index.js b/src/index.js index 4aa982fa19..6b0541ea41 100755 --- a/src/index.js +++ b/src/index.js @@ -19,26 +19,31 @@ // [dammit]: acorn_loose.js // [walk]: util/walk.js -import {Parser} from "./state" -import {getOptions} from "./options" -import "./parseutil" -import "./statement" -import "./lval" -import "./expression" -import "./lookahead" +import {Parser, plugins} from "./state"; +import {getOptions} from "./options"; +import "./parseutil"; +import "./statement"; +import "./lval"; +import "./expression"; +import "./lookahead"; +import "./tokentype"; +import "./tokencontext"; -export {Parser, plugins} from "./state" -export {defaultOptions} from "./options" -export {SourceLocation} from "./location" -export {getLineInfo} from "./location" -export {Node} from "./node" -export {TokenType, types as tokTypes} from "./tokentype" -export {TokContext, types as tokContexts} from "./tokencontext" -export {isIdentifierChar, isIdentifierStart} from "./identifier" -export {Token} from "./tokenize" -export {isNewLine, lineBreak, lineBreakG} from "./whitespace" +export {Parser, plugins} from "./state"; +export {defaultOptions} from "./options"; +export {SourceLocation} from "./location"; +export {getLineInfo} from "./location"; +export {Node} from "./node"; +export {TokenType, types as tokTypes} from "./tokentype"; +export {TokContext, types as tokContexts} from "./tokencontext"; +export {isIdentifierChar, isIdentifierStart} from "./identifier"; +export {Token} from "./tokenize"; +export {isNewLine, lineBreak, lineBreakG} from "./whitespace"; -export const version = "1.0.0" +import flowPlugin from "./plugins/flow"; +import jsxPlugin from "./plugins/jsx"; +plugins.flow = flowPlugin; +plugins.jsx = jsxPlugin; // The main exported interface (under `self.acorn` when in the // browser) is a `parse` function that takes a code string and @@ -48,32 +53,5 @@ export const version = "1.0.0" // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API export function parse(input, options) { - let p = parser(options, input) - let startPos = p.options.locations ? [p.pos, p.curPosition()] : p.pos - p.nextToken() - return p.parseTopLevel(p.options.program || p.startNodeAt(startPos)) -} - -// This function tries to parse a single expression at a given -// offset in a string. Useful for parsing mixed-language formats -// that embed JavaScript expressions. - -export function parseExpressionAt(input, pos, options) { - let p = parser(options, input, pos) - p.nextToken() - return p.parseExpression() -} - -// Acorn is organized as a tokenizer and a recursive-descent parser. -// The `tokenize` export provides an interface to the tokenizer. -// Because the tokenizer is optimized for being efficiently used by -// the Acorn parser itself, this interface is somewhat crude and not -// very modular. - -export function tokenizer(input, options) { - return parser(options, input) -} - -function parser(options, input) { - return new Parser(getOptions(options), String(input)) + return new Parser(getOptions(options), input).parse(); } diff --git a/src/location.js b/src/location.js index 31d40a3501..5ab0028460 100755 --- a/src/location.js +++ b/src/location.js @@ -1,25 +1,25 @@ -import {Parser} from "./state" -import {lineBreakG} from "./whitespace" +import {Parser} from "./state"; +import {lineBreakG} from "./whitespace"; // These are used when `options.locations` is on, for the // `startLoc` and `endLoc` properties. export class Position { constructor(line, col) { - this.line = line - this.column = col + this.line = line; + this.column = col; } offset(n) { - return new Position(this.line, this.column + n) + return new Position(this.line, this.column + n); } } export class SourceLocation { constructor(p, start, end) { - this.start = start - this.end = end - if (p.sourceFile !== null) this.source = p.sourceFile + this.start = start; + this.end = end; + if (p.sourceFile !== null) this.source = p.sourceFile; } } @@ -30,19 +30,19 @@ export class SourceLocation { // into. export function getLineInfo(input, offset) { - for (let line = 1, cur = 0;;) { - lineBreakG.lastIndex = cur - let match = lineBreakG.exec(input) + for (let line = 1, cur = 0; ;) { + lineBreakG.lastIndex = cur; + let match = lineBreakG.exec(input); if (match && match.index < offset) { - ++line - cur = match.index + match[0].length + ++line; + cur = match.index + match[0].length; } else { - return new Position(line, offset - cur) + return new Position(line, offset - cur); } } } -const pp = Parser.prototype +const pp = Parser.prototype; // This function is used to raise exceptions on parse errors. It // takes an offset integer (into the current `input`) to indicate @@ -50,18 +50,18 @@ const pp = Parser.prototype // of the error message, and then raises a `SyntaxError` with that // message. -pp.raise = function(pos, message) { - let loc = getLineInfo(this.input, pos) - message += " (" + loc.line + ":" + loc.column + ")" - let err = new SyntaxError(message) - err.pos = pos; err.loc = loc; err.raisedAt = this.pos - throw err -} +pp.raise = function (pos, message) { + let loc = getLineInfo(this.input, pos); + message += ` (${loc.line}:${loc.column})`; + let err = new SyntaxError(message); + err.pos = pos; + err.loc = loc; + err.raisedAt = this.pos; + throw err; +}; -pp.curPosition = function() { - return new Position(this.curLine, this.pos - this.lineStart) -} - -pp.markPosition = function() { - return this.options.locations ? [this.start, this.startLoc] : this.start -} +pp.curPosition = function () { + if (this.options.locations) { + return new Position(this.curLine, this.pos - this.lineStart); + } +}; diff --git a/src/lookahead.js b/src/lookahead.js index cd3a9c7d08..f6f456cfa3 100644 --- a/src/lookahead.js +++ b/src/lookahead.js @@ -1,6 +1,6 @@ -import {Parser} from "./state" +import {Parser} from "./state"; -const pp = Parser.prototype +const pp = Parser.prototype; var STATE_KEYS = [ "lastTokStartLoc", @@ -27,21 +27,21 @@ var STATE_KEYS = [ ]; pp.getState = function () { - var state = {} + var state = {}; for (var i = 0; i < STATE_KEYS.length; i++) { - var key = STATE_KEYS[i] - state[key] = this[key] + var key = STATE_KEYS[i]; + state[key] = this[key]; } - state.context = this.context.slice() - state.labels = this.labels.slice() - return state + state.context = this.context.slice(); + state.labels = this.labels.slice(); + return state; }; -pp.lookahead = function() { +pp.lookahead = function () { var old = this.getState(); - this.isLookahead = true - this.next() - this.isLookahead = false + this.isLookahead = true; + this.next(); + this.isLookahead = false; var curr = this.getState(); for (var key in old) this[key] = old[key]; return curr; diff --git a/src/lval.js b/src/lval.js index a7718736c6..6a6dc98b7f 100755 --- a/src/lval.js +++ b/src/lval.js @@ -1,205 +1,210 @@ -import {types as tt} from "./tokentype" -import {Parser} from "./state" -import {reservedWords} from "./identifier" -import {has} from "./util" +import {types as tt} from "./tokentype"; +import {Parser} from "./state"; +import {reservedWords} from "./identifier"; +import {has} from "./util"; -const pp = Parser.prototype +const pp = Parser.prototype; // Convert existing expression atom to assignable pattern // if possible. -pp.toAssignable = function(node, isBinding) { +pp.toAssignable = function (node, isBinding) { if (this.options.ecmaVersion >= 6 && node) { switch (node.type) { case "Identifier": case "ObjectPattern": case "ArrayPattern": case "AssignmentPattern": - break + break; case "ObjectExpression": - node.type = "ObjectPattern" + node.type = "ObjectPattern"; for (let i = 0; i < node.properties.length; i++) { - let prop = node.properties[i] + let prop = node.properties[i]; if (prop.type === "SpreadProperty") continue; - if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter") - this.toAssignable(prop.value, isBinding) + if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter"); + this.toAssignable(prop.value, isBinding); } - break + break; case "ArrayExpression": - node.type = "ArrayPattern" - this.toAssignableList(node.elements, isBinding) - break + node.type = "ArrayPattern"; + this.toAssignableList(node.elements, isBinding); + break; case "AssignmentExpression": if (node.operator === "=") { - node.type = "AssignmentPattern" - delete node.operator + node.type = "AssignmentPattern"; + delete node.operator; } else { - this.raise(node.left.end, "Only '=' operator can be used for specifying default value.") + this.raise(node.left.end, "Only '=' operator can be used for specifying default value."); } - break + break; case "MemberExpression": - if (!isBinding) break + if (!isBinding) break; default: - this.raise(node.start, "Assigning to rvalue") + this.raise(node.start, "Assigning to rvalue"); } } - return node -} + return node; +}; // Convert list of expression atoms to binding list. -pp.toAssignableList = function(exprList, isBinding) { - let end = exprList.length +pp.toAssignableList = function (exprList, isBinding) { + let end = exprList.length; if (end) { - let last = exprList[end - 1] - if (last && last.type == "RestElement") { - --end - } else if (last && last.type == "SpreadElement") { - last.type = "RestElement" - let arg = last.argument - this.toAssignable(arg, isBinding) - if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") - this.unexpected(arg.start) - --end + let last = exprList[end - 1]; + if (last && last.type === "RestElement") { + --end; + } else if (last && last.type === "SpreadElement") { + last.type = "RestElement"; + let arg = last.argument; + this.toAssignable(arg, isBinding); + if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") { + this.unexpected(arg.start); + } + --end; } } for (let i = 0; i < end; i++) { - let elt = exprList[i] - if (elt) this.toAssignable(elt, isBinding) + let elt = exprList[i]; + if (elt) this.toAssignable(elt, isBinding); } - return exprList -} + return exprList; +}; // Parses spread element. -pp.parseSpread = function(refShorthandDefaultPos) { - let node = this.startNode() - this.next() - node.argument = this.parseMaybeAssign(refShorthandDefaultPos) - return this.finishNode(node, "SpreadElement") -} +pp.parseSpread = function (refShorthandDefaultPos) { + let node = this.startNode(); + this.next(); + node.argument = this.parseMaybeAssign(refShorthandDefaultPos); + return this.finishNode(node, "SpreadElement"); +}; -pp.parseRest = function() { - let node = this.startNode() - this.next() - node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected() - return this.finishNode(node, "RestElement") -} +pp.parseRest = function () { + let node = this.startNode(); + this.next(); + node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected(); + return this.finishNode(node, "RestElement"); +}; // Parses lvalue (assignable) atom. -pp.parseBindingAtom = function() { - if (this.options.ecmaVersion < 6) return this.parseIdent() +pp.parseBindingAtom = function () { + if (this.options.ecmaVersion < 6) return this.parseIdent(); switch (this.type) { case tt.name: - return this.parseIdent() + return this.parseIdent(); case tt.bracketL: - let node = this.startNode() - this.next() - node.elements = this.parseBindingList(tt.bracketR, true, true) - return this.finishNode(node, "ArrayPattern") + let node = this.startNode(); + this.next(); + node.elements = this.parseBindingList(tt.bracketR, true, true); + return this.finishNode(node, "ArrayPattern"); case tt.braceL: - return this.parseObj(true) + return this.parseObj(true); default: - this.unexpected() + this.unexpected(); } -} +}; -pp.parseBindingList = function(close, allowEmpty, allowTrailingComma) { - var elts = [], first = true +pp.parseBindingList = function (close, allowEmpty, allowTrailingComma) { + var elts = [], first = true; while (!this.eat(close)) { - if (first) first = false - else this.expect(tt.comma) + if (first) first = false; + else this.expect(tt.comma); if (allowEmpty && this.type === tt.comma) { - elts.push(null) + elts.push(null); } else if (allowTrailingComma && this.afterTrailingComma(close)) { - break + break; } else if (this.type === tt.ellipsis) { - elts.push(this.parseAssignableListItemTypes(this.parseRest())) - this.expect(close) - break + elts.push(this.parseAssignableListItemTypes(this.parseRest())); + this.expect(close); + break; } else { - var left = this.parseMaybeDefault() - this.parseAssignableListItemTypes(left) - elts.push(this.parseMaybeDefault(null, left)) + var left = this.parseMaybeDefault(); + this.parseAssignableListItemTypes(left); + elts.push(this.parseMaybeDefault(null, null, left)); } } - return elts -} + return elts; +}; -pp.parseAssignableListItemTypes = function(param) { - return param -} +pp.parseAssignableListItemTypes = function (param) { + return param; +}; // Parses assignment pattern around given atom if possible. -pp.parseMaybeDefault = function(startPos, left) { - startPos = startPos || this.markPosition() - left = left || this.parseBindingAtom() - if (!this.eat(tt.eq)) return left - let node = this.startNodeAt(startPos) - node.operator = "=" - node.left = left - node.right = this.parseMaybeAssign() - return this.finishNode(node, "AssignmentPattern") -} +pp.parseMaybeDefault = function (startPos, startLoc, left) { + startLoc = startLoc || this.startLoc; + startPos = startPos || this.start; + left = left || this.parseBindingAtom(); + if (!this.eat(tt.eq)) return left; + + let node = this.startNodeAt(startPos, startLoc); + node.operator = "="; + node.left = left; + node.right = this.parseMaybeAssign(); + return this.finishNode(node, "AssignmentPattern"); +}; // Verify that a node is an lval — something that can be assigned // to. -pp.checkLVal = function(expr, isBinding, checkClashes) { +pp.checkLVal = function (expr, isBinding, checkClashes) { switch (expr.type) { case "Identifier": if (this.strict && (reservedWords.strictBind(expr.name) || reservedWords.strict(expr.name))) - this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode") + this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode"); if (checkClashes) { - if (has(checkClashes, expr.name)) - this.raise(expr.start, "Argument name clash in strict mode") - checkClashes[expr.name] = true + if (checkClashes[expr.name]) { + this.raise(expr.start, "Argument name clash in strict mode"); + } else { + checkClashes[expr.name] = true; + } } - break + break; case "MemberExpression": - if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression") - break + if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression"); + break; case "ObjectPattern": for (let i = 0; i < expr.properties.length; i++) { var prop = expr.properties[i]; if (prop.type === "Property") prop = prop.value; - this.checkLVal(prop, isBinding, checkClashes) + this.checkLVal(prop, isBinding, checkClashes); } - break + break; case "ArrayPattern": for (let i = 0; i < expr.elements.length; i++) { - let elem = expr.elements[i] - if (elem) this.checkLVal(elem, isBinding, checkClashes) + let elem = expr.elements[i]; + if (elem) this.checkLVal(elem, isBinding, checkClashes); } - break + break; case "AssignmentPattern": - this.checkLVal(expr.left, isBinding, checkClashes) - break + this.checkLVal(expr.left, isBinding, checkClashes); + break; case "SpreadProperty": case "RestElement": - this.checkLVal(expr.argument, isBinding, checkClashes) - break + this.checkLVal(expr.argument, isBinding, checkClashes); + break; case "ParenthesizedExpression": - this.checkLVal(expr.expression, isBinding, checkClashes) - break + this.checkLVal(expr.expression, isBinding, checkClashes); + break; default: - this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue") + this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue"); } -} +}; diff --git a/src/node.js b/src/node.js index 250f2dde0a..e58a28dc55 100755 --- a/src/node.js +++ b/src/node.js @@ -1,57 +1,48 @@ -import {Parser} from "./state" -import {SourceLocation} from "./location" +import {Parser} from "./state"; +import {SourceLocation} from "./location"; // Start an AST node, attaching a start offset. -const pp = Parser.prototype +const pp = Parser.prototype; -export class Node {} - -pp.startNode = function() { - let node = new Node - node.start = this.start - if (this.options.locations) - node.loc = new SourceLocation(this, this.startLoc) - if (this.options.directSourceFile) - node.sourceFile = this.options.directSourceFile - if (this.options.ranges) - node.range = [this.start, 0] - return node +export class Node { + constructor(parser, pos, loc) { + this.type = ""; + this.start = pos; + this.end = 0; + if (parser.options.locations) + this.loc = new SourceLocation(parser, loc); + if (parser.options.directSourceFile) + this.sourceFile = parser.options.directSourceFile; + if (parser.options.ranges) + this.range = [pos, 0]; + } } -pp.startNodeAt = function(pos) { - let node = new Node, start = pos - if (this.options.locations) { - node.loc = new SourceLocation(this, start[1]) - start = pos[0] - } - node.start = start - if (this.options.directSourceFile) - node.sourceFile = this.options.directSourceFile - if (this.options.ranges) - node.range = [start, 0] - return node +pp.startNode = function () { + return new Node(this, this.start, this.startLoc); +}; + +pp.startNodeAt = function (pos, loc) { + return new Node(this, pos, loc); +}; + +function finishNodeAt(node, type, pos, loc) { + node.type = type; + node.end = pos; + if (this.options.locations) node.loc.end = loc; + if (this.options.ranges) node.range[1] = pos; + return node; } // Finish an AST node, adding `type` and `end` properties. -pp.finishNode = function(node, type) { - node.type = type - node.end = this.lastTokEnd - if (this.options.locations) - node.loc.end = this.lastTokEndLoc - if (this.options.ranges) - node.range[1] = this.lastTokEnd - return node -} +pp.finishNode = function (node, type) { + return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc); +}; // Finish node at given position -pp.finishNodeAt = function(node, type, pos) { - if (this.options.locations) { node.loc.end = pos[1]; pos = pos[0] } - node.type = type - node.end = pos - if (this.options.ranges) - node.range[1] = pos - return node -} +pp.finishNodeAt = function (node, type, pos, loc) { + return finishNodeAt.call(this, node, type, pos, loc); +}; diff --git a/src/options.js b/src/options.js index 6a81762f40..88ea8fce47 100755 --- a/src/options.js +++ b/src/options.js @@ -1,5 +1,5 @@ -import {has, isArray} from "./util" -import {SourceLocation} from "./location" +import {has} from "./util"; +import {SourceLocation} from "./location"; // A second optional argument can be given to further configure // the parser process. These options are recognized: @@ -85,38 +85,37 @@ export const defaultOptions = { // Babel-specific options features: {}, strictMode: null -} +}; // Interpret and default an options object export function getOptions(opts) { - let options = {} + let options = {}; for (let opt in defaultOptions) - options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt] + options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; - if (isArray(options.onToken)) { - let tokens = options.onToken - options.onToken = (token) => tokens.push(token) + if (Array.isArray(options.onToken)) { + let tokens = options.onToken; + options.onToken = (token) => tokens.push(token); } - if (isArray(options.onComment)) - options.onComment = pushComment(options, options.onComment) + if (Array.isArray(options.onComment)) + options.onComment = pushComment(options, options.onComment); - return options + return options; } function pushComment(options, array) { return function (block, text, start, end, startLoc, endLoc) { let comment = { - type: block ? 'Block' : 'Line', + type: block ? "Block" : "Line", value: text, start: start, end: end - } + }; if (options.locations) - comment.loc = new SourceLocation(this, startLoc, endLoc) + comment.loc = new SourceLocation(this, startLoc, endLoc); if (options.ranges) - comment.range = [start, end] - array.push(comment) - } + comment.range = [start, end]; + array.push(comment); + }; } - diff --git a/src/parseutil.js b/src/parseutil.js index 3d464a22dc..2fae5e6729 100755 --- a/src/parseutil.js +++ b/src/parseutil.js @@ -1,89 +1,89 @@ -import {types as tt} from "./tokentype" -import {Parser} from "./state" -import {lineBreak} from "./whitespace" +import {types as tt} from "./tokentype"; +import {Parser} from "./state"; +import {lineBreak} from "./whitespace"; -const pp = Parser.prototype +const pp = Parser.prototype; // ## Parser utilities // Test whether a statement node is the string literal `"use strict"`. -pp.isUseStrict = function(stmt) { +pp.isUseStrict = function (stmt) { return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && - stmt.expression.type === "Literal" && stmt.expression.value === "use strict" -} + stmt.expression.type === "Literal" && stmt.expression.value === "use strict"; +}; // Predicate that tests whether the next token is of the given // type, and if yes, consumes it as a side effect. -pp.eat = function(type) { +pp.eat = function (type) { if (this.type === type) { - this.next() - return true + this.next(); + return true; } else { - return false + return false; } -} +}; // Tests whether parsed token is a contextual keyword. -pp.isContextual = function(name) { - return this.type === tt.name && this.value === name -} +pp.isContextual = function (name) { + return this.type === tt.name && this.value === name; +}; // Consumes contextual keyword if possible. -pp.eatContextual = function(name) { - return this.value === name && this.eat(tt.name) -} +pp.eatContextual = function (name) { + return this.value === name && this.eat(tt.name); +}; // Asserts that following token is given contextual keyword. -pp.expectContextual = function(name) { - if (!this.eatContextual(name)) this.unexpected() -} +pp.expectContextual = function (name) { + if (!this.eatContextual(name)) this.unexpected(); +}; // Test whether a semicolon can be inserted at the current position. -pp.canInsertSemicolon = function() { +pp.canInsertSemicolon = function () { return this.type === tt.eof || this.type === tt.braceR || - lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) -} + lineBreak.test(this.input.slice(this.lastTokEnd, this.start)); +}; -pp.insertSemicolon = function() { +pp.insertSemicolon = function () { if (this.canInsertSemicolon()) { if (this.options.onInsertedSemicolon) - this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc) - return true + 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.insertSemicolon()) this.unexpected() -} +pp.semicolon = function () { + if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected(); +}; -pp.afterTrailingComma = function(tokType) { - if (this.type == tokType) { +pp.afterTrailingComma = function (tokType) { + if (this.type === tokType) { if (this.options.onTrailingComma) - this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc) - this.next() - return true + this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); + this.next(); + return true; } -} +}; // Expect a token of a given type. If found, consume it, otherwise, // raise an unexpected token error. -pp.expect = function(type) { - this.eat(type) || this.unexpected() -} +pp.expect = function (type) { + return this.eat(type) || this.unexpected(); +}; // Raise an unexpected token error. -pp.unexpected = function(pos) { - this.raise(pos != null ? pos : this.start, "Unexpected token") -} +pp.unexpected = function (pos) { + this.raise(pos != null ? pos : this.start, "Unexpected token"); +}; diff --git a/src/plugins/flow.js b/src/plugins/flow.js new file mode 100644 index 0000000000..a219d6b37d --- /dev/null +++ b/src/plugins/flow.js @@ -0,0 +1,823 @@ +import { types as tt } from "../tokentype"; +import { Parser } from "../state"; + +var pp = Parser.prototype; + +pp.isRelational = function (op) { + return this.type === tt.relational && this.value === op; +}; + +pp.expectRelational = function (op) { + if (this.isRelational(op)) { + this.next(); + } else { + this.unexpected(); + } +}; + +pp.flowParseTypeInitialiser = function (tok) { + var oldInType = this.inType; + this.inType = true; + this.expect(tok || tt.colon); + var type = this.flowParseType(); + this.inType = oldInType; + return type; +}; + +pp.flowParseDeclareClass = function (node) { + this.next(); + this.flowParseInterfaceish(node, true); + return this.finishNode(node, "DeclareClass"); +}; + +pp.flowParseDeclareFunction = function (node) { + this.next(); + + var id = node.id = this.parseIdent(); + + var typeNode = this.startNode(); + var typeContainer = this.startNode(); + + if (this.isRelational("<")) { + typeNode.typeParameters = this.flowParseTypeParameterDeclaration(); + } else { + typeNode.typeParameters = null; + } + + this.expect(tt.parenL); + var tmp = this.flowParseFunctionTypeParams(); + typeNode.params = tmp.params; + typeNode.rest = tmp.rest; + this.expect(tt.parenR); + typeNode.returnType = this.flowParseTypeInitialiser(); + + typeContainer.typeAnnotation = this.finishNode(typeNode, "FunctionTypeAnnotation"); + id.typeAnnotation = this.finishNode(typeContainer, "TypeAnnotation"); + + this.finishNode(id, id.type); + + this.semicolon(); + + return this.finishNode(node, "DeclareFunction"); +}; + +pp.flowParseDeclare = function (node) { + if (this.type === tt._class) { + return this.flowParseDeclareClass(node); + } else if (this.type === tt._function) { + return this.flowParseDeclareFunction(node); + } else if (this.type === tt._var) { + return this.flowParseDeclareVariable(node); + } else if (this.isContextual("module")) { + return this.flowParseDeclareModule(node); + } else { + this.unexpected(); + } +}; + +pp.flowParseDeclareVariable = function (node) { + this.next(); + node.id = this.flowParseTypeAnnotatableIdentifier(); + this.semicolon(); + return this.finishNode(node, "DeclareVariable"); +}; + +pp.flowParseDeclareModule = function (node) { + this.next(); + + if (this.type === tt.string) { + node.id = this.parseExprAtom(); + } else { + node.id = this.parseIdent(); + } + + var bodyNode = node.body = this.startNode(); + var body = bodyNode.body = []; + this.expect(tt.braceL); + while (this.type !== tt.braceR) { + var node2 = this.startNode(); + + // todo: declare check + this.next(); + + body.push(this.flowParseDeclare(node2)); + } + this.expect(tt.braceR); + + this.finishNode(bodyNode, "BlockStatement"); + return this.finishNode(node, "DeclareModule"); +}; + + +// Interfaces + +pp.flowParseInterfaceish = function (node, allowStatic) { + node.id = this.parseIdent(); + + if (this.isRelational("<")) { + node.typeParameters = this.flowParseTypeParameterDeclaration(); + } else { + node.typeParameters = null; + } + + node.extends = []; + + if (this.eat(tt._extends)) { + do { + node.extends.push(this.flowParseInterfaceExtends()); + } while(this.eat(tt.comma)); + } + + node.body = this.flowParseObjectType(allowStatic); +}; + +pp.flowParseInterfaceExtends = function () { + var node = this.startNode(); + + node.id = this.parseIdent(); + if (this.isRelational("<")) { + node.typeParameters = this.flowParseTypeParameterInstantiation(); + } else { + node.typeParameters = null; + } + + return this.finishNode(node, "InterfaceExtends"); +}; + +pp.flowParseInterface = function (node) { + this.flowParseInterfaceish(node, false); + return this.finishNode(node, "InterfaceDeclaration"); +}; + +// Type aliases + +pp.flowParseTypeAlias = function (node) { + node.id = this.parseIdent(); + + if (this.isRelational("<")) { + node.typeParameters = this.flowParseTypeParameterDeclaration(); + } else { + node.typeParameters = null; + } + + node.right = this.flowParseTypeInitialiser(tt.eq); + this.semicolon(); + + return this.finishNode(node, "TypeAlias"); +}; + +// Type annotations + +pp.flowParseTypeParameterDeclaration = function () { + var node = this.startNode(); + node.params = []; + + this.expectRelational("<"); + while (!this.isRelational(">")) { + node.params.push(this.flowParseTypeAnnotatableIdentifier()); + if (!this.isRelational(">")) { + this.expect(tt.comma); + } + } + this.expectRelational(">"); + + return this.finishNode(node, "TypeParameterDeclaration"); +}; + +pp.flowParseTypeParameterInstantiation = function () { + var node = this.startNode(), oldInType = this.inType; + node.params = []; + + this.inType = true; + + this.expectRelational("<"); + while (!this.isRelational(">")) { + node.params.push(this.flowParseType()); + if (!this.isRelational(">")) { + this.expect(tt.comma); + } + } + this.expectRelational(">"); + + this.inType = oldInType; + + return this.finishNode(node, "TypeParameterInstantiation"); +}; + +pp.flowParseObjectPropertyKey = function () { + return (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true); +}; + +pp.flowParseObjectTypeIndexer = function (node, isStatic) { + node.static = isStatic; + + this.expect(tt.bracketL); + node.id = this.flowParseObjectPropertyKey(); + node.key = this.flowParseTypeInitialiser(); + this.expect(tt.bracketR); + node.value = this.flowParseTypeInitialiser(); + + this.flowObjectTypeSemicolon(); + return this.finishNode(node, "ObjectTypeIndexer"); +}; + +pp.flowParseObjectTypeMethodish = function (node) { + node.params = []; + node.rest = null; + node.typeParameters = null; + + if (this.isRelational("<")) { + node.typeParameters = this.flowParseTypeParameterDeclaration(); + } + + this.expect(tt.parenL); + while (this.type === tt.name) { + node.params.push(this.flowParseFunctionTypeParam()); + if (this.type !== tt.parenR) { + this.expect(tt.comma); + } + } + + if (this.eat(tt.ellipsis)) { + node.rest = this.flowParseFunctionTypeParam(); + } + this.expect(tt.parenR); + node.returnType = this.flowParseTypeInitialiser(); + + return this.finishNode(node, "FunctionTypeAnnotation"); +}; + +pp.flowParseObjectTypeMethod = function (startPos, startLoc, isStatic, key) { + var node = this.startNodeAt(startPos, startLoc); + node.value = this.flowParseObjectTypeMethodish(this.startNodeAt(startPos, startLoc)); + node.static = isStatic; + node.key = key; + node.optional = false; + this.flowObjectTypeSemicolon(); + return this.finishNode(node, "ObjectTypeProperty"); +}; + +pp.flowParseObjectTypeCallProperty = function (node, isStatic) { + var valueNode = this.startNode(); + node.static = isStatic; + node.value = this.flowParseObjectTypeMethodish(valueNode); + this.flowObjectTypeSemicolon(); + return this.finishNode(node, "ObjectTypeCallProperty"); +}; + +pp.flowParseObjectType = function (allowStatic) { + var nodeStart = this.startNode(); + var node; + var optional = false; + var propertyKey; + var isStatic; + + nodeStart.callProperties = []; + nodeStart.properties = []; + nodeStart.indexers = []; + + this.expect(tt.braceL); + + while (this.type !== tt.braceR) { + var startPos = this.start, startLoc = this.startLoc; + node = this.startNode(); + if (allowStatic && this.isContextual("static")) { + this.next(); + isStatic = true; + } + + if (this.type === tt.bracketL) { + nodeStart.indexers.push(this.flowParseObjectTypeIndexer(node, isStatic)); + } else if (this.type === tt.parenL || this.isRelational("<")) { + nodeStart.callProperties.push(this.flowParseObjectTypeCallProperty(node, allowStatic)); + } else { + if (isStatic && this.type === tt.colon) { + propertyKey = this.parseIdent(); + } else { + propertyKey = this.flowParseObjectPropertyKey(); + } + if (this.isRelational("<") || this.type === tt.parenL) { + // This is a method property + nodeStart.properties.push(this.flowParseObjectTypeMethod(startPos, startLoc, isStatic, propertyKey)); + } else { + if (this.eat(tt.question)) { + optional = true; + } + node.key = propertyKey; + node.value = this.flowParseTypeInitialiser(); + node.optional = optional; + node.static = isStatic; + this.flowObjectTypeSemicolon(); + nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty")); + } + } + } + + this.expect(tt.braceR); + + return this.finishNode(nodeStart, "ObjectTypeAnnotation"); +}; + +pp.flowObjectTypeSemicolon = function () { + if (!this.eat(tt.semi) && !this.eat(tt.comma) && this.type !== tt.braceR) { + this.unexpected(); + } +}; + +pp.flowParseGenericType = function (startPos, startLoc, id) { + var node = this.startNodeAt(startPos, startLoc); + + node.typeParameters = null; + node.id = id; + + while (this.eat(tt.dot)) { + var node2 = this.startNodeAt(startPos, startLoc); + node2.qualification = node.id; + node2.id = this.parseIdent(); + node.id = this.finishNode(node2, "QualifiedTypeIdentifier"); + } + + if (this.isRelational("<")) { + node.typeParameters = this.flowParseTypeParameterInstantiation(); + } + + return this.finishNode(node, "GenericTypeAnnotation"); +}; + +pp.flowParseTypeofType = function () { + var node = this.startNode(); + this.expect(tt._typeof); + node.argument = this.flowParsePrimaryType(); + return this.finishNode(node, "TypeofTypeAnnotation"); +}; + +pp.flowParseTupleType = function () { + var node = this.startNode(); + node.types = []; + this.expect(tt.bracketL); + // We allow trailing commas + while (this.pos < this.input.length && this.type !== tt.bracketR) { + node.types.push(this.flowParseType()); + if (this.type === tt.bracketR) break; + this.expect(tt.comma); + } + this.expect(tt.bracketR); + return this.finishNode(node, "TupleTypeAnnotation"); +}; + +pp.flowParseFunctionTypeParam = function () { + var optional = false; + var node = this.startNode(); + node.name = this.parseIdent(); + if (this.eat(tt.question)) { + optional = true; + } + node.optional = optional; + node.typeAnnotation = this.flowParseTypeInitialiser(); + return this.finishNode(node, "FunctionTypeParam"); +}; + +pp.flowParseFunctionTypeParams = function () { + var ret = { params: [], rest: null }; + while (this.type === tt.name) { + ret.params.push(this.flowParseFunctionTypeParam()); + if (this.type !== tt.parenR) { + this.expect(tt.comma); + } + } + if (this.eat(tt.ellipsis)) { + ret.rest = this.flowParseFunctionTypeParam(); + } + return ret; +}; + +pp.flowIdentToTypeAnnotation = function (startPos, startLoc, node, id) { + switch (id.name) { + case "any": + return this.finishNode(node, "AnyTypeAnnotation"); + + case "void": + return this.finishNode(node, "VoidTypeAnnotation"); + + case "bool": + case "boolean": + return this.finishNode(node, "BooleanTypeAnnotation"); + + case "mixed": + return this.finishNode(node, "MixedTypeAnnotation"); + + case "number": + return this.finishNode(node, "NumberTypeAnnotation"); + + case "string": + return this.finishNode(node, "StringTypeAnnotation"); + + default: + return this.flowParseGenericType(startPos, startLoc, id); + } +}; + +// The parsing of types roughly parallels the parsing of expressions, and +// primary types are kind of like primary expressions...they're the +// primitives with which other types are constructed. +pp.flowParsePrimaryType = function () { + var startPos = this.start, startLoc = this.startLoc; + var node = this.startNode(); + var tmp; + var type; + var isGroupedType = false; + + switch (this.type) { + case tt.name: + return this.flowIdentToTypeAnnotation(startPos, startLoc, node, this.parseIdent()); + + case tt.braceL: + return this.flowParseObjectType(); + + case tt.bracketL: + return this.flowParseTupleType(); + + case tt.relational: + if (this.value === "<") { + node.typeParameters = this.flowParseTypeParameterDeclaration(); + this.expect(tt.parenL); + tmp = this.flowParseFunctionTypeParams(); + node.params = tmp.params; + node.rest = tmp.rest; + this.expect(tt.parenR); + + this.expect(tt.arrow); + + node.returnType = this.flowParseType(); + + return this.finishNode(node, "FunctionTypeAnnotation"); + } + + case tt.parenL: + this.next(); + + // Check to see if this is actually a grouped type + if (this.type !== tt.parenR && this.type !== tt.ellipsis) { + if (this.type === tt.name) { + var token = this.lookahead().type; + isGroupedType = token !== tt.question && token !== tt.colon; + } else { + isGroupedType = true; + } + } + + if (isGroupedType) { + type = this.flowParseType(); + this.expect(tt.parenR); + + // If we see a => next then someone was probably confused about + // function types, so we can provide a better error message + if (this.eat(tt.arrow)) { + this.raise(node, + "Unexpected token =>. It looks like " + + "you are trying to write a function type, but you ended up " + + "writing a grouped type followed by an =>, which is a syntax " + + "error. Remember, function type parameters are named so function " + + "types look like (name1: type1, name2: type2) => returnType. You " + + "probably wrote (type1) => returnType" + ); + } + + return type; + } + + tmp = this.flowParseFunctionTypeParams(); + node.params = tmp.params; + node.rest = tmp.rest; + + this.expect(tt.parenR); + + this.expect(tt.arrow); + + node.returnType = this.flowParseType(); + node.typeParameters = null; + + return this.finishNode(node, "FunctionTypeAnnotation"); + + case tt.string: + node.value = this.value; + node.raw = this.input.slice(this.start, this.end); + this.next(); + return this.finishNode(node, "StringLiteralTypeAnnotation"); + + default: + if (this.type.keyword === "typeof") { + return this.flowParseTypeofType(); + } + } + + this.unexpected(); +}; + +pp.flowParsePostfixType = function () { + var node = this.startNode(); + var type = node.elementType = this.flowParsePrimaryType(); + if (this.type === tt.bracketL) { + this.expect(tt.bracketL); + this.expect(tt.bracketR); + return this.finishNode(node, "ArrayTypeAnnotation"); + } else { + return type; + } +}; + +pp.flowParsePrefixType = function () { + var node = this.startNode(); + if (this.eat(tt.question)) { + node.typeAnnotation = this.flowParsePrefixType(); + return this.finishNode(node, "NullableTypeAnnotation"); + } else { + return this.flowParsePostfixType(); + } +}; + +pp.flowParseIntersectionType = function () { + var node = this.startNode(); + var type = this.flowParsePrefixType(); + node.types = [type]; + while (this.eat(tt.bitwiseAND)) { + node.types.push(this.flowParsePrefixType()); + } + return node.types.length === 1 ? type : this.finishNode(node, "IntersectionTypeAnnotation"); +}; + +pp.flowParseUnionType = function () { + var node = this.startNode(); + var type = this.flowParseIntersectionType(); + node.types = [type]; + while (this.eat(tt.bitwiseOR)) { + node.types.push(this.flowParseIntersectionType()); + } + return node.types.length === 1 ? type : this.finishNode(node, "UnionTypeAnnotation"); +}; + +pp.flowParseType = function () { + var oldInType = this.inType; + this.inType = true; + var type = this.flowParseUnionType(); + this.inType = oldInType; + return type; +}; + +pp.flowParseTypeAnnotation = function () { + var node = this.startNode(); + node.typeAnnotation = this.flowParseTypeInitialiser(); + return this.finishNode(node, "TypeAnnotation"); +}; + +pp.flowParseTypeAnnotatableIdentifier = function (requireTypeAnnotation, canBeOptionalParam) { + var ident = this.parseIdent(); + var isOptionalParam = false; + + if (canBeOptionalParam && this.eat(tt.question)) { + this.expect(tt.question); + isOptionalParam = true; + } + + if (requireTypeAnnotation || this.type === tt.colon) { + ident.typeAnnotation = this.flowParseTypeAnnotation(); + this.finishNode(ident, ident.type); + } + + if (isOptionalParam) { + ident.optional = true; + this.finishNode(ident, ident.type); + } + + return ident; +}; + +export default function (instance) { + // function name(): string {} + instance.extend("parseFunctionBody", function (inner) { + return function (node, allowExpression) { + if (this.type === tt.colon) { + node.returnType = this.flowParseTypeAnnotation(); + } + + return inner.call(this, node, allowExpression); + }; + }); + + instance.extend("parseStatement", function (inner) { + return function (declaration, topLevel) { + // strict mode handling of `interface` since it's a reserved word + if (this.strict && this.type === tt.name && this.value === "interface") { + var node = this.startNode(); + this.next(); + return this.flowParseInterface(node); + } else { + return inner.call(this, declaration, topLevel); + } + }; + }); + + instance.extend("parseExpressionStatement", function (inner) { + return function (node, expr) { + if (expr.type === "Identifier") { + if (expr.name === "declare") { + if (this.type === tt._class || this.type === tt.name || this.type === tt._function || this.type === tt._var) { + return this.flowParseDeclare(node); + } + } else if (this.type === tt.name) { + if (expr.name === "interface") { + return this.flowParseInterface(node); + } else if (expr.name === "type") { + return this.flowParseTypeAlias(node); + } + } + } + + return inner.call(this, node, expr); + }; + }); + + instance.extend("shouldParseExportDeclaration", function (inner) { + return function () { + return this.isContextual("type") || inner.call(this); + }; + }); + + instance.extend("parseParenItem", function () { + return function (node, startLoc, startPos) { + if (this.type === tt.colon) { + var typeCastNode = this.startNodeAt(startLoc, startPos); + typeCastNode.expression = node; + typeCastNode.typeAnnotation = this.flowParseTypeAnnotation(); + return this.finishNode(typeCastNode, "TypeCastExpression"); + } else { + return node; + } + }; + }); + + instance.extend("parseClassId", function (inner) { + return function (node, isStatement) { + inner.call(this, node, isStatement); + if (this.isRelational("<")) { + node.typeParameters = this.flowParseTypeParameterDeclaration(); + } + }; + }); + + // don't consider `void` to be a keyword as then it'll use the void token type + // and set startExpr + instance.extend("isKeyword", function (inner) { + return function (name) { + if (this.inType && name === "void") { + return false; + } else { + return inner.call(this, name); + } + }; + }); + + instance.extend("readToken", function (inner) { + return function (code) { + if (this.inType && (code === 62 || code === 60)) { + return this.finishOp(tt.relational, 1); + } else { + return inner.call(this, code); + } + }; + }); + + instance.extend("jsx_readToken", function (inner) { + return function () { + if (!this.inType) return inner.call(this); + }; + }); + + instance.extend("parseParenArrowList", function (inner) { + return function (startPos, startLoc, exprList, isAsync) { + for (var i = 0; i < exprList.length; i++) { + var listItem = exprList[i]; + if (listItem.type === "TypeCastExpression") { + var expr = listItem.expression; + expr.typeAnnotation = listItem.typeAnnotation; + exprList[i] = expr; + } + } + return inner.call(this, startPos, startLoc, exprList, isAsync); + }; + }); + + instance.extend("parseClassProperty", function (inner) { + return function (node) { + if (this.type === tt.colon) { + node.typeAnnotation = this.flowParseTypeAnnotation(); + } + return inner.call(this, node); + }; + }); + + instance.extend("isClassProperty", function (inner) { + return function () { + return this.type === tt.colon || inner.call(this); + }; + }); + + instance.extend("parseClassMethod", function () { + return function (classBody, method, isGenerator, isAsync) { + var typeParameters; + if (this.isRelational("<")) { + typeParameters = this.flowParseTypeParameterDeclaration(); + } + method.value = this.parseMethod(isGenerator, isAsync); + method.value.typeParameters = typeParameters; + classBody.body.push(this.finishNode(method, "MethodDefinition")); + }; + }); + + instance.extend("parseClassSuper", function (inner) { + return function (node, isStatement) { + inner.call(this, node, isStatement); + if (node.superClass && this.isRelational("<")) { + node.superTypeParameters = this.flowParseTypeParameterInstantiation(); + } + if (this.isContextual("implements")) { + this.next(); + var implemented = node.implements = []; + do { + let node = this.startNode(); + node.id = this.parseIdent(); + if (this.isRelational("<")) { + node.typeParameters = this.flowParseTypeParameterInstantiation(); + } else { + node.typeParameters = null; + } + implemented.push(this.finishNode(node, "ClassImplements")); + } while(this.eat(tt.comma)) + } + }; + }); + + instance.extend("parseObjPropValue", function (inner) { + return function (prop) { + var typeParameters; + if (this.isRelational("<")) { + typeParameters = this.flowParseTypeParameterDeclaration(); + if (this.type !== tt.parenL) this.unexpected(); + } + inner.apply(this, arguments); + prop.value.typeParameters = typeParameters; + }; + }); + + instance.extend("parseAssignableListItemTypes", function () { + return function (param) { + if (this.eat(tt.question)) { + param.optional = true; + } + if (this.type === tt.colon) { + param.typeAnnotation = this.flowParseTypeAnnotation(); + } + this.finishNode(param, param.type); + return param; + }; + }); + + instance.extend("parseImportSpecifiers", function (inner) { + return function (node) { + node.isType = false; + if (this.isContextual("type")) { + var startPos = this.start, startLoc = this.startLoc; + var typeId = this.parseIdent(); + if ((this.type === tt.name && this.value !== "from") || this.type === tt.braceL || this.type === tt.star) { + node.isType = true; + } else { + node.specifiers.push(this.parseImportSpecifierDefault(typeId, startPos, startLoc)); + if (this.isContextual("from")) return; + this.eat(tt.comma); + } + } + inner.call(this, node); + }; + }); + + // function foo() {} + instance.extend("parseFunctionParams", function (inner) { + return function (node) { + if (this.isRelational("<")) { + node.typeParameters = this.flowParseTypeParameterDeclaration(); + } + inner.call(this, node); + }; + }); + + // var foo: string = bar + instance.extend("parseVarHead", function (inner) { + return function (decl) { + inner.call(this, decl); + if (this.type === tt.colon) { + decl.id.typeAnnotation = this.flowParseTypeAnnotation(); + this.finishNode(decl.id, decl.id.type); + } + }; + }); +}; diff --git a/src/plugins/jsx/index.js b/src/plugins/jsx/index.js new file mode 100644 index 0000000000..19ff2e3697 --- /dev/null +++ b/src/plugins/jsx/index.js @@ -0,0 +1,421 @@ +import XHTMLEntities from "./xhtml"; +import { TokenType, types as tt } from "../../tokentype"; +import { TokContext, types as tc } from "../../tokencontext"; +import { Parser } from "../../state"; +import { isIdentifierChar, isIdentifierStart } from "../../identifier"; +import { isNewLine, lineBreak, lineBreakG } from "../../whitespace"; + +const HEX_NUMBER = /^[\da-fA-F]+$/; +const DECIMAL_NUMBER = /^\d+$/; + +tc.j_oTag = new TokContext("...", true, true); + +tt.jsxName = new TokenType("jsxName"); +tt.jsxText = new TokenType("jsxText", { beforeExpr: true }); +tt.jsxTagStart = new TokenType("jsxTagStart"); +tt.jsxTagEnd = new TokenType("jsxTagEnd"); + +tt.jsxTagStart.updateContext = function() { + this.context.push(tc.j_expr); // treat as beginning of JSX expression + this.context.push(tc.j_oTag); // start opening tag context + this.exprAllowed = false; +}; + +tt.jsxTagEnd.updateContext = function(prevType) { + var out = this.context.pop(); + if (out === tc.j_oTag && prevType === tt.slash || out === tc.j_cTag) { + this.context.pop(); + this.exprAllowed = this.curContext() === tc.j_expr; + } else { + this.exprAllowed = true; + } +}; + +var pp = Parser.prototype; + +// Reads inline JSX contents token. + +pp.jsxReadToken = function() { + var out = "", chunkStart = this.pos; + for (;;) { + if (this.pos >= this.input.length) + this.raise(this.start, "Unterminated JSX contents"); + var ch = this.input.charCodeAt(this.pos); + + switch (ch) { + case 60: // "<" + case 123: // "{" + if (this.pos === this.start) { + if (ch === 60 && this.exprAllowed) { + ++this.pos; + return this.finishToken(tt.jsxTagStart); + } + return this.getTokenFromCode(ch); + } + out += this.input.slice(chunkStart, this.pos); + return this.finishToken(tt.jsxText, out); + + case 38: // "&" + out += this.input.slice(chunkStart, this.pos); + out += this.jsxReadEntity(); + chunkStart = this.pos; + break; + + default: + if (isNewLine(ch)) { + out += this.input.slice(chunkStart, this.pos); + out += this.jsxReadNewLine(true); + chunkStart = this.pos; + } else { + ++this.pos; + } + } + } +}; + +pp.jsxReadNewLine = function(normalizeCRLF) { + var ch = this.input.charCodeAt(this.pos); + var out; + ++this.pos; + if (ch === 13 && this.input.charCodeAt(this.pos) === 10) { + ++this.pos; + out = normalizeCRLF ? "\n" : "\r\n"; + } else { + out = String.fromCharCode(ch); + } + if (this.options.locations) { + ++this.curLine; + this.lineStart = this.pos; + } + + return out; +}; + +pp.jsxReadString = function(quote) { + var out = "", chunkStart = ++this.pos; + for (;;) { + if (this.pos >= this.input.length) + this.raise(this.start, "Unterminated string constant"); + var ch = this.input.charCodeAt(this.pos); + if (ch === quote) break; + if (ch === 38) { // "&" + out += this.input.slice(chunkStart, this.pos); + out += this.jsxReadEntity(); + chunkStart = this.pos; + } else if (isNewLine(ch)) { + out += this.input.slice(chunkStart, this.pos); + out += this.jsxReadNewLine(false); + chunkStart = this.pos; + } else { + ++this.pos; + } + } + out += this.input.slice(chunkStart, this.pos++); + return this.finishToken(tt.string, out); +}; + +pp.jsxReadEntity = function() { + var str = "", count = 0, entity; + var ch = this.input[this.pos]; + if (ch !== "&") + this.raise(this.pos, "Entity must start with an ampersand"); + var startPos = ++this.pos; + while (this.pos < this.input.length && count++ < 10) { + ch = this.input[this.pos++]; + if (ch === ";") { + if (str[0] === "#") { + if (str[1] === "x") { + str = str.substr(2); + if (HEX_NUMBER.test(str)) + entity = String.fromCharCode(parseInt(str, 16)); + } else { + str = str.substr(1); + if (DECIMAL_NUMBER.test(str)) + entity = String.fromCharCode(parseInt(str, 10)); + } + } else { + entity = XHTMLEntities[str]; + } + break; + } + str += ch; + } + if (!entity) { + this.pos = startPos; + return "&"; + } + return entity; +}; + + +// Read a JSX identifier (valid tag or attribute name). +// +// Optimized version since JSX identifiers can"t contain +// escape characters and so can be read as single slice. +// Also assumes that first character was already checked +// by isIdentifierStart in readToken. + +pp.jsxReadWord = function() { + var ch, start = this.pos; + do { + ch = this.input.charCodeAt(++this.pos); + } while (isIdentifierChar(ch) || ch === 45); // "-" + return this.finishToken(tt.jsxName, this.input.slice(start, this.pos)); +}; + +// Transforms JSX element name to string. + +function getQualifiedJSXName(object) { + if (object.type === "JSXIdentifier") + return object.name; + + if (object.type === "JSXNamespacedName") + return object.namespace.name + ":" + object.name.name; + + if (object.type === "JSXMemberExpression") + return getQualifiedJSXName(object.object) + "." + + getQualifiedJSXName(object.property); +} + +// Parse next token as JSX identifier + +pp.jsxParseIdentifier = function() { + var node = this.startNode(); + if (this.type === tt.jsxName) + node.name = this.value; + else if (this.type.keyword) + node.name = this.type.keyword; + else + this.unexpected(); + this.next(); + return this.finishNode(node, "JSXIdentifier"); +}; + +// Parse namespaced identifier. + +pp.jsxParseNamespacedName = function() { + var startPos = this.start, startLoc = this.startLoc; + var name = this.jsxParseIdentifier(); + if (!this.eat(tt.colon)) return name; + var node = this.startNodeAt(startPos, startLoc); + node.namespace = name; + node.name = this.jsxParseIdentifier(); + return this.finishNode(node, "JSXNamespacedName"); +}; + +// Parses element name in any form - namespaced, member +// or single identifier. + +pp.jsxParseElementName = function() { + var startPos = this.start, startLoc = this.startLoc; + var node = this.jsxParseNamespacedName(); + while (this.eat(tt.dot)) { + var newNode = this.startNodeAt(startPos, startLoc); + newNode.object = node; + newNode.property = this.jsxParseIdentifier(); + node = this.finishNode(newNode, "JSXMemberExpression"); + } + return node; +}; + +// Parses any type of JSX attribute value. + +pp.jsxParseAttributeValue = function() { + switch (this.type) { + case tt.braceL: + var node = this.jsxParseExpressionContainer(); + if (node.expression.type === "JSXEmptyExpression") + this.raise(node.start, "JSX attributes must only be assigned a non-empty expression"); + return node; + + case tt.jsxTagStart: + case tt.string: + return this.parseExprAtom(); + + default: + this.raise(this.start, "JSX value should be either an expression or a quoted JSX text"); + } +}; + +// JSXEmptyExpression is unique type since it doesn"t actually parse anything, +// and so it should start at the end of last read token (left brace) and finish +// at the beginning of the next one (right brace). + +pp.jsxParseEmptyExpression = function() { + var tmp = this.start; + this.start = this.lastTokEnd; + this.lastTokEnd = tmp; + + tmp = this.startLoc; + this.startLoc = this.lastTokEndLoc; + this.lastTokEndLoc = tmp; + + return this.finishNode(this.startNode(), "JSXEmptyExpression"); +}; + +// Parses JSX expression enclosed into curly brackets. + + +pp.jsxParseExpressionContainer = function() { + var node = this.startNode(); + this.next(); + node.expression = this.type === tt.braceR + ? this.jsxParseEmptyExpression() + : this.parseExpression(); + this.expect(tt.braceR); + return this.finishNode(node, "JSXExpressionContainer"); +}; + +// Parses following JSX attribute name-value pair. + +pp.jsxParseAttribute = function() { + var node = this.startNode(); + if (this.eat(tt.braceL)) { + this.expect(tt.ellipsis); + node.argument = this.parseMaybeAssign(); + this.expect(tt.braceR); + return this.finishNode(node, "JSXSpreadAttribute"); + } + node.name = this.jsxParseNamespacedName(); + node.value = this.eat(tt.eq) ? this.jsxParseAttributeValue() : null; + return this.finishNode(node, "JSXAttribute"); +}; + +// Parses JSX opening tag starting after "<". + +pp.jsxParseOpeningElementAt = function(startPos, startLoc) { + var node = this.startNodeAt(startPos, startLoc); + node.attributes = []; + node.name = this.jsxParseElementName(); + while (this.type !== tt.slash && this.type !== tt.jsxTagEnd) + node.attributes.push(this.jsxParseAttribute()); + node.selfClosing = this.eat(tt.slash); + this.expect(tt.jsxTagEnd); + return this.finishNode(node, "JSXOpeningElement"); +}; + +// Parses JSX closing tag starting after ""); + } + } + + node.openingElement = openingElement; + node.closingElement = closingElement; + node.children = children; + if (this.type === tt.relational && this.value === "<") { + this.raise(this.start, "Adjacent JSX elements must be wrapped in an enclosing tag"); + } + return this.finishNode(node, "JSXElement"); +}; + +// Parses entire JSX element from current position. + +pp.jsxParseElement = function() { + var startPos = this.start, startLoc = this.startLoc; + this.next(); + return this.jsxParseElementAt(startPos, startLoc); +}; + +export default function(instance) { + instance.extend("parseExprAtom", function(inner) { + return function(refShortHandDefaultPos) { + if (this.type === tt.jsxText) + return this.parseLiteral(this.value); + else if (this.type === tt.jsxTagStart) + return this.jsxParseElement(); + else + return inner.call(this, refShortHandDefaultPos); + }; + }); + + instance.extend("readToken", function(inner) { + return function(code) { + var context = this.curContext(); + + if (context === tc.j_expr) return this.jsxReadToken(); + + if (context === tc.j_oTag || context === tc.j_cTag) { + if (isIdentifierStart(code)) return this.jsxReadWord(); + + if (code === 62) { + ++this.pos; + return this.finishToken(tt.jsxTagEnd); + } + + if ((code === 34 || code === 39) && context === tc.j_oTag) + return this.jsxReadString(code); + } + + if (code === 60 && this.exprAllowed) { + ++this.pos; + return this.finishToken(tt.jsxTagStart); + } + return inner.call(this, code); + }; + }); + + instance.extend("updateContext", function(inner) { + return function(prevType) { + if (this.type === tt.braceL) { + var curContext = this.curContext(); + if (curContext === tc.j_oTag) this.context.push(tc.b_expr); + else if (curContext === tc.j_expr) this.context.push(tc.b_tmpl); + else inner.call(this, prevType); + this.exprAllowed = true; + } else if (this.type === tt.slash && prevType === tt.jsxTagStart) { + this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore + this.context.push(tc.j_cTag); // reconsider as closing tag context + this.exprAllowed = false; + } else { + return inner.call(this, prevType); + } + }; + }); +}; diff --git a/src/plugins/jsx/xhtml.js b/src/plugins/jsx/xhtml.js new file mode 100644 index 0000000000..232f8b1b1a --- /dev/null +++ b/src/plugins/jsx/xhtml.js @@ -0,0 +1,255 @@ +export default { + quot: "\u0022", + amp: "&", + apos: "\u0027", + lt: "<", + gt: ">", + nbsp: "\u00A0", + iexcl: "\u00A1", + cent: "\u00A2", + pound: "\u00A3", + curren: "\u00A4", + yen: "\u00A5", + brvbar: "\u00A6", + sect: "\u00A7", + uml: "\u00A8", + copy: "\u00A9", + ordf: "\u00AA", + laquo: "\u00AB", + not: "\u00AC", + shy: "\u00AD", + reg: "\u00AE", + macr: "\u00AF", + deg: "\u00B0", + plusmn: "\u00B1", + sup2: "\u00B2", + sup3: "\u00B3", + acute: "\u00B4", + micro: "\u00B5", + para: "\u00B6", + middot: "\u00B7", + cedil: "\u00B8", + sup1: "\u00B9", + ordm: "\u00BA", + raquo: "\u00BB", + frac14: "\u00BC", + frac12: "\u00BD", + frac34: "\u00BE", + iquest: "\u00BF", + Agrave: "\u00C0", + Aacute: "\u00C1", + Acirc: "\u00C2", + Atilde: "\u00C3", + Auml: "\u00C4", + Aring: "\u00C5", + AElig: "\u00C6", + Ccedil: "\u00C7", + Egrave: "\u00C8", + Eacute: "\u00C9", + Ecirc: "\u00CA", + Euml: "\u00CB", + Igrave: "\u00CC", + Iacute: "\u00CD", + Icirc: "\u00CE", + Iuml: "\u00CF", + ETH: "\u00D0", + Ntilde: "\u00D1", + Ograve: "\u00D2", + Oacute: "\u00D3", + Ocirc: "\u00D4", + Otilde: "\u00D5", + Ouml: "\u00D6", + times: "\u00D7", + Oslash: "\u00D8", + Ugrave: "\u00D9", + Uacute: "\u00DA", + Ucirc: "\u00DB", + Uuml: "\u00DC", + Yacute: "\u00DD", + THORN: "\u00DE", + szlig: "\u00DF", + agrave: "\u00E0", + aacute: "\u00E1", + acirc: "\u00E2", + atilde: "\u00E3", + auml: "\u00E4", + aring: "\u00E5", + aelig: "\u00E6", + ccedil: "\u00E7", + egrave: "\u00E8", + eacute: "\u00E9", + ecirc: "\u00EA", + euml: "\u00EB", + igrave: "\u00EC", + iacute: "\u00ED", + icirc: "\u00EE", + iuml: "\u00EF", + eth: "\u00F0", + ntilde: "\u00F1", + ograve: "\u00F2", + oacute: "\u00F3", + ocirc: "\u00F4", + otilde: "\u00F5", + ouml: "\u00F6", + divide: "\u00F7", + oslash: "\u00F8", + ugrave: "\u00F9", + uacute: "\u00FA", + ucirc: "\u00FB", + uuml: "\u00FC", + yacute: "\u00FD", + thorn: "\u00FE", + yuml: "\u00FF", + OElig: "\u0152", + oelig: "\u0153", + Scaron: "\u0160", + scaron: "\u0161", + Yuml: "\u0178", + fnof: "\u0192", + circ: "\u02C6", + tilde: "\u02DC", + Alpha: "\u0391", + Beta: "\u0392", + Gamma: "\u0393", + Delta: "\u0394", + Epsilon: "\u0395", + Zeta: "\u0396", + Eta: "\u0397", + Theta: "\u0398", + Iota: "\u0399", + Kappa: "\u039A", + Lambda: "\u039B", + Mu: "\u039C", + Nu: "\u039D", + Xi: "\u039E", + Omicron: "\u039F", + Pi: "\u03A0", + Rho: "\u03A1", + Sigma: "\u03A3", + Tau: "\u03A4", + Upsilon: "\u03A5", + Phi: "\u03A6", + Chi: "\u03A7", + Psi: "\u03A8", + Omega: "\u03A9", + alpha: "\u03B1", + beta: "\u03B2", + gamma: "\u03B3", + delta: "\u03B4", + epsilon: "\u03B5", + zeta: "\u03B6", + eta: "\u03B7", + theta: "\u03B8", + iota: "\u03B9", + kappa: "\u03BA", + lambda: "\u03BB", + mu: "\u03BC", + nu: "\u03BD", + xi: "\u03BE", + omicron: "\u03BF", + pi: "\u03C0", + rho: "\u03C1", + sigmaf: "\u03C2", + sigma: "\u03C3", + tau: "\u03C4", + upsilon: "\u03C5", + phi: "\u03C6", + chi: "\u03C7", + psi: "\u03C8", + omega: "\u03C9", + thetasym: "\u03D1", + upsih: "\u03D2", + piv: "\u03D6", + ensp: "\u2002", + emsp: "\u2003", + thinsp: "\u2009", + zwnj: "\u200C", + zwj: "\u200D", + lrm: "\u200E", + rlm: "\u200F", + ndash: "\u2013", + mdash: "\u2014", + lsquo: "\u2018", + rsquo: "\u2019", + sbquo: "\u201A", + ldquo: "\u201C", + rdquo: "\u201D", + bdquo: "\u201E", + dagger: "\u2020", + Dagger: "\u2021", + bull: "\u2022", + hellip: "\u2026", + permil: "\u2030", + prime: "\u2032", + Prime: "\u2033", + lsaquo: "\u2039", + rsaquo: "\u203A", + oline: "\u203E", + frasl: "\u2044", + euro: "\u20AC", + image: "\u2111", + weierp: "\u2118", + real: "\u211C", + trade: "\u2122", + alefsym: "\u2135", + larr: "\u2190", + uarr: "\u2191", + rarr: "\u2192", + darr: "\u2193", + harr: "\u2194", + crarr: "\u21B5", + lArr: "\u21D0", + uArr: "\u21D1", + rArr: "\u21D2", + dArr: "\u21D3", + hArr: "\u21D4", + forall: "\u2200", + part: "\u2202", + exist: "\u2203", + empty: "\u2205", + nabla: "\u2207", + isin: "\u2208", + notin: "\u2209", + ni: "\u220B", + prod: "\u220F", + sum: "\u2211", + minus: "\u2212", + lowast: "\u2217", + radic: "\u221A", + prop: "\u221D", + infin: "\u221E", + ang: "\u2220", + and: "\u2227", + or: "\u2228", + cap: "\u2229", + cup: "\u222A", + "int": "\u222B", + there4: "\u2234", + sim: "\u223C", + cong: "\u2245", + asymp: "\u2248", + ne: "\u2260", + equiv: "\u2261", + le: "\u2264", + ge: "\u2265", + sub: "\u2282", + sup: "\u2283", + nsub: "\u2284", + sube: "\u2286", + supe: "\u2287", + oplus: "\u2295", + otimes: "\u2297", + perp: "\u22A5", + sdot: "\u22C5", + lceil: "\u2308", + rceil: "\u2309", + lfloor: "\u230A", + rfloor: "\u230B", + lang: "\u2329", + rang: "\u232A", + loz: "\u25CA", + spades: "\u2660", + clubs: "\u2663", + hearts: "\u2665", + diams: "\u2666" +}; diff --git a/src/state.js b/src/state.js index cfc38c90de..651e485a7e 100755 --- a/src/state.js +++ b/src/state.js @@ -1,79 +1,88 @@ -import {reservedWords, keywords} from "./identifier" -import {types as tt} from "./tokentype" -import {lineBreak} from "./whitespace" +import {reservedWords, keywords} from "./identifier"; +import {types as tt} from "./tokentype"; +import {lineBreak} from "./whitespace"; export function Parser(options, input, startPos) { - this.options = options - this.sourceFile = this.options.sourceFile || null - this.isKeyword = keywords[this.options.ecmaVersion >= 6 ? 6 : 5] - this.isReservedWord = reservedWords[this.options.ecmaVersion] - this.input = input - this.loadPlugins(this.options.plugins) + this.options = options; + this.sourceFile = this.options.sourceFile || null; + this.isKeyword = keywords[this.options.ecmaVersion >= 6 ? 6 : 5]; + this.isReservedWord = reservedWords[this.options.ecmaVersion]; + this.input = input; + this.loadPlugins(this.options.plugins); // Set up token state // The current position of the tokenizer in the input. if (startPos) { - this.pos = startPos - this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos)) - this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length + this.pos = startPos; + this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos)); + this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length; } else { - this.pos = this.lineStart = 0 - this.curLine = 1 + this.pos = this.lineStart = 0; + this.curLine = 1; } // Properties of the current token: // Its type - this.type = tt.eof + this.type = tt.eof; // For tokens that include more information than their type, the value - this.value = null + this.value = null; // Its start and end offset - this.start = this.end = this.pos + this.start = this.end = this.pos; // And, if locations are used, the {line, column} object // corresponding to those offsets - this.startLoc = this.endLoc = null + this.startLoc = this.endLoc = this.curPosition(); // Position information for the previous token - this.lastTokEndLoc = this.lastTokStartLoc = null - this.lastTokStart = this.lastTokEnd = this.pos + this.lastTokEndLoc = this.lastTokStartLoc = null; + this.lastTokStart = this.lastTokEnd = this.pos; // The context stack is used to superficially track syntactic // context to predict whether a regular expression is allowed in a // given position. - this.context = this.initialContext() - this.exprAllowed = true + this.context = this.initialContext(); + this.exprAllowed = true; // Figure out if it's a module code. - this.inModule = this.options.sourceType === "module" - this.strict = this.options.strictMode === false ? false : this.inModule + this.inModule = this.options.sourceType === "module"; + this.strict = this.options.strictMode === false ? false : this.inModule; // Used to signify the start of a potential arrow function - this.potentialArrowAt = -1 + this.potentialArrowAt = -1; // Flags to track whether we are in a function, a generator. - this.inFunction = this.inGenerator = false + this.inFunction = this.inGenerator = false; // Labels in scope. - this.labels = [] + this.labels = []; - this.decorators = [] + this.decorators = []; // If enabled, skip leading hashbang line. - if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === '#!') - this.skipLineComment(2) + if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === "#!") { + this.skipLineComment(2); + } } -Parser.prototype.extend = function(name, f) { - this[name] = f(this[name]) -} +Parser.prototype.extend = function (name, f) { + this[name] = f(this[name]); +}; // Registered plugins -export const plugins = {} +export const plugins = {}; -Parser.prototype.loadPlugins = function(plugins) { +Parser.prototype.loadPlugins = function (plugins) { for (let name in plugins) { - let plugin = exports.plugins[name] - if (!plugin) throw new Error("Plugin '" + name + "' not found") - plugin(this, plugins[name]) + let plugin = exports.plugins[name]; + if (!plugin) throw new Error(`Plugin '${name}' not found`); + plugin(this, plugins[name]); } -} +}; + +Parser.prototype.parse = function () { + return new Promise((resolve) => { + let node = this.options.program || this.startNode(); + this.nextToken(); + resolve(this.parseTopLevel(node)); + }); +}; diff --git a/src/statement.js b/src/statement.js index 30940130fa..e62a54c2d4 100755 --- a/src/statement.js +++ b/src/statement.js @@ -1,8 +1,8 @@ -import {types as tt} from "./tokentype" -import {Parser} from "./state" -import {lineBreak} from "./whitespace" +import {types as tt} from "./tokentype"; +import {Parser} from "./state"; +import {lineBreak} from "./whitespace"; -const pp = Parser.prototype +const pp = Parser.prototype; // ### Statement parsing @@ -11,23 +11,25 @@ const pp = Parser.prototype // `program` argument. If present, the statements will be appended // to its body instead of creating a new node. -pp.parseTopLevel = function(node) { - let first = true - if (!node.body) node.body = [] +pp.parseTopLevel = function (node) { + let first = true; + if (!node.body) node.body = []; while (this.type !== tt.eof) { - let stmt = this.parseStatement(true, true) - node.body.push(stmt) - if (first && this.isUseStrict(stmt)) this.setStrict(true) - first = false + let stmt = this.parseStatement(true, true); + node.body.push(stmt); + if (first) { + if (this.isUseStrict(stmt)) this.setStrict(true); + first = false; + } } - this.next() + this.next(); if (this.options.ecmaVersion >= 6) { - node.sourceType = this.options.sourceType + node.sourceType = this.options.sourceType; } - return this.finishNode(node, "Program") -} + return this.finishNode(node, "Program"); +}; -const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"} +const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}; // Parse a single statement. // @@ -36,51 +38,52 @@ const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"} // `if (foo) /blah/.exec(foo)`, where looking at the previous token // does not help. -pp.parseStatement = function(declaration, topLevel) { +pp.parseStatement = function (declaration, topLevel) { if (this.type === tt.at) { - this.parseDecorators(true) + this.parseDecorators(true); } - let starttype = this.type, node = this.startNode() + let starttype = this.type, node = this.startNode(); // Most types of statements are recognized by the keyword they // start with. Many are trivial to parse, some require a bit of // complexity. switch (starttype) { - case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword) - case tt._debugger: return this.parseDebuggerStatement(node) - case tt._do: return this.parseDoStatement(node) - case tt._for: return this.parseForStatement(node) + case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword); + case tt._debugger: return this.parseDebuggerStatement(node); + case tt._do: return this.parseDoStatement(node); + case tt._for: return this.parseForStatement(node); case tt._function: - if (!declaration && this.options.ecmaVersion >= 6) this.unexpected() - return this.parseFunctionStatement(node) + if (!declaration && this.options.ecmaVersion >= 6) this.unexpected(); + return this.parseFunctionStatement(node); case tt._class: - if (!declaration) this.unexpected() - this.takeDecorators(node) - return this.parseClass(node, true) + if (!declaration) this.unexpected(); + this.takeDecorators(node); + return this.parseClass(node, true); - case tt._if: return this.parseIfStatement(node) - case tt._return: return this.parseReturnStatement(node) - case tt._switch: return this.parseSwitchStatement(node) - 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) - case tt._while: return this.parseWhileStatement(node) - case tt._with: return this.parseWithStatement(node) - case tt.braceL: return this.parseBlock() - case tt.semi: return this.parseEmptyStatement(node) + case tt._if: return this.parseIfStatement(node); + case tt._return: return this.parseReturnStatement(node); + case tt._switch: return this.parseSwitchStatement(node); + 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); + case tt._while: return this.parseWhileStatement(node); + case tt._with: return this.parseWithStatement(node); + case tt.braceL: return this.parseBlock(); + case tt.semi: return this.parseEmptyStatement(node); case tt._export: case tt._import: if (!this.options.allowImportExportEverywhere) { if (!topLevel) - this.raise(this.start, "'import' and 'export' may only appear at the top level") + this.raise(this.start, "'import' and 'export' may only appear at the top level"); + if (!this.inModule) - this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'") + this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); } - return starttype === tt._import ? this.parseImport(node) : this.parseExport(node) + return starttype === tt._import ? this.parseImport(node) : this.parseExport(node); case tt.name: if (this.options.features["es7.asyncFunctions"] && this.value === "async") { @@ -98,21 +101,24 @@ pp.parseStatement = function(declaration, topLevel) { // next token is a colon and the expression was a simple // Identifier node, we switch to interpreting it as a label. default: - let maybeName = this.value, expr = this.parseExpression() - if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) - return this.parseLabeledStatement(node, maybeName, expr) - else return this.parseExpressionStatement(node, expr) - } -} + let maybeName = this.value, expr = this.parseExpression(); -pp.takeDecorators = function(node) { + if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) { + return this.parseLabeledStatement(node, maybeName, expr); + } else { + return this.parseExpressionStatement(node, expr); + } + } +}; + +pp.takeDecorators = function (node) { if (this.decorators.length) { - node.decorators = this.decorators - this.decorators = [] + node.decorators = this.decorators; + this.decorators = []; } -} +}; -pp.parseDecorators = function(allowExport) { +pp.parseDecorators = function (allowExport) { while (this.type === tt.at) { this.decorators.push(this.parseDecorator()); } @@ -124,61 +130,64 @@ pp.parseDecorators = function(allowExport) { if (this.type !== tt._class) { this.raise(this.start, "Leading decorators must be attached to a class declaration"); } -} +}; -pp.parseDecorator = function(allowExport) { +pp.parseDecorator = function () { if (!this.options.features["es7.decorators"]) { - this.unexpected() + this.unexpected(); } - let node = this.startNode() - this.next() - node.expression = this.parseMaybeAssign() - return this.finishNode(node, "Decorator") -} + let node = this.startNode(); + this.next(); + node.expression = this.parseMaybeAssign(); + return this.finishNode(node, "Decorator"); +}; -pp.parseBreakContinueStatement = function(node, keyword) { - let isBreak = keyword == "break" - this.next() - if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null - else if (this.type !== tt.name) this.unexpected() - else { - node.label = this.parseIdent() - this.semicolon() +pp.parseBreakContinueStatement = function (node, keyword) { + let isBreak = keyword === "break"; + this.next(); + + if (this.eat(tt.semi) || this.insertSemicolon()) { + node.label = null; + } else if (this.type !== tt.name) { + this.unexpected(); + } else { + node.label = this.parseIdent(); + this.semicolon(); } // Verify that there is an actual destination to break or // continue to. for (var i = 0; i < this.labels.length; ++i) { - let lab = this.labels[i] + let lab = this.labels[i]; if (node.label == null || lab.name === node.label.name) { - if (lab.kind != null && (isBreak || lab.kind === "loop")) break - if (node.label && isBreak) break + if (lab.kind != null && (isBreak || lab.kind === "loop")) break; + if (node.label && isBreak) break; } } - if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword) - return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement") -} + if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword); + return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); +}; -pp.parseDebuggerStatement = function(node) { - this.next() - this.semicolon() - return this.finishNode(node, "DebuggerStatement") -} +pp.parseDebuggerStatement = function (node) { + this.next(); + this.semicolon(); + return this.finishNode(node, "DebuggerStatement"); +}; -pp.parseDoStatement = function(node) { - let start = this.markPosition() - this.next() - this.labels.push(loopLabel) - node.body = this.parseStatement(false) - this.labels.pop() - this.expect(tt._while) - node.test = this.parseParenExpression() - if (this.options.ecmaVersion >= 6) - this.eat(tt.semi) - else - this.semicolon() - return this.finishNode(node, "DoWhileStatement") -} +pp.parseDoStatement = function (node) { + this.next(); + this.labels.push(loopLabel); + node.body = this.parseStatement(false); + this.labels.pop(); + this.expect(tt._while); + node.test = this.parseParenExpression(); + if (this.options.ecmaVersion >= 6) { + this.eat(tt.semi); + } else { + this.semicolon(); + } + return this.finishNode(node, "DoWhileStatement"); +}; // Disambiguating between a `for` and a `for`/`in` or `for`/`of` // loop is non-trivial. Basically, we have to parse the init `var` @@ -188,575 +197,610 @@ pp.parseDoStatement = function(node) { // part (semicolon immediately after the opening parenthesis), it // is a regular `for` loop. -pp.parseForStatement = function(node) { - this.next() - this.labels.push(loopLabel) - this.expect(tt.parenL) - if (this.type === tt.semi) return this.parseFor(node, null) +pp.parseForStatement = function (node) { + this.next(); + 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 || this.type === tt._const) { - let init = this.startNode(), varKind = this.type - this.next() - this.parseVar(init, true, varKind) - this.finishNode(init, "VariableDeclaration") + let 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 && !(varKind !== tt._var && init.declarations[0].init)) - return this.parseForIn(node, init) - return this.parseFor(node, init) + return this.parseForIn(node, init); + return this.parseFor(node, init); } - let refShorthandDefaultPos = {start: 0} - let init = this.parseExpression(true, refShorthandDefaultPos) + + let refShorthandDefaultPos = {start: 0}; + let init = this.parseExpression(true, refShorthandDefaultPos); if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) { - this.toAssignable(init) - this.checkLVal(init) - return this.parseForIn(node, init) + this.toAssignable(init); + this.checkLVal(init); + return this.parseForIn(node, init); } else if (refShorthandDefaultPos.start) { - this.unexpected(refShorthandDefaultPos.start) + this.unexpected(refShorthandDefaultPos.start); } - return this.parseFor(node, init) -} + return this.parseFor(node, init); +}; -pp.parseFunctionStatement = function(node) { - this.next() - return this.parseFunction(node, true) -} +pp.parseFunctionStatement = function (node) { + this.next(); + return this.parseFunction(node, true); +}; -pp.parseIfStatement = function(node) { - this.next() - node.test = this.parseParenExpression() - node.consequent = this.parseStatement(false) - node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null - return this.finishNode(node, "IfStatement") -} +pp.parseIfStatement = function (node) { + this.next(); + node.test = this.parseParenExpression(); + node.consequent = this.parseStatement(false); + node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null; + return this.finishNode(node, "IfStatement"); +}; -pp.parseReturnStatement = function(node) { - if (!this.inFunction && !this.options.allowReturnOutsideFunction) - this.raise(this.start, "'return' outside of function") - this.next() +pp.parseReturnStatement = function (node) { + if (!this.inFunction && !this.options.allowReturnOutsideFunction) { + this.raise(this.start, "'return' outside of function"); + } + + this.next(); // In `return` (and `break`/`continue`), the keywords with // optional arguments, we eagerly look for a semicolon or the // possibility to insert one. - if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null - else { node.argument = this.parseExpression(); this.semicolon() } - return this.finishNode(node, "ReturnStatement") -} + if (this.eat(tt.semi) || this.insertSemicolon()) { + node.argument = null; + } else { + node.argument = this.parseExpression(); + this.semicolon(); + } -pp.parseSwitchStatement = function(node) { - this.next() - node.discriminant = this.parseParenExpression() - node.cases = [] - this.expect(tt.braceL) - this.labels.push(switchLabel) + return this.finishNode(node, "ReturnStatement"); +}; + +pp.parseSwitchStatement = function (node) { + this.next(); + node.discriminant = this.parseParenExpression(); + node.cases = []; + this.expect(tt.braceL); + this.labels.push(switchLabel); // Statements under must be grouped (by label) in SwitchCase // nodes. `cur` is used to keep the node that we are currently // adding statements to. - for (var cur, sawDefault; this.type != tt.braceR;) { + for (var cur, sawDefault; this.type !== tt.braceR; ) { if (this.type === tt._case || this.type === tt._default) { - let isCase = this.type === tt._case - if (cur) this.finishNode(cur, "SwitchCase") - node.cases.push(cur = this.startNode()) - cur.consequent = [] - this.next() + let isCase = this.type === tt._case; + if (cur) this.finishNode(cur, "SwitchCase"); + node.cases.push(cur = this.startNode()); + cur.consequent = []; + this.next(); if (isCase) { - cur.test = this.parseExpression() + cur.test = this.parseExpression(); } else { - if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses") - sawDefault = true - cur.test = null + if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses"); + sawDefault = true; + cur.test = null; } - this.expect(tt.colon) + this.expect(tt.colon); } else { - if (!cur) this.unexpected() - cur.consequent.push(this.parseStatement(true)) + if (!cur) this.unexpected(); + cur.consequent.push(this.parseStatement(true)); } } - if (cur) this.finishNode(cur, "SwitchCase") - this.next() // Closing brace - this.labels.pop() - return this.finishNode(node, "SwitchStatement") -} + if (cur) this.finishNode(cur, "SwitchCase"); + this.next(); // Closing brace + this.labels.pop(); + return this.finishNode(node, "SwitchStatement"); +}; -pp.parseThrowStatement = function(node) { - this.next() +pp.parseThrowStatement = function (node) { + this.next(); if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) - this.raise(this.lastTokEnd, "Illegal newline after throw") - node.argument = this.parseExpression() - this.semicolon() - return this.finishNode(node, "ThrowStatement") -} + this.raise(this.lastTokEnd, "Illegal newline after throw"); + node.argument = this.parseExpression(); + this.semicolon(); + return this.finishNode(node, "ThrowStatement"); +}; // Reused empty array added for node fields that are always empty. -const empty = [] +var empty = []; -pp.parseTryStatement = function(node) { - this.next() - node.block = this.parseBlock() - node.handler = null +pp.parseTryStatement = function (node) { + this.next(); + node.block = this.parseBlock(); + node.handler = null; if (this.type === tt._catch) { - let clause = this.startNode() - this.next() - this.expect(tt.parenL) - clause.param = this.parseBindingAtom() - this.checkLVal(clause.param, true) - this.expect(tt.parenR) - clause.guard = null - clause.body = this.parseBlock() - node.handler = this.finishNode(clause, "CatchClause") + let clause = this.startNode(); + this.next(); + this.expect(tt.parenL); + clause.param = this.parseBindingAtom(); + this.checkLVal(clause.param, true); + this.expect(tt.parenR); + clause.body = this.parseBlock(); + node.handler = this.finishNode(clause, "CatchClause"); } - node.guardedHandlers = empty - node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null - if (!node.handler && !node.finalizer) - this.raise(node.start, "Missing catch or finally clause") - return this.finishNode(node, "TryStatement") -} -pp.parseVarStatement = function(node, kind) { - this.next() - this.parseVar(node, false, kind) - this.semicolon() - return this.finishNode(node, "VariableDeclaration") -} + node.guardedHandlers = empty; + node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null; -pp.parseWhileStatement = function(node) { - this.next() - node.test = this.parseParenExpression() - this.labels.push(loopLabel) - node.body = this.parseStatement(false) - this.labels.pop() - return this.finishNode(node, "WhileStatement") -} + if (!node.handler && !node.finalizer) { + this.raise(node.start, "Missing catch or finally clause"); + } -pp.parseWithStatement = function(node) { - if (this.strict) this.raise(this.start, "'with' in strict mode") - this.next() - node.object = this.parseParenExpression() - node.body = this.parseStatement(false) - return this.finishNode(node, "WithStatement") -} + return this.finishNode(node, "TryStatement"); +}; -pp.parseEmptyStatement = function(node) { - this.next() - return this.finishNode(node, "EmptyStatement") -} +pp.parseVarStatement = function (node, kind) { + this.next(); + this.parseVar(node, false, kind); + this.semicolon(); + return this.finishNode(node, "VariableDeclaration"); +}; -pp.parseLabeledStatement = function(node, maybeName, expr) { - for (let i = 0; i < this.labels.length; ++i) - if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared") - let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null +pp.parseWhileStatement = function (node) { + this.next(); + node.test = this.parseParenExpression(); + this.labels.push(loopLabel); + node.body = this.parseStatement(false); + this.labels.pop(); + return this.finishNode(node, "WhileStatement"); +}; + +pp.parseWithStatement = function (node) { + if (this.strict) this.raise(this.start, "'with' in strict mode"); + this.next(); + node.object = this.parseParenExpression(); + node.body = this.parseStatement(false); + return this.finishNode(node, "WithStatement"); +}; + +pp.parseEmptyStatement = function (node) { + this.next(); + return this.finishNode(node, "EmptyStatement"); +}; + +pp.parseLabeledStatement = function (node, maybeName, expr) { + for (let i = 0; i < this.labels.length; ++i){ + if (this.labels[i].name === maybeName) { + this.raise(expr.start, `Label '${maybeName}' is already declared`); + } + } + + let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null; for (let i = this.labels.length - 1; i >= 0; i--) { - let label = this.labels[i] - if (label.statementStart == node.start) { + let label = this.labels[i]; + if (label.statementStart === node.start) { label.statementStart = this.start; label.kind = kind; - } else break; + } else { + break; + } } - this.labels.push({name: maybeName, kind: kind, statementStart: this.start}) - node.body = this.parseStatement(true) - this.labels.pop() - node.label = expr - return this.finishNode(node, "LabeledStatement") -} -pp.parseExpressionStatement = function(node, expr) { - node.expression = expr - this.semicolon() - return this.finishNode(node, "ExpressionStatement") -} + this.labels.push({name: maybeName, kind: kind, statementStart: this.start}); + node.body = this.parseStatement(true); + this.labels.pop(); + node.label = expr; + return this.finishNode(node, "LabeledStatement"); +}; + +pp.parseExpressionStatement = function (node, expr) { + node.expression = expr; + this.semicolon(); + return this.finishNode(node, "ExpressionStatement"); +}; // Parse a semicolon-enclosed block of statements, handling `"use // strict"` declarations when `allowStrict` is true (used for // function bodies). -pp.parseBlock = function(allowStrict) { - let node = this.startNode(), first = true, oldStrict - node.body = [] - this.expect(tt.braceL) +pp.parseBlock = function (allowStrict) { + let node = this.startNode(), first = true, oldStrict; + node.body = []; + this.expect(tt.braceL); while (!this.eat(tt.braceR)) { - let stmt = this.parseStatement(true) - node.body.push(stmt) + let stmt = this.parseStatement(true); + node.body.push(stmt); if (first && allowStrict && this.isUseStrict(stmt)) { - oldStrict = this.strict - this.setStrict(this.strict = true) + oldStrict = this.strict; + this.setStrict(this.strict = true); } - first = false + first = false; } - if (oldStrict === false) this.setStrict(false) - return this.finishNode(node, "BlockStatement") -} + if (oldStrict === false) this.setStrict(false); + return this.finishNode(node, "BlockStatement"); +}; // Parse a regular `for` loop. The disambiguation code in // `parseStatement` will already have parsed the init statement or // expression. -pp.parseFor = function(node, init) { - node.init = init - this.expect(tt.semi) - node.test = this.type === tt.semi ? null : this.parseExpression() - this.expect(tt.semi) - node.update = this.type === tt.parenR ? null : this.parseExpression() - this.expect(tt.parenR) - node.body = this.parseStatement(false) - this.labels.pop() - return this.finishNode(node, "ForStatement") -} +pp.parseFor = function (node, init) { + node.init = init; + this.expect(tt.semi); + node.test = this.type === tt.semi ? null : this.parseExpression(); + this.expect(tt.semi); + node.update = this.type === tt.parenR ? null : this.parseExpression(); + this.expect(tt.parenR); + node.body = this.parseStatement(false); + this.labels.pop(); + return this.finishNode(node, "ForStatement"); +}; // Parse a `for`/`in` and `for`/`of` loop, which are almost // same from parser's perspective. -pp.parseForIn = function(node, init) { - let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement" - this.next() - node.left = init - node.right = this.parseExpression() - this.expect(tt.parenR) - node.body = this.parseStatement(false) - this.labels.pop() - return this.finishNode(node, type) -} +pp.parseForIn = function (node, init) { + let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"; + this.next(); + node.left = init; + node.right = this.parseExpression(); + this.expect(tt.parenR); + node.body = this.parseStatement(false); + this.labels.pop(); + return this.finishNode(node, type); +}; // Parse a list of variable declarations. -pp.parseVar = function(node, isFor, kind) { - node.declarations = [] - node.kind = kind.keyword +pp.parseVar = function (node, isFor, kind) { + node.declarations = []; + node.kind = kind.keyword; for (;;) { - let decl = this.startNode() - this.parseVarHead(decl) + let decl = this.startNode(); + this.parseVarHead(decl); if (this.eat(tt.eq)) { - decl.init = this.parseMaybeAssign(isFor) + decl.init = this.parseMaybeAssign(isFor); } else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) { - this.unexpected() - } else if (decl.id.type != "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) { - this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value") + this.unexpected(); + } else if (decl.id.type !== "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) { + this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value"); } else { - decl.init = null + decl.init = null; } - node.declarations.push(this.finishNode(decl, "VariableDeclarator")) - if (!this.eat(tt.comma)) break + node.declarations.push(this.finishNode(decl, "VariableDeclarator")); + if (!this.eat(tt.comma)) break; } - return node -} + return node; +}; pp.parseVarHead = function (decl) { - decl.id = this.parseBindingAtom() - this.checkLVal(decl.id, true) -} + decl.id = this.parseBindingAtom(); + this.checkLVal(decl.id, true); +}; // Parse a function declaration or literal (depending on the // `isStatement` parameter). -pp.parseFunction = function(node, isStatement, allowExpressionBody, isAsync) { - this.initFunction(node, isAsync) - if (this.options.ecmaVersion >= 6) - node.generator = this.eat(tt.star) - if (isStatement || this.type === tt.name) - node.id = this.parseIdent() - this.parseFunctionParams(node); - this.parseFunctionBody(node, allowExpressionBody) - return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression") -} +pp.parseFunction = function (node, isStatement, allowExpressionBody, isAsync) { + this.initFunction(node, isAsync); + if (this.options.ecmaVersion >= 6) { + node.generator = this.eat(tt.star); + } -pp.parseFunctionParams = function(node) { - this.expect(tt.parenL) - node.params = this.parseBindingList(tt.parenR, false, this.options.features["es7.trailingFunctionCommas"]) -} + if (isStatement || this.type === tt.name) { + node.id = this.parseIdent(); + } + + this.parseFunctionParams(node); + this.parseFunctionBody(node, allowExpressionBody); + return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); +}; + +pp.parseFunctionParams = function (node) { + this.expect(tt.parenL); + node.params = this.parseBindingList(tt.parenR, false, this.options.features["es7.trailingFunctionCommas"]); +}; // Parse a class declaration or literal (depending on the // `isStatement` parameter). -pp.parseClass = function(node, isStatement) { - this.next() - this.parseClassId(node, isStatement) - this.parseClassSuper(node) - var classBody = this.startNode() - let hadConstructor = false - classBody.body = [] - this.expect(tt.braceL) - let decorators = [] +pp.parseClass = function (node, isStatement) { + this.next(); + this.parseClassId(node, isStatement); + this.parseClassSuper(node); + var classBody = this.startNode(); + let hadConstructor = false; + classBody.body = []; + this.expect(tt.braceL); + let decorators = []; while (!this.eat(tt.braceR)) { - if (this.eat(tt.semi)) continue + if (this.eat(tt.semi)) continue; if (this.type === tt.at) { - decorators.push(this.parseDecorator()) - continue + decorators.push(this.parseDecorator()); + continue; } - var method = this.startNode() + var method = this.startNode(); if (decorators.length) { - method.decorators = decorators - decorators = [] + method.decorators = decorators; + decorators = []; } - let isMaybeStatic = this.type === tt.name && this.value === "static" - var isGenerator = this.eat(tt.star), isAsync = false - this.parsePropertyName(method) - method.static = isMaybeStatic && this.type !== tt.parenL + let isMaybeStatic = this.type === tt.name && this.value === "static"; + var isGenerator = this.eat(tt.star), isAsync = false; + this.parsePropertyName(method); + method.static = isMaybeStatic && this.type !== tt.parenL; if (method.static) { - if (isGenerator) this.unexpected() - isGenerator = this.eat(tt.star) - this.parsePropertyName(method) + if (isGenerator) this.unexpected(); + isGenerator = this.eat(tt.star); + this.parsePropertyName(method); } if (!isGenerator && method.key.type === "Identifier" && !method.computed && this.isClassProperty()) { - classBody.body.push(this.parseClassProperty(method)) - continue + classBody.body.push(this.parseClassProperty(method)); + continue; } if (this.options.features["es7.asyncFunctions"] && this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && method.key.name === "async") { - isAsync = true - this.parsePropertyName(method) + isAsync = true; + this.parsePropertyName(method); } - let isGetSet = false - method.kind = "method" + let isGetSet = false; + method.kind = "method"; if (!method.computed) { - let {key} = method + let {key} = method; if (!isAsync && !isGenerator && key.type === "Identifier" && this.type !== tt.parenL && (key.name === "get" || key.name === "set")) { - isGetSet = true - method.kind = key.name - key = this.parsePropertyName(method) + isGetSet = true; + method.kind = key.name; + key = this.parsePropertyName(method); } if (!method.static && (key.type === "Identifier" && key.name === "constructor" || key.type === "Literal" && key.value === "constructor")) { - if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class") - if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier") - if (isGenerator) this.raise(key.start, "Constructor can't be a generator") - if (isAsync) this.raise(key.start, "Constructor can't be an async function") - method.kind = "constructor" - hadConstructor = true + if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class"); + if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier"); + if (isGenerator) this.raise(key.start, "Constructor can't be a generator"); + if (isAsync) this.raise(key.start, "Constructor can't be an async function"); + method.kind = "constructor"; + hadConstructor = true; } } if (method.kind === "constructor" && method.decorators) { - this.raise(method.start, "You can't attach decorators to a class constructor") + this.raise(method.start, "You can't attach decorators to a class constructor"); } - this.parseClassMethod(classBody, method, isGenerator, isAsync) + this.parseClassMethod(classBody, method, isGenerator, isAsync); if (isGetSet) { - let paramCount = method.kind === "get" ? 0 : 1 + let paramCount = method.kind === "get" ? 0 : 1; if (method.value.params.length !== paramCount) { - let start = method.value.start - if (method.kind === "get") + let start = method.value.start; + if (method.kind === "get") { this.raise(start, "getter should have no params"); - else - this.raise(start, "setter should have exactly one param") + } else { + this.raise(start, "setter should have exactly one param"); + } } } } + if (decorators.length) { this.raise(this.start, "You have trailing decorators with no method"); } - node.body = this.finishNode(classBody, "ClassBody") - return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression") -} -pp.isClassProperty = function() { - return this.type === tt.eq || (this.type === tt.semi || this.canInsertSemicolon()) -} + node.body = this.finishNode(classBody, "ClassBody"); + return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); +}; -pp.parseClassProperty = function(node) { +pp.isClassProperty = function () { + return this.type === tt.eq || (this.type === tt.semi || this.canInsertSemicolon()); +}; + +pp.parseClassProperty = function (node) { if (this.type === tt.eq) { - if (!this.options.features["es7.classProperties"]) this.unexpected() - this.next() + if (!this.options.features["es7.classProperties"]) this.unexpected(); + this.next(); node.value = this.parseMaybeAssign(); } else { node.value = null; } - this.semicolon() - return this.finishNode(node, "ClassProperty") -} + this.semicolon(); + return this.finishNode(node, "ClassProperty"); +}; -pp.parseClassMethod = function(classBody, method, isGenerator, isAsync) { - method.value = this.parseMethod(isGenerator, isAsync) - classBody.body.push(this.finishNode(method, "MethodDefinition")) -} +pp.parseClassMethod = function (classBody, method, isGenerator, isAsync) { + method.value = this.parseMethod(isGenerator, isAsync); + classBody.body.push(this.finishNode(method, "MethodDefinition")); +}; -pp.parseClassId = function(node, isStatement) { - node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null -} +pp.parseClassId = function (node, isStatement) { + node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null; +}; -pp.parseClassSuper = function(node) { - node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null -} +pp.parseClassSuper = function (node) { + node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null; +}; // Parses module export declaration. -pp.parseExport = function(node) { - this.next() +pp.parseExport = function (node) { + this.next(); // export * from '...' if (this.type === tt.star) { - let specifier = this.startNode() - this.next() + let specifier = this.startNode(); + this.next(); if (this.options.features["es7.exportExtensions"] && this.eatContextual("as")) { - specifier.exported = this.parseIdent() - node.specifiers = [this.finishNode(specifier, "ExportNamespaceSpecifier")] - this.parseExportSpecifiersMaybe(node) - this.parseExportFrom(node) + specifier.exported = this.parseIdent(); + node.specifiers = [this.finishNode(specifier, "ExportNamespaceSpecifier")]; + this.parseExportSpecifiersMaybe(node); + this.parseExportFrom(node); } else { - this.parseExportFrom(node) - return this.finishNode(node, "ExportAllDeclaration") + this.parseExportFrom(node); + return this.finishNode(node, "ExportAllDeclaration"); } } else if (this.options.features["es7.exportExtensions"] && this.isExportDefaultSpecifier()) { - let specifier = this.startNode() - specifier.exported = this.parseIdent(true) - node.specifiers = [this.finishNode(specifier, "ExportDefaultSpecifier")] + let specifier = this.startNode(); + specifier.exported = this.parseIdent(true); + node.specifiers = [this.finishNode(specifier, "ExportDefaultSpecifier")]; if (this.type === tt.comma && this.lookahead().type === tt.star) { - this.expect(tt.comma) - let specifier = this.startNode() - this.expect(tt.star) - this.expectContextual("as") - specifier.exported = this.parseIdent() - node.specifiers.push(this.finishNode(specifier, "ExportNamespaceSpecifier")) + this.expect(tt.comma); + let specifier = this.startNode(); + this.expect(tt.star); + this.expectContextual("as"); + specifier.exported = this.parseIdent(); + node.specifiers.push(this.finishNode(specifier, "ExportNamespaceSpecifier")); } else { - this.parseExportSpecifiersMaybe(node) + this.parseExportSpecifiersMaybe(node); } - this.parseExportFrom(node) + this.parseExportFrom(node); } else if (this.eat(tt._default)) { // export default ... - let expr = this.parseMaybeAssign() - let needsSemi = true - if (expr.type == "FunctionExpression" || - expr.type == "ClassExpression") { - needsSemi = false + let expr = this.parseMaybeAssign(); + let needsSemi = true; + if (expr.type === "FunctionExpression" || expr.type === "ClassExpression") { + needsSemi = false; if (expr.id) { - expr.type = expr.type == "FunctionExpression" - ? "FunctionDeclaration" - : "ClassDeclaration" + expr.type = expr.type === "FunctionExpression" ? "FunctionDeclaration" : "ClassDeclaration"; } } - node.declaration = expr - if (needsSemi) this.semicolon() - this.checkExport(node) - return this.finishNode(node, "ExportDefaultDeclaration") + node.declaration = expr; + if (needsSemi) this.semicolon(); + this.checkExport(node); + return this.finishNode(node, "ExportDefaultDeclaration"); } else if (this.type.keyword || this.shouldParseExportDeclaration()) { - node.declaration = this.parseStatement(true) - node.specifiers = [] - node.source = null + node.declaration = this.parseStatement(true); + node.specifiers = []; + node.source = null; } else { // export { x, y as z } [from '...'] - node.declaration = null - node.specifiers = this.parseExportSpecifiers() + node.declaration = null; + node.specifiers = this.parseExportSpecifiers(); if (this.eatContextual("from")) { - node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); } else { - node.source = null + node.source = null; } - this.semicolon() + this.semicolon(); } - this.checkExport(node) - return this.finishNode(node, "ExportNamedDeclaration") -} + this.checkExport(node); + return this.finishNode(node, "ExportNamedDeclaration"); +}; pp.isExportDefaultSpecifier = function () { if (this.type === tt.name) { - return this.value !== "type" && this.value !== "async" + return this.value !== "type" && this.value !== "async"; } if (this.type !== tt._default) { - return false + return false; } - var lookahead = this.lookahead() - return lookahead.type === tt.comma || (lookahead.type === tt.name && lookahead.value === "from") -} + var lookahead = this.lookahead(); + return lookahead.type === tt.comma || (lookahead.type === tt.name && lookahead.value === "from"); +}; pp.parseExportSpecifiersMaybe = function (node) { if (this.eat(tt.comma)) { - node.specifiers = node.specifiers.concat(this.parseExportSpecifiers()) + node.specifiers = node.specifiers.concat(this.parseExportSpecifiers()); } -} +}; -pp.parseExportFrom = function(node) { - this.expectContextual("from") - node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() - this.semicolon() - this.checkExport(node) -} +pp.parseExportFrom = function (node) { + this.expectContextual("from"); + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); + this.semicolon(); + this.checkExport(node); +}; -pp.shouldParseExportDeclaration = function() { - return this.options.features["es7.asyncFunctions"] && this.isContextual("async") -} +pp.shouldParseExportDeclaration = function () { + return this.options.features["es7.asyncFunctions"] && this.isContextual("async"); +}; -pp.checkExport = function(node) { +pp.checkExport = function (node) { if (this.decorators.length) { - var isClass = node.declaration && (node.declaration.type === "ClassDeclaration" || node.declaration.type === "ClassExpression") + var isClass = node.declaration && (node.declaration.type === "ClassDeclaration" || node.declaration.type === "ClassExpression"); if (!node.declaration || !isClass) { this.raise(node.start, "You can only use decorators on an export when exporting a class"); } - this.takeDecorators(node.declaration) + this.takeDecorators(node.declaration); } -} +}; // Parses a comma-separated list of module exports. -pp.parseExportSpecifiers = function() { - let nodes = [], first = true +pp.parseExportSpecifiers = function () { + let nodes = [], first = true; // export { x, y as z } [from '...'] - this.expect(tt.braceL) - while (!this.eat(tt.braceR)) { - if (!first) { - this.expect(tt.comma) - if (this.afterTrailingComma(tt.braceR)) break - } else first = false + this.expect(tt.braceL); - let 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")) + while (!this.eat(tt.braceR)) { + if (first) { + first = false; + } else { + this.expect(tt.comma); + if (this.afterTrailingComma(tt.braceR)) break; + } + + let 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 -} + + return nodes; +}; // Parses import declaration. -pp.parseImport = function(node) { - this.next() +pp.parseImport = function (node) { + this.next(); + // import '...' if (this.type === tt.string) { - node.specifiers = empty - node.source = this.parseExprAtom() + node.specifiers = []; + node.source = this.parseExprAtom(); } else { - node.specifiers = [] - this.parseImportSpecifiers(node) - this.expectContextual("from") - node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + node.specifiers = []; + this.parseImportSpecifiers(node); + this.expectContextual("from"); + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); } - this.semicolon() - return this.finishNode(node, "ImportDeclaration") -} + this.semicolon(); + return this.finishNode(node, "ImportDeclaration"); +}; // Parses a comma-separated list of module imports. -pp.parseImportSpecifiers = function(node) { - var first = true +pp.parseImportSpecifiers = function (node) { + var first = true; if (this.type === tt.name) { // import defaultObj, { x, y as z } from '...' - var start = this.markPosition() - node.specifiers.push(this.parseImportSpecifierDefault(this.parseIdent(), start)) - if (!this.eat(tt.comma)) return + var startPos = this.start, startLoc = this.startLoc; + node.specifiers.push(this.parseImportSpecifierDefault(this.parseIdent(), startPos, startLoc)); + if (!this.eat(tt.comma)) return; } + if (this.type === tt.star) { - var specifier = this.startNode() - this.next() - this.expectContextual("as") - specifier.local = this.parseIdent() - this.checkLVal(specifier.local, true) - node.specifiers.push(this.finishNode(specifier, "ImportNamespaceSpecifier")) - return + let specifier = this.startNode(); + this.next(); + this.expectContextual("as"); + specifier.local = this.parseIdent(); + this.checkLVal(specifier.local, true); + node.specifiers.push(this.finishNode(specifier, "ImportNamespaceSpecifier")); + return; } - this.expect(tt.braceL) + + this.expect(tt.braceL); while (!this.eat(tt.braceR)) { - if (!first) { - this.expect(tt.comma) - if (this.afterTrailingComma(tt.braceR)) break - } else first = false + if (first) { + first = false; + } else { + this.expect(tt.comma); + if (this.afterTrailingComma(tt.braceR)) break; + } - var specifier = this.startNode() - specifier.imported = this.parseIdent(true) - specifier.local = this.eatContextual("as") ? this.parseIdent() : specifier.imported - this.checkLVal(specifier.local, true) - node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")) + let specifier = this.startNode(); + specifier.imported = this.parseIdent(true); + specifier.local = this.eatContextual("as") ? this.parseIdent() : specifier.imported; + this.checkLVal(specifier.local, true); + node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); } -} +}; -pp.parseImportSpecifierDefault = function (id, start) { - var node = this.startNodeAt(start) - node.local = id - this.checkLVal(node.local, true) - return this.finishNode(node, "ImportDefaultSpecifier") -} +pp.parseImportSpecifierDefault = function (id, startPos, startLoc) { + var node = this.startNodeAt(startPos, startLoc); + node.local = id; + this.checkLVal(node.local, true); + return this.finishNode(node, "ImportDefaultSpecifier"); +}; diff --git a/src/tokencontext.js b/src/tokencontext.js index f1cb36e7b8..8395160c8d 100755 --- a/src/tokencontext.js +++ b/src/tokencontext.js @@ -2,16 +2,16 @@ // given point in the program is loosely based on sweet.js' approach. // See https://github.com/mozilla/sweet.js/wiki/design -import {Parser} from "./state" -import {types as tt} from "./tokentype" -import {lineBreak} from "./whitespace" +import {Parser} from "./state"; +import {types as tt} from "./tokentype"; +import {lineBreak} from "./whitespace"; export class TokContext { constructor(token, isExpr, preserveSpace, override) { - this.token = token - this.isExpr = isExpr - this.preserveSpace = preserveSpace - this.override = override + this.token = token; + this.isExpr = !!isExpr; + this.preserveSpace = !!preserveSpace; + this.override = override; } } @@ -23,85 +23,96 @@ export const types = { p_expr: new TokContext("(", true), q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()), f_expr: new TokContext("function", true) -} +}; -const pp = Parser.prototype +const pp = Parser.prototype; -pp.initialContext = function() { - return [types.b_stat] -} +pp.initialContext = function () { + return [types.b_stat]; +}; + +pp.braceIsBlock = function (prevType) { + if (prevType === tt.colon) { + let parent = this.curContext(); + if (parent === types.b_stat || parent === types.b_expr) + return !parent.isExpr; + } -pp.braceIsBlock = function(prevType) { - let parent - if (prevType === tt.colon && (parent = this.curContext()).token == "{") - return !parent.isExpr if (prevType === tt._return) - return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) - if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof) - return true - if (prevType == tt.braceL) - return this.curContext() === types.b_stat - return !this.exprAllowed -} + return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)); -pp.updateContext = function(prevType) { - let update, type = this.type - if (type.keyword && prevType == tt.dot) - this.exprAllowed = false - else if (update = type.updateContext) - update.call(this, prevType) - else - this.exprAllowed = type.beforeExpr -} + if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof) + return true; + + if (prevType === tt.braceL) + return this.curContext() === types.b_stat; + + return !this.exprAllowed; +}; + +pp.updateContext = function (prevType) { + let update, type = this.type; + if (type.keyword && prevType === tt.dot) { + this.exprAllowed = false; + } else if (update = type.updateContext) { + update.call(this, prevType); + } else { + this.exprAllowed = type.beforeExpr; + } +}; // Token-specific context update code -tt.parenR.updateContext = tt.braceR.updateContext = function() { - if (this.context.length == 1) { - this.exprAllowed = true - return +tt.parenR.updateContext = tt.braceR.updateContext = function () { + if (this.context.length === 1) { + this.exprAllowed = true; + return; } - let out = this.context.pop() + + let out = this.context.pop(); if (out === types.b_stat && this.curContext() === types.f_expr) { - this.context.pop() - this.exprAllowed = false + this.context.pop(); + this.exprAllowed = false; } else if (out === types.b_tmpl) { - this.exprAllowed = true + this.exprAllowed = true; } else { - this.exprAllowed = !out.isExpr + this.exprAllowed = !out.isExpr; } -} +}; -tt.braceL.updateContext = function(prevType) { - this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr) - this.exprAllowed = true -} +tt.braceL.updateContext = function (prevType) { + this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr); + this.exprAllowed = true; +}; -tt.dollarBraceL.updateContext = function() { - this.context.push(types.b_tmpl) - this.exprAllowed = true -} +tt.dollarBraceL.updateContext = function () { + this.context.push(types.b_tmpl); + this.exprAllowed = true; +}; -tt.parenL.updateContext = function(prevType) { - let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while - this.context.push(statementParens ? types.p_stat : types.p_expr) - this.exprAllowed = true -} +tt.parenL.updateContext = function (prevType) { + let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while; + this.context.push(statementParens ? types.p_stat : types.p_expr); + this.exprAllowed = true; +}; -tt.incDec.updateContext = function() { +tt.incDec.updateContext = function () { // tokExprAllowed stays unchanged -} +}; -tt._function.updateContext = function() { - if (this.curContext() !== types.b_stat) - this.context.push(types.f_expr) - this.exprAllowed = false -} +tt._function.updateContext = function () { + if (this.curContext() !== types.b_stat) { + this.context.push(types.f_expr); + } -tt.backQuote.updateContext = function() { - if (this.curContext() === types.q_tmpl) - this.context.pop() - else - this.context.push(types.q_tmpl) - this.exprAllowed = false -} + this.exprAllowed = false; +}; + +tt.backQuote.updateContext = function () { + if (this.curContext() === types.q_tmpl) { + this.context.pop(); + } else { + this.context.push(types.q_tmpl); + } + this.exprAllowed = false; +}; diff --git a/src/tokenize.js b/src/tokenize.js index d9c22d5d04..8cade26638 100755 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -1,8 +1,8 @@ -import {isIdentifierStart, isIdentifierChar} from "./identifier" -import {types as tt, keywords as keywordTypes} from "./tokentype" -import {Parser} from "./state" -import {SourceLocation} from "./location" -import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace" +import {isIdentifierStart, isIdentifierChar} from "./identifier"; +import {types as tt, keywords as keywordTypes} from "./tokentype"; +import {Parser} from "./state"; +import {SourceLocation} from "./location"; +import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace"; // Object type used to represent tokens. Note that normally, tokens // simply exist as properties on the parser object. This is only @@ -10,194 +10,186 @@ import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace export class Token { constructor(p) { - this.type = p.type - this.value = p.value - this.start = p.start - this.end = p.end - if (p.options.locations) - this.loc = new SourceLocation(p, p.startLoc, p.endLoc) - if (p.options.ranges) - this.range = [p.start, p.end] + this.type = p.type; + this.value = p.value; + this.start = p.start; + this.end = p.end; + + if (p.options.locations) { + this.loc = new SourceLocation(p, p.startLoc, p.endLoc); + } + + if (p.options.ranges) { + this.range = [p.start, p.end]; + } } } // ## Tokenizer -const pp = Parser.prototype +const pp = Parser.prototype; // Are we running under Rhino? -const isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]" +const isRhino = typeof Packages === "object" && Object.prototype.toString.call(Packages) === "[object JavaPackage]"; // Move to the next token -pp.next = function() { +pp.next = function () { if (this.options.onToken && !this.isLookahead) - this.options.onToken(new Token(this)) + this.options.onToken(new Token(this)); - this.lastTokEnd = this.end - this.lastTokStart = this.start - this.lastTokEndLoc = this.endLoc - this.lastTokStartLoc = this.startLoc - this.nextToken() -} + this.lastTokEnd = this.end; + this.lastTokStart = this.start; + this.lastTokEndLoc = this.endLoc; + this.lastTokStartLoc = this.startLoc; + this.nextToken(); +}; -pp.getToken = function() { - this.next() - return new Token(this) -} - -// If we're in an ES6 environment, make parsers iterable -if (typeof Symbol !== "undefined") - pp[Symbol.iterator] = function () { - let self = this - return {next: function () { - let token = self.getToken() - return { - done: token.type === tt.eof, - value: token - } - }} - } +pp.getToken = function () { + this.next(); + return new Token(this); +}; // Toggle strict mode. Re-reads the next number or string to please // pedantic tests (`"use strict"; 010;` should fail). -pp.setStrict = function(strict) { - this.strict = strict - if (this.type !== tt.num && this.type !== tt.string) return - this.pos = this.start +pp.setStrict = function (strict) { + this.strict = strict; + if (this.type !== tt.num && this.type !== tt.string) return; + this.pos = this.start; if (this.options.locations) { while (this.pos < this.lineStart) { - this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1 - --this.curLine + this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1; + --this.curLine; } } - this.nextToken() -} + this.nextToken(); +}; -pp.curContext = function() { - return this.context[this.context.length - 1] -} +pp.curContext = function () { + return this.context[this.context.length - 1]; +}; // Read a single token, updating the parser object's token-related // properties. -pp.nextToken = function() { - let curContext = this.curContext() - if (!curContext || !curContext.preserveSpace) this.skipSpace() +pp.nextToken = function () { + let curContext = this.curContext(); + if (!curContext || !curContext.preserveSpace) this.skipSpace(); - this.start = this.pos - if (this.options.locations) this.startLoc = this.curPosition() - if (this.pos >= this.input.length) return this.finishToken(tt.eof) + this.start = this.pos; + if (this.options.locations) this.startLoc = this.curPosition(); + if (this.pos >= this.input.length) return this.finishToken(tt.eof); - if (curContext.override) return curContext.override(this) - else this.readToken(this.fullCharCodeAtPos()) -} + if (curContext.override) return curContext.override(this); + else this.readToken(this.fullCharCodeAtPos()); +}; -pp.readToken = function(code) { +pp.readToken = function (code) { // Identifier or keyword. '\uXXXX' sequences are allowed in // identifiers, so '\' also dispatches to that. if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */) - return this.readWord() + return this.readWord(); - return this.getTokenFromCode(code) -} + return this.getTokenFromCode(code); +}; -pp.fullCharCodeAtPos = function() { - let code = this.input.charCodeAt(this.pos) - if (code <= 0xd7ff || code >= 0xe000) return code - let next = this.input.charCodeAt(this.pos + 1) - return (code << 10) + next - 0x35fdc00 -} +pp.fullCharCodeAtPos = function () { + let code = this.input.charCodeAt(this.pos); + if (code <= 0xd7ff || code >= 0xe000) return code; -pp.skipBlockComment = function() { - let startLoc = this.options.onComment && this.options.locations && this.curPosition() - let start = this.pos, end = this.input.indexOf("*/", this.pos += 2) - if (end === -1) this.raise(this.pos - 2, "Unterminated comment") - this.pos = end + 2 + let next = this.input.charCodeAt(this.pos + 1); + return (code << 10) + next - 0x35fdc00; +}; + +pp.skipBlockComment = function () { + let startLoc = this.options.onComment && this.curPosition(); + let start = this.pos, end = this.input.indexOf("*/", this.pos += 2); + if (end === -1) this.raise(this.pos - 2, "Unterminated comment"); + this.pos = end + 2; if (this.options.locations) { - lineBreakG.lastIndex = start - let match + lineBreakG.lastIndex = start; + let match; while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) { - ++this.curLine - this.lineStart = match.index + match[0].length + ++this.curLine; + this.lineStart = match.index + match[0].length; } } if (this.options.onComment) this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos, - startLoc, this.options.locations && this.curPosition()) -} + startLoc, this.curPosition()); +}; -pp.skipLineComment = function(startSkip) { - let start = this.pos - let startLoc = this.options.onComment && this.options.locations && this.curPosition() - let ch = this.input.charCodeAt(this.pos+=startSkip) +pp.skipLineComment = function (startSkip) { + let start = this.pos; + let startLoc = this.options.onComment && this.curPosition(); + let ch = this.input.charCodeAt(this.pos+=startSkip); while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { - ++this.pos - ch = this.input.charCodeAt(this.pos) + ++this.pos; + ch = this.input.charCodeAt(this.pos); } if (this.options.onComment) this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, - startLoc, this.options.locations && this.curPosition()) -} + startLoc, this.curPosition()); +}; // Called at the start of the parse and after every token. Skips // whitespace and comments, and. pp.skipSpace = function() { - while (this.pos < this.input.length) { - let ch = this.input.charCodeAt(this.pos) - if (ch === 32) { // ' ' - ++this.pos - } else if (ch === 13) { - ++this.pos - let next = this.input.charCodeAt(this.pos) - if (next === 10) { - ++this.pos - } - if (this.options.locations) { - ++this.curLine - this.lineStart = this.pos - } - } else if (ch === 10 || ch === 8232 || ch === 8233) { - ++this.pos - if (this.options.locations) { - ++this.curLine - this.lineStart = this.pos - } - } else if (ch > 8 && ch < 14) { - ++this.pos - } else if (ch === 47) { // '/' - let next = this.input.charCodeAt(this.pos + 1) - if (next === 42) { // '*' - this.skipBlockComment() - } else if (next === 47) { // '/' - this.skipLineComment(2) - } else break - } else if (ch === 160) { // '\xa0' - ++this.pos - } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { - ++this.pos - } else { - break + loop: while (this.pos < this.input.length) { + let ch = this.input.charCodeAt(this.pos); + switch (ch) { + case 32: case 160: // ' ' + ++this.pos; + break; + case 13: + if (this.input.charCodeAt(this.pos + 1) === 10) { + ++this.pos; + } + case 10: case 8232: case 8233: + ++this.pos; + if (this.options.locations) { + ++this.curLine; + this.lineStart = this.pos; + } + break; + case 47: // '/' + switch (this.input.charCodeAt(this.pos + 1)) { + case 42: // '*' + this.skipBlockComment(); + break; + case 47: + this.skipLineComment(2); + break; + default: + break loop; + } + break; + default: + if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { + ++this.pos; + } else { + break loop; + } } } -} +}; // Called at the end of every token. Sets `end`, `val`, and // maintains `context` and `exprAllowed`, and skips the space after // the token, so that the next one's `start` will point at the // right position. -pp.finishToken = function(type, val) { - this.end = this.pos - if (this.options.locations) this.endLoc = this.curPosition() - let prevType = this.type - this.type = type - this.value = val +pp.finishToken = function (type, val) { + this.end = this.pos; + if (this.options.locations) this.endLoc = this.curPosition(); + let prevType = this.type; + this.type = type; + this.value = val; - this.updateContext(prevType) -} + this.updateContext(prevType); +}; // ### Token reading @@ -208,138 +200,155 @@ pp.finishToken = function(type, val) { // // All in the name of speed. // -pp.readToken_dot = function() { - let next = this.input.charCodeAt(this.pos + 1) - if (next >= 48 && next <= 57) return this.readNumber(true) - let next2 = this.input.charCodeAt(this.pos + 2) +pp.readToken_dot = function () { + let next = this.input.charCodeAt(this.pos + 1); + if (next >= 48 && next <= 57) return this.readNumber(true); + + let next2 = this.input.charCodeAt(this.pos + 2); if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.' - this.pos += 3 - return this.finishToken(tt.ellipsis) + this.pos += 3; + return this.finishToken(tt.ellipsis); } else { - ++this.pos - return this.finishToken(tt.dot) + ++this.pos; + return this.finishToken(tt.dot); } -} +}; -pp.readToken_slash = function() { // '/' - let next = this.input.charCodeAt(this.pos + 1) - if (this.exprAllowed) {++this.pos; return this.readRegexp();} - if (next === 61) return this.finishOp(tt.assign, 2) - return this.finishOp(tt.slash, 1) -} +pp.readToken_slash = function () { // '/' + let next = this.input.charCodeAt(this.pos + 1); + if (this.exprAllowed) { + ++this.pos; + return this.readRegexp(); + } + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.slash, 1); +}; -pp.readToken_mult_modulo = function(code) { // '%*' - var type = code === 42 ? tt.star : tt.modulo - var width = 1 - var next = this.input.charCodeAt(this.pos + 1) +pp.readToken_mult_modulo = function (code) { // '%*' + var type = code === 42 ? tt.star : tt.modulo; + var width = 1; + var next = this.input.charCodeAt(this.pos + 1); if (next === 42) { // '*' - width++ - next = this.input.charCodeAt(this.pos + 2) - type = tt.exponent + width++; + next = this.input.charCodeAt(this.pos + 2); + type = tt.exponent; } if (next === 61) { - width++ - type = tt.assign + width++; + type = tt.assign; } - return this.finishOp(type, width) -} + return this.finishOp(type, width); +}; -pp.readToken_pipe_amp = function(code) { // '|&' - let next = this.input.charCodeAt(this.pos + 1) - if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2) - if (next === 61) return this.finishOp(tt.assign, 2) - return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1) -} +pp.readToken_pipe_amp = function (code) { // '|&' + let next = this.input.charCodeAt(this.pos + 1); + if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2); + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1); +}; -pp.readToken_caret = function() { // '^' - let next = this.input.charCodeAt(this.pos + 1) - if (next === 61) return this.finishOp(tt.assign, 2) - return this.finishOp(tt.bitwiseXOR, 1) -} +pp.readToken_caret = function () { // '^' + let next = this.input.charCodeAt(this.pos + 1); + if (next === 61) { + return this.finishOp(tt.assign, 2); + } else { + return this.finishOp(tt.bitwiseXOR, 1); + } +}; + +pp.readToken_plus_min = function (code) { // '+-' + let next = this.input.charCodeAt(this.pos + 1); -pp.readToken_plus_min = function(code) { // '+-' - let next = this.input.charCodeAt(this.pos + 1) if (next === code) { - if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 && - lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) { + if (next === 45 && this.input.charCodeAt(this.pos + 2) === 62 && lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) { // A `-->` 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) -} -pp.readToken_lt_gt = function(code) { // '<>' - let next = this.input.charCodeAt(this.pos + 1) - let size = 1 + if (next === 61) { + return this.finishOp(tt.assign, 2); + } else { + return this.finishOp(tt.plusMin, 1); + } +}; + +pp.readToken_lt_gt = function (code) { // '<>' + 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) this.unexpected() + + if (next === 33 && code === 60 && this.input.charCodeAt(this.pos + 2) === 45 && this.input.charCodeAt(this.pos + 3) === 45) { + if (this.inModule) this.unexpected(); // `10;\n --> nothing", { + type: "Program", + body: [ + { + type: "ExpressionStatement", + expression: { + type: "AssignmentExpression", + operator: "=", + left: { + type: "Identifier", + name: "x" + }, + right: { + type: "BinaryExpression", + left: { + type: "UpdateExpression", + operator: "--", + prefix: false, + argument: { + type: "Identifier", + name: "y" + } + }, + operator: ">", + right: { + type: "Literal", + value: 10 + } + } + } + } + ] +}); + +test("'use strict';\nobject.static();", { + type: "Program", + body: [ + { + type: "ExpressionStatement", + expression: { + type: "Literal", + value: "use strict", + raw: "'use strict'" + } + }, + { + type: "ExpressionStatement", + expression: { + type: "CallExpression", + callee: { + type: "MemberExpression", + object: { + type: "Identifier", + name: "object" + }, + property: { + type: "Identifier", + name: "static" + }, + computed: false + }, + arguments: [] + } + } + ] +}); + +// Failure tests + +testFail("{", + "Unexpected token (1:1)"); + +testFail("}", + "Unexpected token (1:0)"); + +testFail("3ea", + "Invalid number (1:0)"); + +testFail("3in []", + "Identifier directly after number (1:1)"); + +testFail("3e", + "Invalid number (1:0)"); + +testFail("3e+", + "Invalid number (1:0)"); + +testFail("3e-", + "Invalid number (1:0)"); + +testFail("3x", + "Identifier directly after number (1:1)"); + +testFail("3x0", + "Identifier directly after number (1:1)"); + +testFail("0x", + "Expected number in radix 16 (1:2)"); + +testFail("09", + "Invalid number (1:0)"); + +testFail("018", + "Invalid number (1:0)"); + +testFail("01a", + "Identifier directly after number (1:2)"); + +testFail("3in[]", + "Identifier directly after number (1:1)"); + +testFail("0x3in[]", + "Identifier directly after number (1:3)"); + +testFail("\"Hello\nWorld\"", + "Unterminated string constant (1:0)"); + +testFail("x\\", + "Expecting Unicode escape sequence \\uXXXX (1:2)"); + +testFail("x\\u005c", + "Invalid Unicode escape (1:1)"); + +testFail("x\\u002a", + "Invalid Unicode escape (1:1)"); + +testFail("/", + "Unterminated regular expression (1:1)"); + +testFail("/test", + "Unterminated regular expression (1:1)"); + +testFail("var x = /[a-z]/\\ux", + "Bad character escape sequence (1:17)"); + +testFail("3 = 4", + "Assigning to rvalue (1:0)"); + +testFail("func() = 4", + "Assigning to rvalue (1:0)"); + +testFail("(1 + 1) = 10", + "Assigning to rvalue (1:1)"); + +testFail("1++", + "Assigning to rvalue (1:0)"); + +testFail("1--", + "Assigning to rvalue (1:0)"); + +testFail("++1", + "Assigning to rvalue (1:2)"); + +testFail("--1", + "Assigning to rvalue (1:2)"); + +testFail("for((1 + 1) in list) process(x);", + "Assigning to rvalue (1:5)"); + +testFail("[", + "Unexpected token (1:1)"); + +testFail("[,", + "Unexpected token (1:2)"); + +testFail("1 + {", + "Unexpected token (1:5)"); + +testFail("1 + { t:t ", + "Unexpected token (1:10)"); + +testFail("1 + { t:t,", + "Unexpected token (1:10)"); + +testFail("var x = /\n/", + "Unterminated regular expression (1:9)"); + +testFail("var x = \"\n", + "Unterminated string constant (1:8)"); + +testFail("var if = 42", + "Unexpected token (1:4)"); + +testFail("i + 2 = 42", + "Assigning to rvalue (1:0)"); + +testFail("+i = 42", + "Assigning to rvalue (1:0)"); + +testFail("1 + (", + "Unexpected token (1:5)"); + +testFail("\n\n\n{", + "Unexpected token (4:1)"); + +testFail("\n/* Some multiline\ncomment */\n)", + "Unexpected token (4:0)"); + +testFail("{ set 1 }", + "Unexpected token (1:6)"); + +testFail("{ get 2 }", + "Unexpected token (1:6)"); + +testFail("({ set: s(if) { } })", + "Unexpected token (1:10)"); + +testFail("({ set s(.) { } })", + "Unexpected token (1:9)"); + +testFail("({ set: s() { } })", + "Unexpected token (1:12)"); + +testFail("({ set: s(a, b) { } })", + "Unexpected token (1:16)"); + +testFail("({ get: g(d) { } })", + "Unexpected token (1:13)"); + +testFail("({ get i() { }, i: 42 })", + "Redefinition of property (1:16)"); + +testFail("({ i: 42, get i() { } })", + "Redefinition of property (1:14)"); + +testFail("({ set i(x) { }, i: 42 })", + "Redefinition of property (1:17)"); + +testFail("({ i: 42, set i(x) { } })", + "Redefinition of property (1:14)"); + +testFail("({ get i() { }, get i() { } })", + "Redefinition of property (1:20)"); + +testFail("({ set i(x) { }, set i(x) { } })", + "Redefinition of property (1:21)"); + +testFail("function t(...) { }", + "Unexpected token (1:11)"); + +testFail("function t(...) { }", + "Unexpected token (1:14)", + { ecmaVersion: 6 }); + +testFail("function t(...rest, b) { }", + "Unexpected token (1:18)", + { ecmaVersion: 6 }); + +testFail("function t(if) { }", + "Unexpected token (1:11)"); + +testFail("function t(true) { }", + "Unexpected token (1:11)"); + +testFail("function t(false) { }", + "Unexpected token (1:11)"); + +testFail("function t(null) { }", + "Unexpected token (1:11)"); + +testFail("function null() { }", + "Unexpected token (1:9)"); + +testFail("function true() { }", + "Unexpected token (1:9)"); + +testFail("function false() { }", + "Unexpected token (1:9)"); + +testFail("function if() { }", + "Unexpected token (1:9)"); + +testFail("a b;", + "Unexpected token (1:2)"); + +testFail("if.a;", + "Unexpected token (1:2)"); + +testFail("a if;", + "Unexpected token (1:2)"); + +testFail("a class;", + "Unexpected token (1:2)"); + +testFail("break\n", + "Unsyntactic break (1:0)"); + +testFail("break 1;", + "Unexpected token (1:6)"); + +testFail("continue\n", + "Unsyntactic continue (1:0)"); + +testFail("continue 2;", + "Unexpected token (1:9)"); + +testFail("throw", + "Unexpected token (1:5)"); + +testFail("throw;", + "Unexpected token (1:5)"); + +testFail("for (var i, i2 in {});", + "Unexpected token (1:15)"); + +testFail("for ((i in {}));", + "Unexpected token (1:14)"); + +testFail("for (i + 1 in {});", + "Assigning to rvalue (1:5)"); + +testFail("for (+i in {});", + "Assigning to rvalue (1:5)"); + +testFail("if(false)", + "Unexpected token (1:9)"); + +testFail("if(false) doThis(); else", + "Unexpected token (1:24)"); + +testFail("do", + "Unexpected token (1:2)"); + +testFail("while(false)", + "Unexpected token (1:12)"); + +testFail("for(;;)", + "Unexpected token (1:7)"); + +testFail("with(x)", + "Unexpected token (1:7)"); + +testFail("try { }", + "Missing catch or finally clause (1:0)"); + +testFail("‿ = 10", + "Unexpected character '‿' (1:0)"); + +testFail("if(true) let a = 1;", + "Unexpected token (1:13)"); + +testFail("switch (c) { default: default: }", + "Multiple default clauses (1:22)"); + +testFail("new X().\"s\"", + "Unexpected token (1:8)"); + +testFail("/*", + "Unterminated comment (1:0)"); + +testFail("/*\n\n\n", + "Unterminated comment (1:0)"); + +testFail("/**", + "Unterminated comment (1:0)"); + +testFail("/*\n\n*", + "Unterminated comment (1:0)"); + +testFail("/*hello", + "Unterminated comment (1:0)"); + +testFail("/*hello *", + "Unterminated comment (1:0)"); + +testFail("\n]", + "Unexpected token (2:0)"); + +testFail("\r]", + "Unexpected token (2:0)"); + +testFail("\r\n]", + "Unexpected token (2:0)"); + +testFail("\n\r]", + "Unexpected token (3:0)"); + +testFail("//\r\n]", + "Unexpected token (2:0)"); + +testFail("//\n\r]", + "Unexpected token (3:0)"); + +testFail("/a\\\n/", + "Unterminated regular expression (1:1)"); + +testFail("//\r \n]", + "Unexpected token (3:0)"); + +testFail("/*\r\n*/]", + "Unexpected token (2:2)"); + +testFail("/*\n\r*/]", + "Unexpected token (3:2)"); + +testFail("/*\r \n*/]", + "Unexpected token (3:2)"); + +testFail("\\\\", + "Expecting Unicode escape sequence \\uXXXX (1:1)"); + +testFail("\\u005c", + "Invalid Unicode escape (1:0)"); + +testFail("\\x", + "Expecting Unicode escape sequence \\uXXXX (1:1)"); + +testFail("\\u0000", + "Invalid Unicode escape (1:0)"); + +testFail("‌ = []", + "Unexpected character '‌' (1:0)"); + +testFail("‍ = []", + "Unexpected character '‍' (1:0)"); + +testFail("\"\\", + "Unterminated string constant (1:0)"); + +testFail("\"\\u", + "Bad character escape sequence (1:3)"); + +testFail("return", + "'return' outside of function (1:0)"); + +testFail("break", + "Unsyntactic break (1:0)"); + +testFail("continue", + "Unsyntactic continue (1:0)"); + +testFail("switch (x) { default: continue; }", + "Unsyntactic continue (1:22)"); + +testFail("do { x } *", + "Unexpected token (1:9)"); + +testFail("while (true) { break x; }", + "Unsyntactic break (1:15)"); + +testFail("while (true) { continue x; }", + "Unsyntactic continue (1:15)"); + +testFail("x: while (true) { (function () { break x; }); }", + "Unsyntactic break (1:33)"); + +testFail("x: while (true) { (function () { continue x; }); }", + "Unsyntactic continue (1:33)"); + +testFail("x: while (true) { (function () { break; }); }", + "Unsyntactic break (1:33)"); + +testFail("x: while (true) { (function () { continue; }); }", + "Unsyntactic continue (1:33)"); + +testFail("x: while (true) { x: while (true) { } }", + "Label 'x' is already declared (1:18)"); + +testFail("(function () { 'use strict'; delete i; }())", + "Deleting local variable in strict mode (1:29)"); + +testFail("(function () { 'use strict'; with (i); }())", + "'with' in strict mode (1:29)"); + +testFail("function hello() {'use strict'; ({ i: 42, i: 42 }) }", + "Redefinition of property (1:42)"); + +testFail("function hello() {'use strict'; ({ hasOwnProperty: 42, hasOwnProperty: 42 }) }", + "Redefinition of property (1:55)"); + +testFail("function hello() {'use strict'; var eval = 10; }", + "Binding eval in strict mode (1:36)"); + +testFail("function hello() {'use strict'; var arguments = 10; }", + "Binding arguments in strict mode (1:36)"); + +testFail("function hello() {'use strict'; try { } catch (eval) { } }", + "Binding eval in strict mode (1:47)"); + +testFail("function hello() {'use strict'; try { } catch (arguments) { } }", + "Binding arguments in strict mode (1:47)"); + +testFail("function hello() {'use strict'; eval = 10; }", + "Assigning to eval in strict mode (1:32)"); + +testFail("function hello() {'use strict'; arguments = 10; }", + "Assigning to arguments in strict mode (1:32)"); + +testFail("function hello() {'use strict'; ++eval; }", + "Assigning to eval in strict mode (1:34)"); + +testFail("function hello() {'use strict'; --eval; }", + "Assigning to eval in strict mode (1:34)"); + +testFail("function hello() {'use strict'; ++arguments; }", + "Assigning to arguments in strict mode (1:34)"); + +testFail("function hello() {'use strict'; --arguments; }", + "Assigning to arguments in strict mode (1:34)"); + +testFail("function hello() {'use strict'; eval++; }", + "Assigning to eval in strict mode (1:32)"); + +testFail("function hello() {'use strict'; eval--; }", + "Assigning to eval in strict mode (1:32)"); + +testFail("function hello() {'use strict'; arguments++; }", + "Assigning to arguments in strict mode (1:32)"); + +testFail("function hello() {'use strict'; arguments--; }", + "Assigning to arguments in strict mode (1:32)"); + +testFail("function hello() {'use strict'; function eval() { } }", + "Binding eval in strict mode (1:41)"); + +testFail("function hello() {'use strict'; function arguments() { } }", + "Binding arguments in strict mode (1:41)"); + +testFail("function eval() {'use strict'; }", + "Binding eval in strict mode (1:9)"); + +testFail("function arguments() {'use strict'; }", + "Binding arguments in strict mode (1:9)"); + +testFail("function hello() {'use strict'; (function eval() { }()) }", + "Binding eval in strict mode (1:42)"); + +testFail("function hello() {'use strict'; (function arguments() { }()) }", + "Binding arguments in strict mode (1:42)"); + +testFail("(function eval() {'use strict'; })()", + "Binding eval in strict mode (1:10)"); + +testFail("(function arguments() {'use strict'; })()", + "Binding arguments in strict mode (1:10)"); + +testFail("function hello() {'use strict'; ({ s: function eval() { } }); }", + "Binding eval in strict mode (1:47)"); + +testFail("(function package() {'use strict'; })()", + "Binding package in strict mode (1:10)"); + +testFail("function hello() {'use strict'; ({ i: 10, set s(eval) { } }); }", + "Binding eval in strict mode (1:48)"); + +testFail("function hello() {'use strict'; ({ set s(eval) { } }); }", + "Binding eval in strict mode (1:41)"); + +testFail("function hello() {'use strict'; ({ s: function s(eval) { } }); }", + "Binding eval in strict mode (1:49)"); + +testFail("function hello(eval) {'use strict';}", + "Binding eval in strict mode (1:15)"); + +testFail("function hello(arguments) {'use strict';}", + "Binding arguments in strict mode (1:15)"); + +testFail("function hello() { 'use strict'; function inner(eval) {} }", + "Binding eval in strict mode (1:48)"); + +testFail("function hello() { 'use strict'; function inner(arguments) {} }", + "Binding arguments in strict mode (1:48)"); + +testFail("function hello() { 'use strict'; \"\\1\"; }", + "Octal literal in strict mode (1:34)"); + +testFail("function hello() { 'use strict'; 021; }", + "Invalid number (1:33)"); + +testFail("function hello() { 'use strict'; ({ \"\\1\": 42 }); }", + "Octal literal in strict mode (1:37)"); + +testFail("function hello() { 'use strict'; ({ 021: 42 }); }", + "Invalid number (1:36)"); + +testFail("function hello() { \"use strict\"; function inner() { \"octal directive\\1\"; } }", + "Octal literal in strict mode (1:68)"); + +testFail("function hello() { \"use strict\"; var implements; }", + "The keyword 'implements' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var interface; }", + "The keyword 'interface' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var package; }", + "The keyword 'package' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var private; }", + "The keyword 'private' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var protected; }", + "The keyword 'protected' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var public; }", + "The keyword 'public' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var static; }", + "The keyword 'static' is reserved (1:37)"); + +testFail("function hello(static) { \"use strict\"; }", + "Binding static in strict mode (1:15)"); + +testFail("function static() { \"use strict\"; }", + "Binding static in strict mode (1:9)"); + +testFail("\"use strict\"; function static() { }", + "The keyword 'static' is reserved (1:23)"); + +testFail("function a(t, t) { \"use strict\"; }", + "Argument name clash in strict mode (1:14)"); + +testFail("function a(eval) { \"use strict\"; }", + "Binding eval in strict mode (1:11)"); + +testFail("function a(package) { \"use strict\"; }", + "Binding package in strict mode (1:11)"); + +testFail("function a() { \"use strict\"; function b(t, t) { }; }", + "Argument name clash in strict mode (1:43)"); + +testFail("(function a(t, t) { \"use strict\"; })", + "Argument name clash in strict mode (1:15)"); + +testFail("function a() { \"use strict\"; (function b(t, t) { }); }", + "Argument name clash in strict mode (1:44)"); + +testFail("(function a(eval) { \"use strict\"; })", + "Binding eval in strict mode (1:12)"); + +testFail("(function a(package) { \"use strict\"; })", + "Binding package in strict mode (1:12)"); + +testFail("\"use strict\";function foo(){\"use strict\";}function bar(){var v = 015}", + "Invalid number (1:65)"); + +testFail("var this = 10;", "Unexpected token (1:4)"); + +testFail("throw\n10;", "Illegal newline after throw (1:5)"); + + +// ECMA < 6 mode should work as before + +testFail("const a;", "Unexpected token (1:6)"); + +testFail("let x;", "Unexpected token (1:4)"); + +testFail("const a = 1;", "Unexpected token (1:6)"); + +testFail("let a = 1;", "Unexpected token (1:4)"); + +testFail("for(const x = 0;;);", "Unexpected token (1:10)"); + +testFail("for(let x = 0;;);", "Unexpected token (1:8)"); + +test("let++", { + type: "Program", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + }, + body: [ + { + type: "ExpressionStatement", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + }, + expression: { + type: "UpdateExpression", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + }, + operator: "++", + prefix: false, + argument: { + type: "Identifier", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 3 + } + }, + name: "let" + } + } + } + ] +}); + +// ECMA 6 support + +test("let x", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + init: null, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("let x, y;", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + init: null, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "y", + loc: { + start: { + line: 1, + column: 7 + }, + end: { + line: 1, + column: 8 + } + } + }, + init: null, + loc: { + start: { + line: 1, + column: 7 + }, + end: { + line: 1, + column: 8 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 9 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 9 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("let x = 42", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 10 + } + } + }, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 10 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 10 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 10 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("let eval = 42, arguments = 42", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "eval", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 8 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 11 + }, + end: { + line: 1, + column: 13 + } + } + }, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 13 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "arguments", + loc: { + start: { + line: 1, + column: 15 + }, + end: { + line: 1, + column: 24 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 27 + }, + end: { + line: 1, + column: 29 + } + } + }, + loc: { + start: { + line: 1, + column: 15 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 29 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("let x = 14, y = 3, z = 1977", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + init: { + type: "Literal", + value: 14, + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 10 + } + } + }, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 10 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "y", + loc: { + start: { + line: 1, + column: 12 + }, + end: { + line: 1, + column: 13 + } + } + }, + init: { + type: "Literal", + value: 3, + loc: { + start: { + line: 1, + column: 16 + }, + end: { + line: 1, + column: 17 + } + } + }, + loc: { + start: { + line: 1, + column: 12 + }, + end: { + line: 1, + column: 17 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "z", + loc: { + start: { + line: 1, + column: 19 + }, + end: { + line: 1, + column: 20 + } + } + }, + init: { + type: "Literal", + value: 1977, + loc: { + start: { + line: 1, + column: 23 + }, + end: { + line: 1, + column: 27 + } + } + }, + loc: { + start: { + line: 1, + column: 19 + }, + end: { + line: 1, + column: 27 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 27 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 27 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("for(let x = 0;;);", { + type: "Program", + body: [ + { + type: "ForStatement", + init: { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 9 + } + } + }, + init: { + type: "Literal", + value: 0, + loc: { + start: { + line: 1, + column: 12 + }, + end: { + line: 1, + column: 13 + } + } + }, + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 13 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 13 + } + } + }, + test: null, + update: null, + body: { + type: "EmptyStatement", + loc: { + start: { + line: 1, + column: 16 + }, + end: { + line: 1, + column: 17 + } + } + }, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 17 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 17 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("for(let x = 0, y = 1;;);", { + type: "Program", + body: [ + { + type: "ForStatement", + init: { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 9 + } + } + }, + init: { + type: "Literal", + value: 0, + loc: { + start: { + line: 1, + column: 12 + }, + end: { + line: 1, + column: 13 + } + } + }, + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 13 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "y", + loc: { + start: { + line: 1, + column: 15 + }, + end: { + line: 1, + column: 16 + } + } + }, + init: { + type: "Literal", + value: 1, + loc: { + start: { + line: 1, + column: 19 + }, + end: { + line: 1, + column: 20 + } + } + }, + loc: { + start: { + line: 1, + column: 15 + }, + end: { + line: 1, + column: 20 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 20 + } + } + }, + test: null, + update: null, + body: { + type: "EmptyStatement", + loc: { + start: { + line: 1, + column: 23 + }, + end: { + line: 1, + column: 24 + } + } + }, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 24 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 24 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("for (let x 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: null, + loc: { + start: { + line: 1, + column: 9 + }, + end: { + line: 1, + column: 10 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 5 + }, + end: { + line: 1, + column: 10 + } + } + }, + right: { + type: "Identifier", + name: "list", + loc: { + start: { + line: 1, + column: 14 + }, + end: { + line: 1, + column: 18 + } + } + }, + body: { + type: "ExpressionStatement", + expression: { + type: "CallExpression", + callee: { + type: "Identifier", + name: "process", + loc: { + start: { + line: 1, + column: 20 + }, + end: { + line: 1, + column: 27 + } + } + }, + arguments: [ + { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 28 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + loc: { + start: { + line: 1, + column: 20 + }, + end: { + line: 1, + column: 30 + } + } + }, + loc: { + start: { + line: 1, + column: 20 + }, + end: { + line: 1, + column: 31 + } + } + }, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 31 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 31 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("const x = 42", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 7 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 10 + }, + end: { + line: 1, + column: 12 + } + } + }, + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 12 + } + } + } + ], + kind: "const", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 12 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 12 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("const eval = 42, arguments = 42", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "eval", + loc: { + start: { + line: 1, + column: 6 + }, + 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: 6 + }, + end: { + line: 1, + column: 15 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "arguments", + loc: { + start: { + line: 1, + column: 17 + }, + end: { + line: 1, + column: 26 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 29 + }, + end: { + line: 1, + column: 31 + } + } + }, + loc: { + start: { + line: 1, + column: 17 + }, + end: { + line: 1, + column: 31 + } + } + } + ], + kind: "const", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 31 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 31 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("const x = 14, y = 3, z = 1977", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 7 + } + } + }, + init: { + type: "Literal", + value: 14, + loc: { + start: { + line: 1, + column: 10 + }, + end: { + line: 1, + column: 12 + } + } + }, + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 12 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "y", + loc: { + start: { + line: 1, + column: 14 + }, + end: { + line: 1, + column: 15 + } + } + }, + init: { + type: "Literal", + value: 3, + loc: { + start: { + line: 1, + column: 18 + }, + end: { + line: 1, + column: 19 + } + } + }, + loc: { + start: { + line: 1, + column: 14 + }, + end: { + line: 1, + column: 19 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "z", + loc: { + start: { + line: 1, + column: 21 + }, + end: { + line: 1, + column: 22 + } + } + }, + init: { + type: "Literal", + value: 1977, + loc: { + start: { + line: 1, + column: 25 + }, + end: { + line: 1, + column: 29 + } + } + }, + loc: { + start: { + line: 1, + column: 21 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + kind: "const", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 29 + } + } +}, {ecmaVersion: 6, locations: true}); + +testFail("const a;", "Unexpected token (1:7)", {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)"); + +testFail("for(var x of a);", "Unexpected token (1:10)"); + +// Assertion Tests +test(function TestComments() { + // Bear class + function Bear(x,y,z) { + this.position = [x||0,y||0,z||0] + } + + Bear.prototype.roar = function(message) { + return 'RAWWW: ' + message; // Whatever + }; + + function Cat() { + /* 1 + 2 + 3*/ + } + + Cat.prototype.roar = function(message) { + return 'MEOOWW: ' + /*stuff*/ message; + }; +}.toString().replace(/\r\n/g, '\n'), {}, { + onComment: [ + {type: "Line", value: " Bear class"}, + {type: "Line", value: " Whatever"}, + {type: "Block", value: [ + " 1", + " 2", + " 3" + ].join('\n')}, + {type: "Block", value: "stuff"} + ] +}); + +test(" HTML comment", {}, { + locations: true, + onComment: [{ + type: "Line", + value: " HTML comment", + loc: { + start: { line: 2, column: 0 }, + end: { line: 2, column: 16 } + } + }] +}); + +var tokTypes = acorn.tokTypes; + +test('var x = (1 + 2)', {}, { + locations: true, + loose: false, + onToken: [ + { + type: tokTypes._var, + value: "var", + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 3} + } + }, + { + type: tokTypes.name, + value: "x", + loc: { + start: {line: 1, column: 4}, + end: {line: 1, column: 5} + } + }, + { + type: tokTypes.eq, + value: "=", + loc: { + start: {line: 1, column: 6}, + end: {line: 1, column: 7} + } + }, + { + type: tokTypes.parenL, + value: undefined, + loc: { + start: {line: 1, column: 8}, + end: {line: 1, column: 9} + } + }, + { + type: tokTypes.num, + value: 1, + loc: { + start: {line: 1, column: 9}, + end: {line: 1, column: 10} + } + }, + { + type: {binop: 9, prefix: true, beforeExpr: true}, + value: "+", + loc: { + start: {line: 1, column: 11}, + end: {line: 1, column: 12} + } + }, + { + type: tokTypes.num, + value: 2, + loc: { + start: {line: 1, column: 13}, + end: {line: 1, column: 14} + } + }, + { + type: tokTypes.parenR, + value: undefined, + loc: { + start: {line: 1, column: 14}, + end: {line: 1, column: 15} + } + }, + { + type: tokTypes.eof, + value: undefined, + loc: { + start: {line: 1, column: 15}, + end: {line: 1, column: 15} + } + } + ] +}); + +test("function f(f) { 'use strict'; }", {}); + +// https://github.com/marijnh/acorn/issues/180 +test("#!/usr/bin/node\n;", {}, { + allowHashBang: true, + onComment: [{ + type: "Line", + value: "/usr/bin/node", + start: 0, + end: 15 + }] +}); + +// https://github.com/marijnh/acorn/issues/204 +test("(function () {} / 1)", { + type: "Program", + body: [{ + type: "ExpressionStatement", + expression: { + type: "BinaryExpression", + left: { + type: "FunctionExpression", + id: null, + params: [], + body: { + type: "BlockStatement", + body: [] + } + }, + operator: "/", + right: {type: "Literal", value: 1} + } + }] +}); + +test("function f() {} / 1 /", { + type: "Program", + body: [ + { + type: "FunctionDeclaration", + id: {type: "Identifier", name: "f"}, + params: [], + body: { + type: "BlockStatement", + body: [] + } + }, + { + type: "ExpressionStatement", + expression: { + type: "Literal", + regex: {pattern: " 1 ", flags: ""}, + value: / 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}) + +// https://github.com/marijnh/acorn/issues/275 + +testFail("({ get prop(x) {} })", "getter should have no params (1:11)"); +testFail("({ set prop() {} })", "setter should have exactly one param (1:11)"); +testFail("({ set prop(x, y) {} })", "setter should have exactly one param (1:11)");