update to modular acorn
This commit is contained in:
32
lib/acorn/AUTHORS
Normal file
32
lib/acorn/AUTHORS
Normal file
@@ -0,0 +1,32 @@
|
||||
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
|
||||
19
lib/acorn/LICENSE
Normal file
19
lib/acorn/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
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.
|
||||
44
lib/acorn/package.json
Normal file
44
lib/acorn/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "acorn",
|
||||
"description": "ECMAScript parser",
|
||||
"homepage": "https://github.com/marijnh/acorn",
|
||||
"main": "src/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"
|
||||
}
|
||||
}
|
||||
818
lib/acorn/plugins/flow.js
Normal file
818
lib/acorn/plugins/flow.js
Normal file
@@ -0,0 +1,818 @@
|
||||
"use strict";
|
||||
|
||||
var acorn = require("..");
|
||||
|
||||
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_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);
|
||||
|
||||
this.expect(tt.colon);
|
||||
typeNode.returnType = this.flow_parseType();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
this.expect(tt.eq);
|
||||
|
||||
node.right = this.flow_parseType();
|
||||
|
||||
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();
|
||||
this.expect(tt.colon);
|
||||
node.key = this.flow_parseType();
|
||||
this.expect(tt.bracketR);
|
||||
this.expect(tt.colon);
|
||||
node.value = this.flow_parseType();
|
||||
|
||||
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);
|
||||
this.expect(tt.colon);
|
||||
node.returnType = this.flow_parseType();
|
||||
|
||||
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;
|
||||
return this.finishNode(node, "ObjectTypeProperty");
|
||||
};
|
||||
|
||||
pp.flow_parseObjectTypeCallProperty = function (node, isStatic) {
|
||||
var valueNode = this.startNode();
|
||||
node["static"] = isStatic;
|
||||
node.value = this.flow_parseObjectTypeMethodish(valueNode);
|
||||
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;
|
||||
}
|
||||
this.expect(tt.colon);
|
||||
node.key = propertyKey;
|
||||
node.value = this.flow_parseType();
|
||||
node.optional = optional;
|
||||
node["static"] = isStatic;
|
||||
nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.eat(tt.semi) && this.type !== tt.braceR) {
|
||||
this.unexpected();
|
||||
}
|
||||
}
|
||||
|
||||
this.expect(tt.braceR);
|
||||
|
||||
return this.finishNode(nodeStart, "ObjectTypeAnnotation");
|
||||
};
|
||||
|
||||
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_parseVoidType = function () {
|
||||
var node = this.startNode();
|
||||
this.expect(tt._void);
|
||||
return this.finishNode(node, "VoidTypeAnnotation");
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
this.expect(tt.colon);
|
||||
node.optional = optional;
|
||||
node.typeAnnotation = this.flow_parseType();
|
||||
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 "bool":
|
||||
case "boolean":
|
||||
return this.finishNode(node, "BooleanTypeAnnotation");
|
||||
|
||||
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) {
|
||||
switch (this.type.keyword) {
|
||||
case "void":
|
||||
return this.flow_parseVoidType();
|
||||
|
||||
case "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();
|
||||
|
||||
var oldInType = this.inType;
|
||||
this.inType = true;
|
||||
this.expect(tt.colon);
|
||||
node.typeAnnotation = this.flow_parseType();
|
||||
this.inType = oldInType;
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
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("parseClassMethod", function (inner) {
|
||||
return function (classBody, method, isGenerator, isAsync) {
|
||||
var classProperty = false;
|
||||
|
||||
if (this.type === tt.colon) {
|
||||
method.typeAnnotation = this.flow_parseTypeAnnotation();
|
||||
classProperty = true;
|
||||
}
|
||||
|
||||
if (classProperty) {
|
||||
this.semicolon();
|
||||
classBody.body.push(this.finishNode(method, "ClassProperty"));
|
||||
} else {
|
||||
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<T>() {}
|
||||
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);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
637
lib/acorn/plugins/jsx.js
Normal file
637
lib/acorn/plugins/jsx.js
Normal file
@@ -0,0 +1,637 @@
|
||||
"use strict";
|
||||
|
||||
var acorn = require("..");
|
||||
|
||||
var tt = acorn.tokTypes;
|
||||
var tc = acorn.tokContexts;
|
||||
|
||||
tc.j_oTag = new acorn.TokContext("<tag", false);
|
||||
tc.j_cTag = new acorn.TokContext("</tag", false);
|
||||
tc.j_expr = new acorn.TokContext("<tag>...</tag>", true, true);
|
||||
|
||||
tt.jsxName = new acorn.TokenType("jsxName");
|
||||
tt.jsxText = new acorn.TokenType("jsxText", { beforeExpr: true });
|
||||
tt.jsxTagStart = new acorn.TokenType("jsxTagStart");
|
||||
tt.jsxTagEnd = new acorn.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 = acorn.Parser.prototype;
|
||||
|
||||
// Reads inline JSX contents token.
|
||||
|
||||
pp.jsx_readToken = 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.jsx_readEntity();
|
||||
chunkStart = this.pos;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (acorn.isNewLine(ch)) {
|
||||
out += this.input.slice(chunkStart, this.pos);
|
||||
++this.pos;
|
||||
if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {
|
||||
++this.pos;
|
||||
out += "\n";
|
||||
} else {
|
||||
out += String.fromCharCode(ch);
|
||||
}
|
||||
if (this.options.locations) {
|
||||
++this.curLine;
|
||||
this.lineStart = this.pos;
|
||||
}
|
||||
chunkStart = this.pos;
|
||||
} else {
|
||||
++this.pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pp.jsx_readString = 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.jsx_readEntity();
|
||||
chunkStart = this.pos;
|
||||
} else {
|
||||
++this.pos;
|
||||
}
|
||||
}
|
||||
out += this.input.slice(chunkStart, this.pos++);
|
||||
return this.finishToken(tt.string, out);
|
||||
};
|
||||
|
||||
var XHTMLEntities = {
|
||||
quot: "\"",
|
||||
amp: "&",
|
||||
apos: "'",
|
||||
lt: "<",
|
||||
gt: ">",
|
||||
nbsp: " ",
|
||||
iexcl: "¡",
|
||||
cent: "¢",
|
||||
pound: "£",
|
||||
curren: "¤",
|
||||
yen: "¥",
|
||||
brvbar: "¦",
|
||||
sect: "§",
|
||||
uml: "¨",
|
||||
copy: "©",
|
||||
ordf: "ª",
|
||||
laquo: "«",
|
||||
not: "¬",
|
||||
shy: "",
|
||||
reg: "®",
|
||||
macr: "¯",
|
||||
deg: "°",
|
||||
plusmn: "±",
|
||||
sup2: "²",
|
||||
sup3: "³",
|
||||
acute: "´",
|
||||
micro: "µ",
|
||||
para: "¶",
|
||||
middot: "·",
|
||||
cedil: "¸",
|
||||
sup1: "¹",
|
||||
ordm: "º",
|
||||
raquo: "»",
|
||||
frac14: "¼",
|
||||
frac12: "½",
|
||||
frac34: "¾",
|
||||
iquest: "¿",
|
||||
Agrave: "À",
|
||||
Aacute: "Á",
|
||||
Acirc: "Â",
|
||||
Atilde: "Ã",
|
||||
Auml: "Ä",
|
||||
Aring: "Å",
|
||||
AElig: "Æ",
|
||||
Ccedil: "Ç",
|
||||
Egrave: "È",
|
||||
Eacute: "É",
|
||||
Ecirc: "Ê",
|
||||
Euml: "Ë",
|
||||
Igrave: "Ì",
|
||||
Iacute: "Í",
|
||||
Icirc: "Î",
|
||||
Iuml: "Ï",
|
||||
ETH: "Ð",
|
||||
Ntilde: "Ñ",
|
||||
Ograve: "Ò",
|
||||
Oacute: "Ó",
|
||||
Ocirc: "Ô",
|
||||
Otilde: "Õ",
|
||||
Ouml: "Ö",
|
||||
times: "×",
|
||||
Oslash: "Ø",
|
||||
Ugrave: "Ù",
|
||||
Uacute: "Ú",
|
||||
Ucirc: "Û",
|
||||
Uuml: "Ü",
|
||||
Yacute: "Ý",
|
||||
THORN: "Þ",
|
||||
szlig: "ß",
|
||||
agrave: "à",
|
||||
aacute: "á",
|
||||
acirc: "â",
|
||||
atilde: "ã",
|
||||
auml: "ä",
|
||||
aring: "å",
|
||||
aelig: "æ",
|
||||
ccedil: "ç",
|
||||
egrave: "è",
|
||||
eacute: "é",
|
||||
ecirc: "ê",
|
||||
euml: "ë",
|
||||
igrave: "ì",
|
||||
iacute: "í",
|
||||
icirc: "î",
|
||||
iuml: "ï",
|
||||
eth: "ð",
|
||||
ntilde: "ñ",
|
||||
ograve: "ò",
|
||||
oacute: "ó",
|
||||
ocirc: "ô",
|
||||
otilde: "õ",
|
||||
ouml: "ö",
|
||||
divide: "÷",
|
||||
oslash: "ø",
|
||||
ugrave: "ù",
|
||||
uacute: "ú",
|
||||
ucirc: "û",
|
||||
uuml: "ü",
|
||||
yacute: "ý",
|
||||
thorn: "þ",
|
||||
yuml: "ÿ",
|
||||
OElig: "Œ",
|
||||
oelig: "œ",
|
||||
Scaron: "Š",
|
||||
scaron: "š",
|
||||
Yuml: "Ÿ",
|
||||
fnof: "ƒ",
|
||||
circ: "ˆ",
|
||||
tilde: "˜",
|
||||
Alpha: "Α",
|
||||
Beta: "Β",
|
||||
Gamma: "Γ",
|
||||
Delta: "Δ",
|
||||
Epsilon: "Ε",
|
||||
Zeta: "Ζ",
|
||||
Eta: "Η",
|
||||
Theta: "Θ",
|
||||
Iota: "Ι",
|
||||
Kappa: "Κ",
|
||||
Lambda: "Λ",
|
||||
Mu: "Μ",
|
||||
Nu: "Ν",
|
||||
Xi: "Ξ",
|
||||
Omicron: "Ο",
|
||||
Pi: "Π",
|
||||
Rho: "Ρ",
|
||||
Sigma: "Σ",
|
||||
Tau: "Τ",
|
||||
Upsilon: "Υ",
|
||||
Phi: "Φ",
|
||||
Chi: "Χ",
|
||||
Psi: "Ψ",
|
||||
Omega: "Ω",
|
||||
alpha: "α",
|
||||
beta: "β",
|
||||
gamma: "γ",
|
||||
delta: "δ",
|
||||
epsilon: "ε",
|
||||
zeta: "ζ",
|
||||
eta: "η",
|
||||
theta: "θ",
|
||||
iota: "ι",
|
||||
kappa: "κ",
|
||||
lambda: "λ",
|
||||
mu: "μ",
|
||||
nu: "ν",
|
||||
xi: "ξ",
|
||||
omicron: "ο",
|
||||
pi: "π",
|
||||
rho: "ρ",
|
||||
sigmaf: "ς",
|
||||
sigma: "σ",
|
||||
tau: "τ",
|
||||
upsilon: "υ",
|
||||
phi: "φ",
|
||||
chi: "χ",
|
||||
psi: "ψ",
|
||||
omega: "ω",
|
||||
thetasym: "ϑ",
|
||||
upsih: "ϒ",
|
||||
piv: "ϖ",
|
||||
ensp: " ",
|
||||
emsp: " ",
|
||||
thinsp: " ",
|
||||
zwnj: "",
|
||||
zwj: "",
|
||||
lrm: "",
|
||||
rlm: "",
|
||||
ndash: "–",
|
||||
mdash: "—",
|
||||
lsquo: "‘",
|
||||
rsquo: "’",
|
||||
sbquo: "‚",
|
||||
ldquo: "“",
|
||||
rdquo: "”",
|
||||
bdquo: "„",
|
||||
dagger: "†",
|
||||
Dagger: "‡",
|
||||
bull: "•",
|
||||
hellip: "…",
|
||||
permil: "‰",
|
||||
prime: "′",
|
||||
Prime: "″",
|
||||
lsaquo: "‹",
|
||||
rsaquo: "›",
|
||||
oline: "‾",
|
||||
frasl: "⁄",
|
||||
euro: "€",
|
||||
image: "ℑ",
|
||||
weierp: "℘",
|
||||
real: "ℜ",
|
||||
trade: "™",
|
||||
alefsym: "ℵ",
|
||||
larr: "←",
|
||||
uarr: "↑",
|
||||
rarr: "→",
|
||||
darr: "↓",
|
||||
harr: "↔",
|
||||
crarr: "↵",
|
||||
lArr: "⇐",
|
||||
uArr: "⇑",
|
||||
rArr: "⇒",
|
||||
dArr: "⇓",
|
||||
hArr: "⇔",
|
||||
forall: "∀",
|
||||
part: "∂",
|
||||
exist: "∃",
|
||||
empty: "∅",
|
||||
nabla: "∇",
|
||||
isin: "∈",
|
||||
notin: "∉",
|
||||
ni: "∋",
|
||||
prod: "∏",
|
||||
sum: "∑",
|
||||
minus: "−",
|
||||
lowast: "∗",
|
||||
radic: "√",
|
||||
prop: "∝",
|
||||
infin: "∞",
|
||||
ang: "∠",
|
||||
and: "∧",
|
||||
or: "∨",
|
||||
cap: "∩",
|
||||
cup: "∪",
|
||||
int: "∫",
|
||||
there4: "∴",
|
||||
sim: "∼",
|
||||
cong: "≅",
|
||||
asymp: "≈",
|
||||
ne: "≠",
|
||||
equiv: "≡",
|
||||
le: "≤",
|
||||
ge: "≥",
|
||||
sub: "⊂",
|
||||
sup: "⊃",
|
||||
nsub: "⊄",
|
||||
sube: "⊆",
|
||||
supe: "⊇",
|
||||
oplus: "⊕",
|
||||
otimes: "⊗",
|
||||
perp: "⊥",
|
||||
sdot: "⋅",
|
||||
lceil: "⌈",
|
||||
rceil: "⌉",
|
||||
lfloor: "⌊",
|
||||
rfloor: "⌋",
|
||||
lang: "〈",
|
||||
rang: "〉",
|
||||
loz: "◊",
|
||||
spades: "♠",
|
||||
clubs: "♣",
|
||||
hearts: "♥",
|
||||
diams: "♦"
|
||||
};
|
||||
|
||||
var hexNumber = /^[\da-fA-F]+$/;
|
||||
var decimalNumber = /^\d+$/;
|
||||
|
||||
pp.jsx_readEntity = 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 (hexNumber.test(str)) entity = String.fromCharCode(parseInt(str, 16));
|
||||
} else {
|
||||
str = str.substr(1);
|
||||
if (decimalNumber.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.jsx_readWord = function () {
|
||||
var ch,
|
||||
start = this.pos;
|
||||
do {
|
||||
ch = this.input.charCodeAt(++this.pos);
|
||||
} while (acorn.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.jsx_parseIdentifier = 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.jsx_parseNamespacedName = function () {
|
||||
var start = this.markPosition();
|
||||
var name = this.jsx_parseIdentifier();
|
||||
if (!this.eat(tt.colon)) return name;
|
||||
var node = this.startNodeAt(start);
|
||||
node.namespace = name;
|
||||
node.name = this.jsx_parseIdentifier();
|
||||
return this.finishNode(node, "JSXNamespacedName");
|
||||
};
|
||||
|
||||
// Parses element name in any form - namespaced, member
|
||||
// or single identifier.
|
||||
|
||||
pp.jsx_parseElementName = function () {
|
||||
var start = this.markPosition();
|
||||
var node = this.jsx_parseNamespacedName();
|
||||
while (this.eat(tt.dot)) {
|
||||
var newNode = this.startNodeAt(start);
|
||||
newNode.object = node;
|
||||
newNode.property = this.jsx_parseIdentifier();
|
||||
node = this.finishNode(newNode, "JSXMemberExpression");
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
// Parses any type of JSX attribute value.
|
||||
|
||||
pp.jsx_parseAttributeValue = function () {
|
||||
switch (this.type) {
|
||||
case tt.braceL:
|
||||
var node = this.jsx_parseExpressionContainer();
|
||||
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.jsx_parseEmptyExpression = 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.jsx_parseExpressionContainer = function () {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
node.expression = this.type === tt.braceR ? this.jsx_parseEmptyExpression() : this.parseExpression();
|
||||
this.expect(tt.braceR);
|
||||
return this.finishNode(node, "JSXExpressionContainer");
|
||||
};
|
||||
|
||||
// Parses following JSX attribute name-value pair.
|
||||
|
||||
pp.jsx_parseAttribute = 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.jsx_parseNamespacedName();
|
||||
node.value = this.eat(tt.eq) ? this.jsx_parseAttributeValue() : null;
|
||||
return this.finishNode(node, "JSXAttribute");
|
||||
};
|
||||
|
||||
// Parses JSX opening tag starting after '<'.
|
||||
|
||||
pp.jsx_parseOpeningElementAt = function (start) {
|
||||
var node = this.startNodeAt(start);
|
||||
node.attributes = [];
|
||||
node.name = this.jsx_parseElementName();
|
||||
while (this.type !== tt.slash && this.type !== tt.jsxTagEnd) node.attributes.push(this.jsx_parseAttribute());
|
||||
node.selfClosing = this.eat(tt.slash);
|
||||
this.expect(tt.jsxTagEnd);
|
||||
return this.finishNode(node, "JSXOpeningElement");
|
||||
};
|
||||
|
||||
// Parses JSX closing tag starting after '</'.
|
||||
|
||||
pp.jsx_parseClosingElementAt = function (start) {
|
||||
var node = this.startNodeAt(start);
|
||||
node.name = this.jsx_parseElementName();
|
||||
this.expect(tt.jsxTagEnd);
|
||||
return this.finishNode(node, "JSXClosingElement");
|
||||
};
|
||||
|
||||
// Parses entire JSX element, including it's opening tag
|
||||
// (starting after '<'), attributes, contents and closing tag.
|
||||
|
||||
pp.jsx_parseElementAt = function (start) {
|
||||
var node = this.startNodeAt(start);
|
||||
var children = [];
|
||||
var openingElement = this.jsx_parseOpeningElementAt(start);
|
||||
var closingElement = null;
|
||||
|
||||
if (!openingElement.selfClosing) {
|
||||
contents: for (;;) {
|
||||
switch (this.type) {
|
||||
case tt.jsxTagStart:
|
||||
start = this.markPosition();
|
||||
this.next();
|
||||
if (this.eat(tt.slash)) {
|
||||
closingElement = this.jsx_parseClosingElementAt(start);
|
||||
break contents;
|
||||
}
|
||||
children.push(this.jsx_parseElementAt(start));
|
||||
break;
|
||||
|
||||
case tt.jsxText:
|
||||
children.push(this.parseExprAtom());
|
||||
break;
|
||||
|
||||
case tt.braceL:
|
||||
children.push(this.jsx_parseExpressionContainer());
|
||||
break;
|
||||
|
||||
default:
|
||||
this.unexpected();
|
||||
}
|
||||
}
|
||||
if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) this.raise(closingElement.start, "Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">");
|
||||
}
|
||||
|
||||
node.openingElement = openingElement;
|
||||
node.closingElement = closingElement;
|
||||
node.children = children;
|
||||
return this.finishNode(node, "JSXElement");
|
||||
};
|
||||
|
||||
// Parses entire JSX element from current position.
|
||||
|
||||
pp.jsx_parseElement = function () {
|
||||
var start = this.markPosition();
|
||||
this.next();
|
||||
return this.jsx_parseElementAt(start);
|
||||
};
|
||||
|
||||
acorn.plugins.jsx = 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.jsx_parseElement();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.jsx_readToken();
|
||||
|
||||
if (context === tc.j_oTag || context === tc.j_cTag) {
|
||||
if (acorn.isIdentifierStart(code)) return this.jsx_readWord();
|
||||
|
||||
if (code == 62) {
|
||||
++this.pos;
|
||||
return this.finishToken(tt.jsxTagEnd);
|
||||
}
|
||||
|
||||
if ((code === 34 || code === 39) && context == tc.j_oTag) return this.jsx_readString(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);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
668
lib/acorn/src/.subladf.tmp
Normal file
668
lib/acorn/src/.subladf.tmp
Normal file
@@ -0,0 +1,668 @@
|
||||
// A recursive descent parser operates by defining functions for all
|
||||
// syntactic elements, and recursively calling those, each function
|
||||
// advancing the input stream and returning an AST node. Precedence
|
||||
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
|
||||
// instead of `(!x)[1]` is handled by the fact that the parser
|
||||
// function that parses unary prefix operators is called first, and
|
||||
// in turn calls the function that parses `[]` subscripts — that
|
||||
// way, it'll receive the node for `x[1]` already parsed, and wraps
|
||||
// *that* in the unary operator node.
|
||||
//
|
||||
// Acorn uses an [operator precedence parser][opp] to handle binary
|
||||
// operator precedence, because it is much more compact than using
|
||||
// the technique outlined above, which uses different, nesting
|
||||
// functions to specify precedence, for all of the ten binary
|
||||
// precedence levels that JavaScript defines.
|
||||
//
|
||||
// [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"
|
||||
|
||||
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) {
|
||||
if (this.options.ecmaVersion >= 6) return
|
||||
let key = prop.key, name
|
||||
switch (key.type) {
|
||||
case "Identifier": name = key.name; break
|
||||
case "Literal": name = String(key.value); break
|
||||
default: return
|
||||
}
|
||||
let kind = prop.kind || "init", other
|
||||
if (has(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")
|
||||
} else {
|
||||
other = propHash[name] = {
|
||||
init: false,
|
||||
get: false,
|
||||
set: false
|
||||
}
|
||||
}
|
||||
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,
|
||||
// and, *if* the syntactic construct they handle is present, wrap
|
||||
// the AST node that the inner parser gave them in another node.
|
||||
|
||||
// Parse a full expression. The optional arguments are used to
|
||||
// forbid the `in` operator (in for loops initalization expressions)
|
||||
// and provide reference for storing '=' operator inside shorthand
|
||||
// property assignment in contexts where both object expression
|
||||
// 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)
|
||||
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")
|
||||
}
|
||||
return expr
|
||||
}
|
||||
|
||||
// Parse an assignment expression. This includes applications of
|
||||
// operators like `+=`.
|
||||
|
||||
pp.parseMaybeAssign = function(noIn, refShorthandDefaultPos) {
|
||||
if (this.type == tt._yield && this.inGenerator) return this.parseYield()
|
||||
|
||||
let failOnShorthandAssign
|
||||
if (!refShorthandDefaultPos) {
|
||||
refShorthandDefaultPos = {start: 0}
|
||||
failOnShorthandAssign = true
|
||||
} else {
|
||||
failOnShorthandAssign = false
|
||||
}
|
||||
let start = this.markPosition()
|
||||
let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos)
|
||||
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)
|
||||
this.next()
|
||||
node.right = this.parseMaybeAssign(noIn)
|
||||
return this.finishNode(node, "AssignmentExpression")
|
||||
} else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
|
||||
this.unexpected(refShorthandDefaultPos.start)
|
||||
}
|
||||
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
|
||||
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")
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// Parse binary operators with the operator precedence parsing
|
||||
// algorithm. `left` is the left-hand side of the operator.
|
||||
// `minPrec` provides context that allows the function to stop and
|
||||
// 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
|
||||
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)
|
||||
}
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
// Parse unary operators, both prefix and postfix.
|
||||
|
||||
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)
|
||||
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")
|
||||
}
|
||||
let start = this.markPosition()
|
||||
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")
|
||||
}
|
||||
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.parseSubscripts = function(base, start, noCalls) {
|
||||
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, false)
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
switch (this.type) {
|
||||
case tt._this:
|
||||
case tt._super:
|
||||
let type = this.type === tt._this ? "ThisExpression" : "Super"
|
||||
node = this.startNode()
|
||||
this.next()
|
||||
return this.finishNode(node, type)
|
||||
|
||||
case tt._yield:
|
||||
if (this.inGenerator) unexpected()
|
||||
|
||||
case tt.name:
|
||||
let start = this.markPosition()
|
||||
let id = this.parseIdent(this.type !== tt.name)
|
||||
if (!this.canInsertSemicolon() && this.eat(tt.arrow)) {
|
||||
return this.parseArrowExpression(this.startNodeAt(start), [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
|
||||
|
||||
case tt.num: case tt.string:
|
||||
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")
|
||||
|
||||
case tt.parenL:
|
||||
return this.parseParenAndDistinguishExpression()
|
||||
|
||||
case tt.bracketL:
|
||||
node = this.startNode()
|
||||
this.next()
|
||||
// check whether this is array comprehension or regular array
|
||||
if ((this.options.ecmaVersion >= 7 || this.option.features["es7.comprehensions"]) && this.type === tt._for) {
|
||||
return this.parseComprehension(node, false)
|
||||
}
|
||||
node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos)
|
||||
return this.finishNode(node, "ArrayExpression")
|
||||
|
||||
case tt.braceL:
|
||||
return this.parseObj(false, refShorthandDefaultPos)
|
||||
|
||||
case tt._function:
|
||||
node = this.startNode()
|
||||
this.next()
|
||||
return this.parseFunction(node, false)
|
||||
|
||||
case tt._class:
|
||||
return this.parseClass(this.startNode(), false)
|
||||
|
||||
case tt._new:
|
||||
return this.parseNew()
|
||||
|
||||
case tt.backQuote:
|
||||
return this.parseTemplate()
|
||||
|
||||
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) {
|
||||
start = start || this.markPosition()
|
||||
var 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)
|
||||
}
|
||||
|
||||
var innerStart = this.markPosition(), exprList = [], first = true
|
||||
var refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart
|
||||
while (this.type !== tt.parenR) {
|
||||
first ? first = false : this.expect(tt.comma)
|
||||
if (this.type === tt.ellipsis) {
|
||||
var spreadNodeStart = this.markPosition()
|
||||
spreadStart = this.start
|
||||
exprList.push(this.parseParenItem(this.parseRest(), spreadNodeStart))
|
||||
break
|
||||
} else {
|
||||
if (this.type === tt.parenL && !innerParenStart) {
|
||||
innerParenStart = this.start
|
||||
}
|
||||
exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem))
|
||||
}
|
||||
}
|
||||
var innerEnd = this.markPosition()
|
||||
this.expect(tt.parenR)
|
||||
|
||||
if (!this.canInsertSemicolon() && this.eat(tt.arrow)) {
|
||||
if (innerParenStart) this.unexpected(innerParenStart)
|
||||
return this.parseParenArrowList(start, exprList, isAsync)
|
||||
}
|
||||
|
||||
if (!exprList.length) this.unexpected(this.lastTokStart)
|
||||
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)
|
||||
} else {
|
||||
val = exprList[0]
|
||||
}
|
||||
} else {
|
||||
val = this.parseParenExpression()
|
||||
}
|
||||
|
||||
if (this.options.preserveParens) {
|
||||
var par = this.startNodeAt(start)
|
||||
par.expression = val
|
||||
return this.finishNode(par, "ParenthesizedExpression")
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
pp.parseParenArrowList = function (start, exprList, isAsync) {
|
||||
return this.parseArrowExpression(this.startNodeAt(start), exprList, isAsync)
|
||||
}
|
||||
|
||||
pp.parseParenItem = function (node, start) {
|
||||
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)
|
||||
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")
|
||||
}
|
||||
let start = this.markPosition()
|
||||
node.callee = this.parseSubscripts(this.parseExprAtom(), start, true)
|
||||
if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false)
|
||||
else node.arguments = empty
|
||||
return this.finishNode(node, "NewExpression")
|
||||
}
|
||||
|
||||
// Parse template expression.
|
||||
|
||||
pp.parseTemplateElement = function() {
|
||||
let elem = this.startNode()
|
||||
elem.value = {
|
||||
raw: this.input.slice(this.start, this.end),
|
||||
cooked: this.value
|
||||
}
|
||||
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]
|
||||
while (!curElt.tail) {
|
||||
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")
|
||||
}
|
||||
|
||||
// Parse an object literal or binding pattern.
|
||||
|
||||
pp.parseObj = function(isPattern, refShorthandDefaultPos) {
|
||||
let node = this.startNode(), first = true, propHash = {}
|
||||
node.properties = []
|
||||
this.next()
|
||||
while (!this.eat(tt.braceR)) {
|
||||
if (!first) {
|
||||
this.expect(tt.comma)
|
||||
if (this.afterTrailingComma(tt.braceR)) break
|
||||
} else first = false
|
||||
|
||||
let prop = this.startNode(), isGenerator, start
|
||||
if (this.options.ecmaVersion >= 6) {
|
||||
prop.method = false
|
||||
prop.shorthand = false
|
||||
if (isPattern || refShorthandDefaultPos)
|
||||
start = this.markPosition()
|
||||
if (!isPattern)
|
||||
isGenerator = this.eat(tt.star)
|
||||
}
|
||||
this.parsePropertyName(prop)
|
||||
if (this.eat(tt.colon)) {
|
||||
prop.value = isPattern ? this.parseMaybeDefault() : 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)
|
||||
} 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 || isPattern) this.unexpected()
|
||||
prop.kind = prop.key.name
|
||||
this.parsePropertyName(prop)
|
||||
prop.value = this.parseMethod(false)
|
||||
} else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
|
||||
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)
|
||||
} else if (this.type === tt.eq && refShorthandDefaultPos) {
|
||||
if (!refShorthandDefaultPos.start)
|
||||
refShorthandDefaultPos.start = this.start
|
||||
prop.value = this.parseMaybeDefault(start, prop.key)
|
||||
} else {
|
||||
prop.value = prop.key
|
||||
}
|
||||
prop.shorthand = true
|
||||
} else this.unexpected()
|
||||
|
||||
this.checkPropClash(prop, propHash)
|
||||
node.properties.push(this.finishNode(prop, "Property"))
|
||||
}
|
||||
return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
prop.computed = false
|
||||
}
|
||||
}
|
||||
prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true)
|
||||
}
|
||||
|
||||
// Initialize empty function node.
|
||||
|
||||
pp.initFunction = function(node) {
|
||||
node.id = null
|
||||
if (this.options.ecmaVersion >= 6) {
|
||||
node.generator = false
|
||||
node.expression = false
|
||||
}
|
||||
}
|
||||
|
||||
// Parse object or class method.
|
||||
|
||||
pp.parseMethod = function(isGenerator) {
|
||||
let node = this.startNode()
|
||||
this.initFunction(node)
|
||||
this.expect(tt.parenL)
|
||||
node.params = this.parseBindingList(tt.parenR, false, false)
|
||||
let allowExpressionBody
|
||||
if (this.options.ecmaVersion >= 6) {
|
||||
node.generator = isGenerator
|
||||
allowExpressionBody = true
|
||||
} else {
|
||||
allowExpressionBody = false
|
||||
}
|
||||
this.parseFunctionBody(node, allowExpressionBody)
|
||||
return this.finishNode(node, "FunctionExpression")
|
||||
}
|
||||
|
||||
// Parse arrow function expression with given parameters.
|
||||
|
||||
pp.parseArrowExpression = function(node, params) {
|
||||
this.initFunction(node)
|
||||
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
|
||||
|
||||
if (isExpression) {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
if (node.id)
|
||||
this.checkLVal(node.id, true)
|
||||
for (let i = 0; i < node.params.length; i++)
|
||||
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
|
||||
// `allowEmpty` can be turned on to allow subsequent commas with
|
||||
// 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
|
||||
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)
|
||||
} else {
|
||||
if (this.type === tt.ellipsis)
|
||||
elts.push(this.parseSpread(refShorthandDefaultPos))
|
||||
else
|
||||
elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos))
|
||||
}
|
||||
}
|
||||
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
|
||||
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
|
||||
} else if (liberal && this.type.keyword) {
|
||||
node.name = this.type.keyword
|
||||
} else {
|
||||
this.unexpected()
|
||||
}
|
||||
this.next()
|
||||
return this.finishNode(node, "Identifier")
|
||||
}
|
||||
|
||||
// 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
|
||||
} else {
|
||||
node.delegate = this.eat(tt.star)
|
||||
node.argument = this.parseMaybeAssign()
|
||||
}
|
||||
return this.finishNode(node, "YieldExpression")
|
||||
}
|
||||
|
||||
// Parses array and generator comprehensions.
|
||||
|
||||
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"))
|
||||
}
|
||||
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")
|
||||
}
|
||||
744
lib/acorn/src/expression.js
Normal file
744
lib/acorn/src/expression.js
Normal file
@@ -0,0 +1,744 @@
|
||||
// A recursive descent parser operates by defining functions for all
|
||||
// syntactic elements, and recursively calling those, each function
|
||||
// advancing the input stream and returning an AST node. Precedence
|
||||
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
|
||||
// instead of `(!x)[1]` is handled by the fact that the parser
|
||||
// function that parses unary prefix operators is called first, and
|
||||
// in turn calls the function that parses `[]` subscripts — that
|
||||
// way, it'll receive the node for `x[1]` already parsed, and wraps
|
||||
// *that* in the unary operator node.
|
||||
//
|
||||
// Acorn uses an [operator precedence parser][opp] to handle binary
|
||||
// operator precedence, because it is much more compact than using
|
||||
// the technique outlined above, which uses different, nesting
|
||||
// functions to specify precedence, for all of the ten binary
|
||||
// precedence levels that JavaScript defines.
|
||||
//
|
||||
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
|
||||
|
||||
"use strict";
|
||||
|
||||
var tt = require("./tokentype").types;
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var reservedWords = require("./identifier").reservedWords;
|
||||
|
||||
var has = require("./util").has;
|
||||
|
||||
var 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) {
|
||||
if (this.options.ecmaVersion >= 6) return;
|
||||
var key = prop.key,
|
||||
name = undefined;
|
||||
switch (key.type) {
|
||||
case "Identifier":
|
||||
name = key.name;break;
|
||||
case "Literal":
|
||||
name = String(key.value);break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
var kind = prop.kind || "init",
|
||||
other = undefined;
|
||||
if (has(propHash, name)) {
|
||||
other = propHash[name];
|
||||
var isGetSet = kind !== "init";
|
||||
if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init)) this.raise(key.start, "Redefinition of property");
|
||||
} else {
|
||||
other = propHash[name] = {
|
||||
init: false,
|
||||
get: false,
|
||||
set: false
|
||||
};
|
||||
}
|
||||
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,
|
||||
// and, *if* the syntactic construct they handle is present, wrap
|
||||
// the AST node that the inner parser gave them in another node.
|
||||
|
||||
// Parse a full expression. The optional arguments are used to
|
||||
// forbid the `in` operator (in for loops initalization expressions)
|
||||
// and provide reference for storing '=' operator inside shorthand
|
||||
// property assignment in contexts where both object expression
|
||||
// and object pattern might appear (so it's possible to raise
|
||||
// delayed syntax error at correct position).
|
||||
|
||||
pp.parseExpression = function (noIn, refShorthandDefaultPos) {
|
||||
var start = this.markPosition();
|
||||
var expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos);
|
||||
if (this.type === tt.comma) {
|
||||
var node = this.startNodeAt(start);
|
||||
node.expressions = [expr];
|
||||
while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos));
|
||||
return this.finishNode(node, "SequenceExpression");
|
||||
}
|
||||
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();
|
||||
|
||||
var failOnShorthandAssign = undefined;
|
||||
if (!refShorthandDefaultPos) {
|
||||
refShorthandDefaultPos = { start: 0 };
|
||||
failOnShorthandAssign = true;
|
||||
} else {
|
||||
failOnShorthandAssign = false;
|
||||
}
|
||||
var start = this.markPosition();
|
||||
var left = this.parseMaybeConditional(noIn, refShorthandDefaultPos);
|
||||
if (afterLeftParse) left = afterLeftParse.call(this, left, start);
|
||||
if (this.type.isAssign) {
|
||||
var 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);
|
||||
this.next();
|
||||
node.right = this.parseMaybeAssign(noIn);
|
||||
return this.finishNode(node, "AssignmentExpression");
|
||||
} else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
|
||||
this.unexpected(refShorthandDefaultPos.start);
|
||||
}
|
||||
return left;
|
||||
};
|
||||
|
||||
// Parse a ternary conditional (`?:`) operator.
|
||||
|
||||
pp.parseMaybeConditional = function (noIn, refShorthandDefaultPos) {
|
||||
var start = this.markPosition();
|
||||
var expr = this.parseExprOps(noIn, refShorthandDefaultPos);
|
||||
if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
|
||||
if (this.eat(tt.question)) {
|
||||
var 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");
|
||||
}
|
||||
return expr;
|
||||
};
|
||||
|
||||
// Start the precedence parser.
|
||||
|
||||
pp.parseExprOps = function (noIn, refShorthandDefaultPos) {
|
||||
var start = this.markPosition();
|
||||
var expr = this.parseMaybeUnary(refShorthandDefaultPos);
|
||||
if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
|
||||
return this.parseExprOp(expr, start, -1, noIn);
|
||||
};
|
||||
|
||||
// Parse binary operators with the operator precedence parsing
|
||||
// algorithm. `left` is the left-hand side of the operator.
|
||||
// `minPrec` provides context that allows the function to stop and
|
||||
// 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) {
|
||||
var prec = this.type.binop;
|
||||
if (prec != null && (!noIn || this.type !== tt._in)) {
|
||||
if (prec > minPrec) {
|
||||
var node = this.startNodeAt(leftStart);
|
||||
node.left = left;
|
||||
node.operator = this.value;
|
||||
var op = this.type;
|
||||
this.next();
|
||||
var 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);
|
||||
}
|
||||
}
|
||||
return left;
|
||||
};
|
||||
|
||||
// Parse unary operators, both prefix and postfix.
|
||||
|
||||
pp.parseMaybeUnary = function (refShorthandDefaultPos) {
|
||||
if (this.type.prefix) {
|
||||
var 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");
|
||||
}
|
||||
var start = this.markPosition();
|
||||
var expr = this.parseExprSubscripts(refShorthandDefaultPos);
|
||||
if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
|
||||
while (this.type.postfix && !this.canInsertSemicolon()) {
|
||||
var node = this.startNodeAt(start);
|
||||
node.operator = this.value;
|
||||
node.prefix = false;
|
||||
node.argument = expr;
|
||||
this.checkLVal(expr);
|
||||
this.next();
|
||||
expr = this.finishNode(node, "UpdateExpression");
|
||||
}
|
||||
return expr;
|
||||
};
|
||||
|
||||
// Parse call, dot, and `[]`-subscript expressions.
|
||||
|
||||
pp.parseExprSubscripts = function (refShorthandDefaultPos) {
|
||||
var start = this.markPosition();
|
||||
var expr = this.parseExprAtom(refShorthandDefaultPos);
|
||||
if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
|
||||
return this.parseSubscripts(expr, start);
|
||||
};
|
||||
|
||||
pp.parseSubscripts = function (base, start, noCalls) {
|
||||
if (this.eat(tt.dot)) {
|
||||
var 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)) {
|
||||
var 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)) {
|
||||
var node = this.startNodeAt(start);
|
||||
node.callee = base;
|
||||
node.arguments = this.parseExprList(tt.parenR, false);
|
||||
return this.parseSubscripts(this.finishNode(node, "CallExpression"), start, noCalls);
|
||||
} else if (this.type === tt.backQuote) {
|
||||
var node = this.startNodeAt(start);
|
||||
node.tag = base;
|
||||
node.quasi = this.parseTemplate();
|
||||
return this.parseSubscripts(this.finishNode(node, "TaggedTemplateExpression"), start, noCalls);
|
||||
}return base;
|
||||
};
|
||||
|
||||
// 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) {
|
||||
var node = undefined;
|
||||
switch (this.type) {
|
||||
case tt._this:
|
||||
case tt._super:
|
||||
var type = this.type === tt._this ? "ThisExpression" : "Super";
|
||||
node = this.startNode();
|
||||
this.next();
|
||||
return this.finishNode(node, type);
|
||||
|
||||
case tt._yield:
|
||||
if (this.inGenerator) unexpected();
|
||||
|
||||
case tt.name:
|
||||
var start = this.markPosition();
|
||||
node = this.startNode();
|
||||
var id = this.parseIdent(this.type !== tt.name);
|
||||
|
||||
//
|
||||
if (this.options.features["es7.asyncFunctions"]) {
|
||||
// async functions!
|
||||
if (id.name === "async") {
|
||||
// arrow functions
|
||||
if (this.type === tt.parenL) {
|
||||
var expr = this.parseParenAndDistinguishExpression(start, true);
|
||||
if (expr.type === "ArrowFunctionExpression") {
|
||||
return expr;
|
||||
} else {
|
||||
node.callee = id;
|
||||
if (expr.type === "SequenceExpression") {
|
||||
node.arguments = expr.expressions;
|
||||
} else {
|
||||
node.arguments = [expr];
|
||||
}
|
||||
return this.parseSubscripts(this.finishNode(node, "CallExpression"), start);
|
||||
}
|
||||
} else if (this.type === tt.name) {
|
||||
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);
|
||||
}
|
||||
} else if (id.name === "await") {
|
||||
if (this.inAsync) return this.parseAwait(node);
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
if (!this.canInsertSemicolon() && this.eat(tt.arrow)) {
|
||||
return this.parseArrowExpression(this.startNodeAt(start), [id]);
|
||||
}
|
||||
return id;
|
||||
|
||||
case tt.regexp:
|
||||
var 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);
|
||||
|
||||
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");
|
||||
|
||||
case tt.parenL:
|
||||
return this.parseParenAndDistinguishExpression();
|
||||
|
||||
case tt.bracketL:
|
||||
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);
|
||||
}
|
||||
node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos);
|
||||
return this.finishNode(node, "ArrayExpression");
|
||||
|
||||
case tt.braceL:
|
||||
return this.parseObj(false, refShorthandDefaultPos);
|
||||
|
||||
case tt._function:
|
||||
node = this.startNode();
|
||||
this.next();
|
||||
return this.parseFunction(node, false);
|
||||
|
||||
case tt._class:
|
||||
return this.parseClass(this.startNode(), false);
|
||||
|
||||
case tt._new:
|
||||
return this.parseNew();
|
||||
|
||||
case tt.backQuote:
|
||||
return this.parseTemplate();
|
||||
|
||||
default:
|
||||
this.unexpected();
|
||||
}
|
||||
};
|
||||
|
||||
pp.parseLiteral = function (value) {
|
||||
var 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);
|
||||
var val = this.parseExpression();
|
||||
this.expect(tt.parenR);
|
||||
return val;
|
||||
};
|
||||
|
||||
pp.parseParenAndDistinguishExpression = function (start, isAsync) {
|
||||
start = start || this.markPosition();
|
||||
var val = undefined;
|
||||
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);
|
||||
}
|
||||
|
||||
var innerStart = this.markPosition(),
|
||||
exprList = [],
|
||||
first = true;
|
||||
var refShorthandDefaultPos = { start: 0 },
|
||||
spreadStart = undefined,
|
||||
innerParenStart = undefined;
|
||||
while (this.type !== tt.parenR) {
|
||||
first ? first = false : this.expect(tt.comma);
|
||||
if (this.type === tt.ellipsis) {
|
||||
var spreadNodeStart = this.markPosition();
|
||||
spreadStart = this.start;
|
||||
exprList.push(this.parseParenItem(this.parseRest(), spreadNodeStart));
|
||||
break;
|
||||
} else {
|
||||
if (this.type === tt.parenL && !innerParenStart) {
|
||||
innerParenStart = this.start;
|
||||
}
|
||||
exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem));
|
||||
}
|
||||
}
|
||||
var innerEnd = this.markPosition();
|
||||
this.expect(tt.parenR);
|
||||
|
||||
if (!this.canInsertSemicolon() && this.eat(tt.arrow)) {
|
||||
if (innerParenStart) this.unexpected(innerParenStart);
|
||||
return this.parseParenArrowList(start, exprList, isAsync);
|
||||
}
|
||||
|
||||
if (!exprList.length) this.unexpected(this.lastTokStart);
|
||||
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);
|
||||
} else {
|
||||
val = exprList[0];
|
||||
}
|
||||
} else {
|
||||
val = this.parseParenExpression();
|
||||
}
|
||||
|
||||
if (this.options.preserveParens) {
|
||||
var par = this.startNodeAt(start);
|
||||
par.expression = val;
|
||||
return this.finishNode(par, "ParenthesizedExpression");
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
pp.parseParenArrowList = function (start, exprList, isAsync) {
|
||||
return this.parseArrowExpression(this.startNodeAt(start), exprList, isAsync);
|
||||
};
|
||||
|
||||
pp.parseParenItem = function (node, start) {
|
||||
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
|
||||
|
||||
var empty = [];
|
||||
|
||||
pp.parseNew = function () {
|
||||
var node = this.startNode();
|
||||
var 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");
|
||||
}
|
||||
var start = this.markPosition();
|
||||
node.callee = this.parseSubscripts(this.parseExprAtom(), start, true);
|
||||
if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false);else node.arguments = empty;
|
||||
return this.finishNode(node, "NewExpression");
|
||||
};
|
||||
|
||||
// Parse template expression.
|
||||
|
||||
pp.parseTemplateElement = function () {
|
||||
var elem = this.startNode();
|
||||
elem.value = {
|
||||
raw: this.input.slice(this.start, this.end),
|
||||
cooked: this.value
|
||||
};
|
||||
this.next();
|
||||
elem.tail = this.type === tt.backQuote;
|
||||
return this.finishNode(elem, "TemplateElement");
|
||||
};
|
||||
|
||||
pp.parseTemplate = function () {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
node.expressions = [];
|
||||
var 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.next();
|
||||
return this.finishNode(node, "TemplateLiteral");
|
||||
};
|
||||
|
||||
// Parse an object literal or binding pattern.
|
||||
|
||||
pp.parseObj = function (isPattern, refShorthandDefaultPos) {
|
||||
var node = this.startNode(),
|
||||
first = true,
|
||||
propHash = {};
|
||||
node.properties = [];
|
||||
this.next();
|
||||
while (!this.eat(tt.braceR)) {
|
||||
if (!first) {
|
||||
this.expect(tt.comma);
|
||||
if (this.afterTrailingComma(tt.braceR)) break;
|
||||
} else first = false;
|
||||
|
||||
var prop = this.startNode(),
|
||||
isGenerator = false,
|
||||
isAsync = false,
|
||||
start = undefined;
|
||||
if (this.options.features["es7.objectRestSpread"] && this.type === tt.ellipsis) {
|
||||
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();
|
||||
if (!isPattern) isGenerator = this.eat(tt.star);
|
||||
}
|
||||
if (this.options.features["es7.asyncFunctions"] && this.isContextual("async")) {
|
||||
if (isGenerator || isPattern) this.unexpected();
|
||||
var asyncId = this.parseIdent();
|
||||
if (this.type === tt.colon || this.type === tt.parenL) {
|
||||
prop.key = asyncId;
|
||||
} else {
|
||||
isAsync = true;
|
||||
this.parsePropertyName(prop);
|
||||
}
|
||||
} else {
|
||||
this.parsePropertyName(prop);
|
||||
}
|
||||
this.parseObjPropValue(prop, start, isGenerator, isAsync, isPattern, refShorthandDefaultPos);
|
||||
this.checkPropClash(prop, propHash);
|
||||
node.properties.push(this.finishNode(prop, "Property"));
|
||||
}
|
||||
return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
|
||||
};
|
||||
|
||||
pp.parseObjPropValue = function (prop, start, isGenerator, isAsync, isPattern, refShorthandDefaultPos) {
|
||||
if (this.eat(tt.colon)) {
|
||||
prop.value = isPattern ? this.parseMaybeDefault() : 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);
|
||||
} 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);
|
||||
} else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
|
||||
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);
|
||||
} else if (this.type === tt.eq && refShorthandDefaultPos) {
|
||||
if (!refShorthandDefaultPos.start) refShorthandDefaultPos.start = this.start;
|
||||
prop.value = this.parseMaybeDefault(start, prop.key);
|
||||
} else {
|
||||
prop.value = prop.key;
|
||||
}
|
||||
prop.shorthand = true;
|
||||
} else this.unexpected();
|
||||
};
|
||||
|
||||
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;
|
||||
} else {
|
||||
prop.computed = false;
|
||||
}
|
||||
}
|
||||
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;
|
||||
if (this.options.ecmaVersion >= 6) {
|
||||
node.generator = false;
|
||||
node.expression = false;
|
||||
}
|
||||
if (this.options.features["es7.asyncFunctions"]) {
|
||||
node.async = !!isAsync;
|
||||
}
|
||||
};
|
||||
|
||||
// Parse object or class method.
|
||||
|
||||
pp.parseMethod = function (isGenerator, isAsync) {
|
||||
var node = this.startNode();
|
||||
this.initFunction(node, isAsync);
|
||||
this.expect(tt.parenL);
|
||||
node.params = this.parseBindingList(tt.parenR, false, false);
|
||||
var allowExpressionBody = undefined;
|
||||
if (this.options.ecmaVersion >= 6) {
|
||||
node.generator = isGenerator;
|
||||
allowExpressionBody = true;
|
||||
} else {
|
||||
allowExpressionBody = false;
|
||||
}
|
||||
this.parseFunctionBody(node, allowExpressionBody);
|
||||
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");
|
||||
};
|
||||
|
||||
// Parse function body and check parameters.
|
||||
|
||||
pp.parseFunctionBody = function (node, allowExpression) {
|
||||
var isExpression = allowExpression && this.type !== tt.braceL;
|
||||
|
||||
var oldInAsync = this.inAsync;
|
||||
this.inAsync = node.async;
|
||||
if (isExpression) {
|
||||
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).
|
||||
var 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;
|
||||
|
||||
// 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])) {
|
||||
var nameHash = {},
|
||||
oldStrict = this.strict;
|
||||
this.strict = true;
|
||||
if (node.id) this.checkLVal(node.id, true);
|
||||
for (var i = 0; i < node.params.length; i++) {
|
||||
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
|
||||
// `allowEmpty` can be turned on to allow subsequent commas with
|
||||
// nothing in between them to be parsed as `null` (which is needed
|
||||
// for array literals).
|
||||
|
||||
pp.parseExprList = function (close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {
|
||||
var 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);
|
||||
} else {
|
||||
if (this.type === tt.ellipsis) elts.push(this.parseSpread(refShorthandDefaultPos));else elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos));
|
||||
}
|
||||
}
|
||||
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) {
|
||||
var 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;
|
||||
} else if (liberal && this.type.keyword) {
|
||||
node.name = this.type.keyword;
|
||||
} else {
|
||||
this.unexpected();
|
||||
}
|
||||
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();
|
||||
}
|
||||
node.all = this.eat(tt.star);
|
||||
node.argument = this.parseMaybeAssign(true);
|
||||
return this.finishNode(node, "AwaitExpression");
|
||||
};
|
||||
|
||||
// Parses yield expression inside generator.
|
||||
|
||||
pp.parseYield = function () {
|
||||
var 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();
|
||||
}
|
||||
return this.finishNode(node, "YieldExpression");
|
||||
};
|
||||
|
||||
// Parses array and generator comprehensions.
|
||||
|
||||
pp.parseComprehension = function (node, isGenerator) {
|
||||
node.blocks = [];
|
||||
while (this.type === tt._for) {
|
||||
var 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");
|
||||
};
|
||||
143
lib/acorn/src/identifier.js
Normal file
143
lib/acorn/src/identifier.js
Normal file
@@ -0,0 +1,143 @@
|
||||
|
||||
|
||||
// Test whether a given character code starts an identifier.
|
||||
|
||||
"use strict";
|
||||
|
||||
exports.isIdentifierStart = isIdentifierStart;
|
||||
|
||||
// Test whether a given character is part of an identifier.
|
||||
|
||||
exports.isIdentifierChar = isIdentifierChar;
|
||||
exports.__esModule = true;
|
||||
// This is a trick taken from Esprima. It turns out that, on
|
||||
// non-Chrome browsers, to check whether a string is in a set, a
|
||||
// predicate containing a big ugly `switch` statement is faster than
|
||||
// a regular expression, and on Chrome the two are about on par.
|
||||
// This function uses `eval` (non-lexical) to produce such a
|
||||
// predicate from a space-separated string of words.
|
||||
//
|
||||
// It starts by sorting the words by length.
|
||||
|
||||
function makePredicate(words) {
|
||||
words = words.split(" ");
|
||||
var f = "",
|
||||
cats = [];
|
||||
out: for (var i = 0; i < words.length; ++i) {
|
||||
for (var j = 0; j < cats.length; ++j) {
|
||||
if (cats[j][0].length == words[i].length) {
|
||||
cats[j].push(words[i]);
|
||||
continue out;
|
||||
}
|
||||
}cats.push([words[i]]);
|
||||
}
|
||||
function compareTo(arr) {
|
||||
if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
|
||||
f += "switch(str){";
|
||||
for (var i = 0; i < arr.length; ++i) {
|
||||
f += "case " + JSON.stringify(arr[i]) + ":";
|
||||
}f += "return true}return false;";
|
||||
}
|
||||
|
||||
// When there are more than three length categories, an outer
|
||||
// switch first dispatches on the lengths, to save on comparisons.
|
||||
|
||||
if (cats.length > 3) {
|
||||
cats.sort(function (a, b) {
|
||||
return b.length - a.length;
|
||||
});
|
||||
f += "switch(str.length){";
|
||||
for (var i = 0; i < cats.length; ++i) {
|
||||
var cat = cats[i];
|
||||
f += "case " + cat[0].length + ":";
|
||||
compareTo(cat);
|
||||
}
|
||||
f += "}"
|
||||
|
||||
// Otherwise, simply generate a flat `switch` statement.
|
||||
|
||||
;
|
||||
} else {
|
||||
compareTo(words);
|
||||
}
|
||||
return new Function("str", f);
|
||||
}
|
||||
|
||||
// Reserved word lists for various dialects of the language
|
||||
|
||||
var reservedWords = {
|
||||
3: makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),
|
||||
5: makePredicate("class enum extends super const export import"),
|
||||
6: makePredicate("enum await"),
|
||||
strict: makePredicate("implements interface let package private protected public static yield"),
|
||||
strictBind: makePredicate("eval arguments")
|
||||
};
|
||||
|
||||
exports.reservedWords = reservedWords;
|
||||
// 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 keywords = {
|
||||
5: makePredicate(ecma5AndLessKeywords),
|
||||
6: makePredicate(ecma5AndLessKeywords + " let const class extends export import yield super")
|
||||
};
|
||||
|
||||
exports.keywords = keywords;
|
||||
// ## Character categories
|
||||
|
||||
// Big ugly regular expressions that match characters in the
|
||||
// whitespace, identifier, and identifier-start categories. These
|
||||
// are only applied when a character is found to actually have a
|
||||
// code point above 128.
|
||||
// Generated by `tools/generate-identifier-regex.js`.
|
||||
|
||||
var nonASCIIidentifierStartChars = "ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙա-ևא-תװ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠ-ࢲऄ-हऽॐक़-ॡॱ-ঀঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ々-〇〡-〩〱-〵〸-〼ぁ-ゖ゛-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚝꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞭꞰꞱꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꩾ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭟꭤꭥꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ";
|
||||
var nonASCIIidentifierChars = "·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-٩ٰۖ-ۜ۟-۪ۤۧۨ-ۭ۰-۹ܑܰ-݊ަ-ް߀-߉߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣤ-ःऺ-़ा-ॏ॑-ॗॢॣ०-९ঁ-ঃ়া-ৄেৈো-্ৗৢৣ০-৯ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑ੦-ੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣ૦-૯ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣ୦-୯ஂா-ூெ-ைொ-்ௗ௦-௯ఀ-ఃా-ౄె-ైొ-్ౕౖౢౣ౦-౯ಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣ೦-೯ഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣ൦-൯ංඃ්ා-ුූෘ-ෟ෦-෯ෲෳัิ-ฺ็-๎๐-๙ັິ-ູົຼ່-ໍ໐-໙༘༙༠-༩༹༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှ၀-၉ၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏ-ႝ፝-፟፩-፱ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝០-៩᠋-᠍᠐-᠙ᢩᤠ-ᤫᤰ-᤻᥆-᥏ᦰ-ᧀᧈᧉ᧐-᧚ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼-᪉᪐-᪙᪰-᪽ᬀ-ᬄ᬴-᭄᭐-᭙᭫-᭳ᮀ-ᮂᮡ-ᮭ᮰-᮹᯦-᯳ᰤ-᰷᱀-᱉᱐-᱙᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷼-᷿‿⁀⁔⃐-⃥⃜⃡-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꘠-꘩꙯ꙴ-꙽ꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-꣄꣐-꣙꣠-꣱꤀-꤉ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀꧐-꧙ꧥ꧰-꧹ꨩ-ꨶꩃꩌꩍ꩐-꩙ꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭꯰-꯹ﬞ︀-️︠-︭︳︴﹍-﹏0-9_";
|
||||
|
||||
var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
|
||||
var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
|
||||
|
||||
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];
|
||||
|
||||
// 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) {
|
||||
var pos = 65536;
|
||||
for (var i = 0; i < set.length; i += 2) {
|
||||
pos += set[i];
|
||||
if (pos > code) return false;
|
||||
pos += set[i + 1];
|
||||
if (pos >= code) return true;
|
||||
}
|
||||
}
|
||||
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 <= 65535) return code >= 170 && nonASCIIidentifierStart.test(String.fromCharCode(code));
|
||||
if (astral === false) return false;
|
||||
return isInAstralSet(code, astralIdentifierStartCodes);
|
||||
}
|
||||
|
||||
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 <= 65535) return code >= 170 && nonASCIIidentifier.test(String.fromCharCode(code));
|
||||
if (astral === false) return false;
|
||||
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
|
||||
}
|
||||
124
lib/acorn/src/index.js
Normal file
124
lib/acorn/src/index.js
Normal file
@@ -0,0 +1,124 @@
|
||||
|
||||
|
||||
// The main exported interface (under `self.acorn` when in the
|
||||
// browser) is a `parse` function that takes a code string and
|
||||
// returns an abstract syntax tree as specified by [Mozilla parser
|
||||
// API][api].
|
||||
//
|
||||
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
|
||||
|
||||
"use strict";
|
||||
|
||||
exports.parse = parse;
|
||||
|
||||
// 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.
|
||||
|
||||
exports.parseExpressionAt = parseExpressionAt;
|
||||
|
||||
// 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.
|
||||
|
||||
exports.tokenizer = tokenizer;
|
||||
exports.__esModule = true;
|
||||
// Acorn is a tiny, fast JavaScript parser written in JavaScript.
|
||||
//
|
||||
// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
|
||||
// various contributors and released under an MIT license.
|
||||
//
|
||||
// Git repositories for Acorn are available at
|
||||
//
|
||||
// http://marijnhaverbeke.nl/git/acorn
|
||||
// https://github.com/marijnh/acorn.git
|
||||
//
|
||||
// Please use the [github bug tracker][ghbt] to report issues.
|
||||
//
|
||||
// [ghbt]: https://github.com/marijnh/acorn/issues
|
||||
//
|
||||
// This file defines the main parser interface. The library also comes
|
||||
// with a [error-tolerant parser][dammit] and an
|
||||
// [abstract syntax tree walker][walk], defined in other files.
|
||||
//
|
||||
// [dammit]: acorn_loose.js
|
||||
// [walk]: util/walk.js
|
||||
|
||||
var _state = require("./state");
|
||||
|
||||
var Parser = _state.Parser;
|
||||
|
||||
var _options = require("./options");
|
||||
|
||||
var getOptions = _options.getOptions;
|
||||
|
||||
require("./parseutil");
|
||||
|
||||
require("./statement");
|
||||
|
||||
require("./lval");
|
||||
|
||||
require("./expression");
|
||||
|
||||
require("./lookahead");
|
||||
|
||||
exports.Parser = _state.Parser;
|
||||
exports.plugins = _state.plugins;
|
||||
exports.defaultOptions = _options.defaultOptions;
|
||||
|
||||
var _location = require("./location");
|
||||
|
||||
exports.SourceLocation = _location.SourceLocation;
|
||||
exports.getLineInfo = _location.getLineInfo;
|
||||
exports.Node = require("./node").Node;
|
||||
|
||||
var _tokentype = require("./tokentype");
|
||||
|
||||
exports.TokenType = _tokentype.TokenType;
|
||||
exports.tokTypes = _tokentype.types;
|
||||
|
||||
var _tokencontext = require("./tokencontext");
|
||||
|
||||
exports.TokContext = _tokencontext.TokContext;
|
||||
exports.tokContexts = _tokencontext.types;
|
||||
|
||||
var _identifier = require("./identifier");
|
||||
|
||||
exports.isIdentifierChar = _identifier.isIdentifierChar;
|
||||
exports.isIdentifierStart = _identifier.isIdentifierStart;
|
||||
exports.Token = require("./tokenize").Token;
|
||||
|
||||
var _whitespace = require("./whitespace");
|
||||
|
||||
exports.isNewLine = _whitespace.isNewLine;
|
||||
exports.lineBreak = _whitespace.lineBreak;
|
||||
exports.lineBreakG = _whitespace.lineBreakG;
|
||||
|
||||
require("../plugins/flow");
|
||||
|
||||
require("../plugins/jsx");
|
||||
|
||||
var version = "1.0.0";exports.version = version;
|
||||
|
||||
function parse(input, options) {
|
||||
var p = parser(options, input);
|
||||
var startPos = p.options.locations ? [p.pos, p.curPosition()] : p.pos;
|
||||
p.nextToken();
|
||||
return p.parseTopLevel(p.options.program || p.startNodeAt(startPos));
|
||||
}
|
||||
|
||||
function parseExpressionAt(input, pos, options) {
|
||||
var p = parser(options, input, pos);
|
||||
p.nextToken();
|
||||
return p.parseExpression();
|
||||
}
|
||||
|
||||
function tokenizer(input, options) {
|
||||
return parser(options, input);
|
||||
}
|
||||
|
||||
function parser(options, input) {
|
||||
return new Parser(getOptions(options), String(input));
|
||||
}
|
||||
79
lib/acorn/src/location.js
Normal file
79
lib/acorn/src/location.js
Normal file
@@ -0,0 +1,79 @@
|
||||
"use strict";
|
||||
|
||||
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
|
||||
|
||||
// The `getLineInfo` function is mostly useful when the
|
||||
// `locations` option is off (for performance reasons) and you
|
||||
// want to find the line/column position for a given character
|
||||
// offset. `input` should be the code string that the offset refers
|
||||
// into.
|
||||
|
||||
exports.getLineInfo = getLineInfo;
|
||||
exports.__esModule = true;
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var lineBreakG = require("./whitespace").lineBreakG;
|
||||
|
||||
// These are used when `options.locations` is on, for the
|
||||
// `startLoc` and `endLoc` properties.
|
||||
|
||||
var Position = exports.Position = (function () {
|
||||
function Position(line, col) {
|
||||
_classCallCheck(this, Position);
|
||||
|
||||
this.line = line;
|
||||
this.column = col;
|
||||
}
|
||||
|
||||
Position.prototype.offset = function offset(n) {
|
||||
return new Position(this.line, this.column + n);
|
||||
};
|
||||
|
||||
return Position;
|
||||
})();
|
||||
|
||||
var SourceLocation = exports.SourceLocation = function SourceLocation(p, start, end) {
|
||||
_classCallCheck(this, SourceLocation);
|
||||
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
if (p.sourceFile !== null) this.source = p.sourceFile;
|
||||
};
|
||||
|
||||
function getLineInfo(input, offset) {
|
||||
for (var line = 1, cur = 0;;) {
|
||||
lineBreakG.lastIndex = cur;
|
||||
var match = lineBreakG.exec(input);
|
||||
if (match && match.index < offset) {
|
||||
++line;
|
||||
cur = match.index + match[0].length;
|
||||
} else {
|
||||
return new Position(line, offset - cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pp = Parser.prototype;
|
||||
|
||||
// This function is used to raise exceptions on parse errors. It
|
||||
// takes an offset integer (into the current `input`) to indicate
|
||||
// the location of the error, attaches the position to the end
|
||||
// of the error message, and then raises a `SyntaxError` with that
|
||||
// message.
|
||||
|
||||
pp.raise = function (pos, message) {
|
||||
var loc = getLineInfo(this.input, pos);
|
||||
message += " (" + loc.line + ":" + loc.column + ")";
|
||||
var 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;
|
||||
};
|
||||
24
lib/acorn/src/lookahead.js
Normal file
24
lib/acorn/src/lookahead.js
Normal file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var pp = Parser.prototype;
|
||||
|
||||
var STATE_KEYS = ["lastTokStartLoc", "lastTokEndLoc", "lastTokStart", "lastTokEnd", "lineStart", "startLoc", "endLoc", "start", "pos", "end", "type", "value"];
|
||||
|
||||
pp.getState = function () {
|
||||
var state = {};
|
||||
for (var i = 0; i < STATE_KEYS.length; i++) {
|
||||
var key = STATE_KEYS[i];
|
||||
state[key] = this[key];
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
pp.lookahead = function () {
|
||||
var old = this.getState();
|
||||
this.next();
|
||||
var curr = this.getState();
|
||||
for (var key in old) this[key] = old[key];
|
||||
return curr;
|
||||
};
|
||||
199
lib/acorn/src/lval.js
Normal file
199
lib/acorn/src/lval.js
Normal file
@@ -0,0 +1,199 @@
|
||||
"use strict";
|
||||
|
||||
var tt = require("./tokentype").types;
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var reservedWords = require("./identifier").reservedWords;
|
||||
|
||||
var has = require("./util").has;
|
||||
|
||||
var pp = Parser.prototype;
|
||||
|
||||
// Convert existing expression atom to assignable pattern
|
||||
// if possible.
|
||||
|
||||
pp.toAssignable = function (node, isBinding) {
|
||||
if (this.options.ecmaVersion >= 6 && node) {
|
||||
switch (node.type) {
|
||||
case "Identifier":
|
||||
case "ObjectPattern":
|
||||
case "ArrayPattern":
|
||||
case "AssignmentPattern":
|
||||
break;
|
||||
|
||||
case "ObjectExpression":
|
||||
node.type = "ObjectPattern";
|
||||
for (var i = 0; i < node.properties.length; i++) {
|
||||
var prop = node.properties[i];
|
||||
if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter");
|
||||
this.toAssignable(prop.value, isBinding);
|
||||
}
|
||||
break;
|
||||
|
||||
case "ArrayExpression":
|
||||
node.type = "ArrayPattern";
|
||||
this.toAssignableList(node.elements, isBinding);
|
||||
break;
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (node.operator === "=") {
|
||||
node.type = "AssignmentPattern";
|
||||
} else {
|
||||
this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
|
||||
}
|
||||
break;
|
||||
|
||||
case "MemberExpression":
|
||||
if (!isBinding) break;
|
||||
|
||||
default:
|
||||
this.raise(node.start, "Assigning to rvalue");
|
||||
}
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
// Convert list of expression atoms to binding list.
|
||||
|
||||
pp.toAssignableList = function (exprList, isBinding) {
|
||||
var end = exprList.length;
|
||||
if (end) {
|
||||
var last = exprList[end - 1];
|
||||
if (last && last.type == "RestElement") {
|
||||
--end;
|
||||
} else if (last && last.type == "SpreadElement") {
|
||||
last.type = "RestElement";
|
||||
var arg = last.argument;
|
||||
this.toAssignable(arg, isBinding);
|
||||
if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") this.unexpected(arg.start);
|
||||
--end;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < end; i++) {
|
||||
var elt = exprList[i];
|
||||
if (elt) this.toAssignable(elt, isBinding);
|
||||
}
|
||||
return exprList;
|
||||
};
|
||||
|
||||
// Parses spread element.
|
||||
|
||||
pp.parseSpread = function (refShorthandDefaultPos) {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
node.argument = this.parseMaybeAssign(refShorthandDefaultPos);
|
||||
return this.finishNode(node, "SpreadElement");
|
||||
};
|
||||
|
||||
pp.parseRest = function () {
|
||||
var 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();
|
||||
switch (this.type) {
|
||||
case tt.name:
|
||||
return this.parseIdent();
|
||||
|
||||
case tt.bracketL:
|
||||
var 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);
|
||||
|
||||
default:
|
||||
this.unexpected();
|
||||
}
|
||||
};
|
||||
|
||||
pp.parseBindingList = function (close, allowEmpty, allowTrailingComma) {
|
||||
var elts = [],
|
||||
first = true;
|
||||
while (!this.eat(close)) {
|
||||
if (first) first = false;else this.expect(tt.comma);
|
||||
if (allowEmpty && this.type === tt.comma) {
|
||||
elts.push(null);
|
||||
} else if (allowTrailingComma && this.afterTrailingComma(close)) {
|
||||
break;
|
||||
} else if (this.type === tt.ellipsis) {
|
||||
elts.push(this.parseAssignableListItemTypes(this.parseRest()));
|
||||
this.expect(close);
|
||||
break;
|
||||
} else {
|
||||
elts.push(this.parseAssignableListItemTypes(this.parseMaybeDefault()));
|
||||
}
|
||||
}
|
||||
return elts;
|
||||
};
|
||||
|
||||
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;
|
||||
var node = this.startNodeAt(startPos);
|
||||
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) {
|
||||
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");
|
||||
if (checkClashes) {
|
||||
if (has(checkClashes, expr.name)) this.raise(expr.start, "Argument name clash in strict mode");
|
||||
checkClashes[expr.name] = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case "MemberExpression":
|
||||
if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression");
|
||||
break;
|
||||
|
||||
case "ObjectPattern":
|
||||
for (var i = 0; i < expr.properties.length; i++) {
|
||||
var prop = expr.properties[i];
|
||||
if (prop.type === "Property") prop = prop.value;
|
||||
this.checkLVal(prop, isBinding, checkClashes);
|
||||
}
|
||||
break;
|
||||
|
||||
case "ArrayPattern":
|
||||
for (var i = 0; i < expr.elements.length; i++) {
|
||||
var elem = expr.elements[i];
|
||||
if (elem) this.checkLVal(elem, isBinding, checkClashes);
|
||||
}
|
||||
break;
|
||||
|
||||
case "AssignmentPattern":
|
||||
this.checkLVal(expr.left, isBinding, checkClashes);
|
||||
break;
|
||||
|
||||
case "SpreadProperty":
|
||||
case "RestElement":
|
||||
this.checkLVal(expr.argument, isBinding, checkClashes);
|
||||
break;
|
||||
|
||||
default:
|
||||
this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue");
|
||||
}
|
||||
};
|
||||
61
lib/acorn/src/node.js
Normal file
61
lib/acorn/src/node.js
Normal file
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
|
||||
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var SourceLocation = require("./location").SourceLocation;
|
||||
|
||||
// Start an AST node, attaching a start offset.
|
||||
|
||||
var pp = Parser.prototype;
|
||||
|
||||
var Node = exports.Node = function Node() {
|
||||
_classCallCheck(this, Node);
|
||||
};
|
||||
|
||||
pp.startNode = function () {
|
||||
var 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;
|
||||
};
|
||||
|
||||
pp.startNodeAt = function (pos) {
|
||||
var 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;
|
||||
};
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
// 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;
|
||||
};
|
||||
132
lib/acorn/src/options.js
Normal file
132
lib/acorn/src/options.js
Normal file
@@ -0,0 +1,132 @@
|
||||
|
||||
|
||||
// Interpret and default an options object
|
||||
|
||||
"use strict";
|
||||
|
||||
exports.getOptions = getOptions;
|
||||
exports.__esModule = true;
|
||||
|
||||
var _util = require("./util");
|
||||
|
||||
var has = _util.has;
|
||||
var isArray = _util.isArray;
|
||||
|
||||
var SourceLocation = require("./location").SourceLocation;
|
||||
|
||||
// A second optional argument can be given to further configure
|
||||
// the parser process. These options are recognized:
|
||||
|
||||
var defaultOptions = {
|
||||
// `ecmaVersion` indicates the ECMAScript version to parse. Must
|
||||
// be either 3, or 5, or 6. This influences support for strict
|
||||
// mode, the set of reserved words, support for getters and
|
||||
// setters and other features.
|
||||
ecmaVersion: 5,
|
||||
// Source type ("script" or "module") for different semantics
|
||||
sourceType: "script",
|
||||
// `onInsertedSemicolon` can be a callback that will be called
|
||||
// when a semicolon is automatically inserted. It will be passed
|
||||
// th position of the comma as an offset, and if `locations` is
|
||||
// enabled, it is given the location as a `{line, column}` object
|
||||
// as second argument.
|
||||
onInsertedSemicolon: null,
|
||||
// `onTrailingComma` is similar to `onInsertedSemicolon`, but for
|
||||
// trailing commas.
|
||||
onTrailingComma: null,
|
||||
// By default, reserved words are not enforced. Disable
|
||||
// `allowReserved` to enforce them. When this option has the
|
||||
// value "never", reserved words and keywords can also not be
|
||||
// used as property names.
|
||||
allowReserved: true,
|
||||
// When enabled, a return at the top level is not considered an
|
||||
// error.
|
||||
allowReturnOutsideFunction: false,
|
||||
// When enabled, import/export statements are not constrained to
|
||||
// appearing at the top of the program.
|
||||
allowImportExportEverywhere: false,
|
||||
// When enabled, hashbang directive in the beginning of file
|
||||
// is allowed and treated as a line comment.
|
||||
allowHashBang: false,
|
||||
// When `locations` is on, `loc` properties holding objects with
|
||||
// `start` and `end` properties in `{line, column}` form (with
|
||||
// line being 1-based and column 0-based) will be attached to the
|
||||
// nodes.
|
||||
locations: false,
|
||||
// A function can be passed as `onToken` option, which will
|
||||
// cause Acorn to call that function with object in the same
|
||||
// format as tokenize() returns. Note that you are not
|
||||
// allowed to call the parser from the callback—that will
|
||||
// corrupt its internal state.
|
||||
onToken: null,
|
||||
// A function can be passed as `onComment` option, which will
|
||||
// cause Acorn to call that function with `(block, text, start,
|
||||
// end)` parameters whenever a comment is skipped. `block` is a
|
||||
// boolean indicating whether this is a block (`/* */`) comment,
|
||||
// `text` is the content of the comment, and `start` and `end` are
|
||||
// character offsets that denote the start and end of the comment.
|
||||
// When the `locations` option is on, two more parameters are
|
||||
// passed, the full `{line, column}` locations of the start and
|
||||
// end of the comments. Note that you are not allowed to call the
|
||||
// parser from the callback—that will corrupt its internal state.
|
||||
onComment: null,
|
||||
// Nodes have their start and end characters offsets recorded in
|
||||
// `start` and `end` properties (directly on the node, rather than
|
||||
// the `loc` object, which holds line/column data. To also add a
|
||||
// [semi-standardized][range] `range` property holding a `[start,
|
||||
// end]` array with the same numbers, set the `ranges` option to
|
||||
// `true`.
|
||||
//
|
||||
// [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
|
||||
ranges: false,
|
||||
// It is possible to parse multiple files into a single AST by
|
||||
// passing the tree produced by parsing the first file as
|
||||
// `program` option in subsequent parses. This will add the
|
||||
// toplevel forms of the parsed file to the `Program` (top) node
|
||||
// of an existing parse tree.
|
||||
program: null,
|
||||
// When `locations` is on, you can pass this to record the source
|
||||
// file in every node's `loc` object.
|
||||
sourceFile: null,
|
||||
// This value, if given, is stored in every node, whether
|
||||
// `locations` is on or off.
|
||||
directSourceFile: null,
|
||||
// When enabled, parenthesized expressions are represented by
|
||||
// (non-standard) ParenthesizedExpression nodes
|
||||
preserveParens: false,
|
||||
plugins: {},
|
||||
// Babel-specific options
|
||||
features: {},
|
||||
strictMode: null
|
||||
};exports.defaultOptions = defaultOptions;
|
||||
|
||||
function getOptions(opts) {
|
||||
var options = {};
|
||||
for (var opt in defaultOptions) {
|
||||
options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt];
|
||||
}if (isArray(options.onToken)) {
|
||||
(function () {
|
||||
var tokens = options.onToken;
|
||||
options.onToken = function (token) {
|
||||
return tokens.push(token);
|
||||
};
|
||||
})();
|
||||
}
|
||||
if (isArray(options.onComment)) options.onComment = pushComment(options, options.onComment);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function pushComment(options, array) {
|
||||
return function (block, text, start, end, startLoc, endLoc) {
|
||||
var comment = {
|
||||
type: block ? "Block" : "Line",
|
||||
value: text,
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
if (options.locations) comment.loc = new SourceLocation(this, startLoc, endLoc);
|
||||
if (options.ranges) comment.range = [start, end];
|
||||
array.push(comment);
|
||||
};
|
||||
}
|
||||
88
lib/acorn/src/parseutil.js
Normal file
88
lib/acorn/src/parseutil.js
Normal file
@@ -0,0 +1,88 @@
|
||||
"use strict";
|
||||
|
||||
var tt = require("./tokentype").types;
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var lineBreak = require("./whitespace").lineBreak;
|
||||
|
||||
var pp = Parser.prototype;
|
||||
|
||||
// ## Parser utilities
|
||||
|
||||
// Test whether a statement node is the string literal `"use strict"`.
|
||||
|
||||
pp.isUseStrict = function (stmt) {
|
||||
return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && 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) {
|
||||
if (this.type === type) {
|
||||
this.next();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Tests whether parsed token is a contextual keyword.
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
// Asserts that following token is given contextual keyword.
|
||||
|
||||
pp.expectContextual = function (name) {
|
||||
if (!this.eatContextual(name)) this.unexpected();
|
||||
};
|
||||
|
||||
// Test whether a semicolon can be inserted at the current position.
|
||||
|
||||
pp.canInsertSemicolon = function () {
|
||||
return this.type === tt.eof || this.type === tt.braceR || lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
|
||||
};
|
||||
|
||||
pp.insertSemicolon = function () {
|
||||
if (this.canInsertSemicolon()) {
|
||||
if (this.options.onInsertedSemicolon) this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Consume a semicolon, or, failing that, see if we are allowed to
|
||||
// pretend that there is a semicolon at this position.
|
||||
|
||||
pp.semicolon = function () {
|
||||
if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected();
|
||||
};
|
||||
|
||||
pp.afterTrailingComma = function (tokType) {
|
||||
if (this.type == tokType) {
|
||||
if (this.options.onTrailingComma) this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc);
|
||||
this.next();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Expect a token of a given type. If found, consume it, otherwise,
|
||||
// raise an unexpected token error.
|
||||
|
||||
pp.expect = function (type) {
|
||||
this.eat(type) || this.unexpected();
|
||||
};
|
||||
|
||||
// Raise an unexpected token error.
|
||||
|
||||
pp.unexpected = function (pos) {
|
||||
this.raise(pos != null ? pos : this.start, "Unexpected token");
|
||||
};
|
||||
85
lib/acorn/src/state.js
Normal file
85
lib/acorn/src/state.js
Normal file
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
|
||||
exports.Parser = Parser;
|
||||
exports.__esModule = true;
|
||||
|
||||
var _identifier = require("./identifier");
|
||||
|
||||
var reservedWords = _identifier.reservedWords;
|
||||
var keywords = _identifier.keywords;
|
||||
|
||||
var _tokentype = require("./tokentype");
|
||||
|
||||
var tt = _tokentype.types;
|
||||
var lineBreak = _tokentype.lineBreak;
|
||||
|
||||
function Parser(options, input, startPos) {
|
||||
this.options = options;
|
||||
this.loadPlugins(this.options.plugins);
|
||||
this.sourceFile = this.options.sourceFile || null;
|
||||
this.isKeyword = keywords[this.options.ecmaVersion >= 6 ? 6 : 5];
|
||||
this.isReservedWord = reservedWords[this.options.ecmaVersion];
|
||||
this.input = input;
|
||||
|
||||
// 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;
|
||||
} else {
|
||||
this.pos = this.lineStart = 0;
|
||||
this.curLine = 1;
|
||||
}
|
||||
|
||||
// Properties of the current token:
|
||||
// Its type
|
||||
this.type = tt.eof;
|
||||
// For tokens that include more information than their type, the value
|
||||
this.value = null;
|
||||
// Its start and end offset
|
||||
this.start = this.end = this.pos;
|
||||
// And, if locations are used, the {line, column} object
|
||||
// corresponding to those offsets
|
||||
this.startLoc = this.endLoc = null;
|
||||
|
||||
// Position information for the previous token
|
||||
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;
|
||||
|
||||
// Figure out if it's a module code.
|
||||
this.inModule = this.options.sourceType === "module";
|
||||
this.strict = this.options.strictMode === false ? false : this.inModule;
|
||||
|
||||
// Flags to track whether we are in a function, a generator.
|
||||
this.inFunction = this.inGenerator = false;
|
||||
// Labels in scope.
|
||||
this.labels = [];
|
||||
|
||||
// If enabled, skip leading hashbang line.
|
||||
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]);
|
||||
};
|
||||
|
||||
// Registered plugins
|
||||
|
||||
var plugins = {};
|
||||
|
||||
exports.plugins = plugins;
|
||||
Parser.prototype.loadPlugins = function (plugins) {
|
||||
for (var _name in plugins) {
|
||||
var plugin = exports.plugins[_name];
|
||||
if (!plugin) throw new Error("Plugin '" + _name + "' not found");
|
||||
plugin(this, plugins[_name]);
|
||||
}
|
||||
};
|
||||
666
lib/acorn/src/statement.js
Normal file
666
lib/acorn/src/statement.js
Normal file
@@ -0,0 +1,666 @@
|
||||
"use strict";
|
||||
|
||||
var tt = require("./tokentype").types;
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var lineBreak = require("./whitespace").lineBreak;
|
||||
|
||||
var pp = Parser.prototype;
|
||||
|
||||
// ### Statement parsing
|
||||
|
||||
// Parse a program. Initializes the parser, reads any number of
|
||||
// statements, and wraps them in a Program node. Optionally takes a
|
||||
// `program` argument. If present, the statements will be appended
|
||||
// to its body instead of creating a new node.
|
||||
|
||||
pp.parseTopLevel = function (node) {
|
||||
var first = true;
|
||||
if (!node.body) node.body = [];
|
||||
while (this.type !== tt.eof) {
|
||||
var stmt = this.parseStatement(true, true);
|
||||
node.body.push(stmt);
|
||||
if (first && this.isUseStrict(stmt)) this.setStrict(true);
|
||||
first = false;
|
||||
}
|
||||
this.next();
|
||||
if (this.options.ecmaVersion >= 6) {
|
||||
node.sourceType = this.options.sourceType;
|
||||
}
|
||||
return this.finishNode(node, "Program");
|
||||
};
|
||||
|
||||
var loopLabel = { kind: "loop" },
|
||||
switchLabel = { kind: "switch" };
|
||||
|
||||
// Parse a single statement.
|
||||
//
|
||||
// If expecting a statement and finding a slash operator, parse a
|
||||
// regular expression literal. This is to handle cases like
|
||||
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
|
||||
// does not help.
|
||||
|
||||
pp.parseStatement = function (declaration, topLevel) {
|
||||
var 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._function:
|
||||
if (!declaration && this.options.ecmaVersion >= 6) this.unexpected();
|
||||
return this.parseFunctionStatement(node);
|
||||
case tt._class:
|
||||
if (!declaration) this.unexpected();
|
||||
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._export:
|
||||
case tt._import:
|
||||
if (!this.options.allowImportExportEverywhere) {
|
||||
if (!topLevel) 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'");
|
||||
}
|
||||
return starttype === tt._import ? this.parseImport(node) : this.parseExport(node);
|
||||
|
||||
case tt.name:
|
||||
if (this.options.features["es7.asyncFunctions"] && this.value === "async") {
|
||||
// check to see if `function ` appears after this token, this is
|
||||
// pretty hacky
|
||||
if (this.lookahead().type === tt._function) {
|
||||
this.next();
|
||||
this.expect(tt._function);
|
||||
return this.parseFunction(node, true, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
// If the statement does not start with a statement keyword or a
|
||||
// brace, it's an ExpressionStatement or LabeledStatement. We
|
||||
// simply start parsing an expression, and afterwards, if the
|
||||
// next token is a colon and the expression was a simple
|
||||
// Identifier node, we switch to interpreting it as a label.
|
||||
default:
|
||||
var 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);
|
||||
}
|
||||
};
|
||||
|
||||
pp.parseBreakContinueStatement = function (node, keyword) {
|
||||
var 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) {
|
||||
var 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 (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.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`
|
||||
// statement or expression, disallowing the `in` operator (see
|
||||
// the second parameter to `parseExpression`), and then check
|
||||
// whether the next token is `in` or `of`. When there is no init
|
||||
// part (semicolon immediately after the opening parenthesis), it
|
||||
// is a regular `for` loop.
|
||||
|
||||
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) {
|
||||
var _init = this.startNode(),
|
||||
varKind = this.type;
|
||||
this.next();
|
||||
this.parseVar(_init, true, varKind);
|
||||
this.finishNode(_init, "VariableDeclaration");
|
||||
if ((this.type === tt._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) && _init.declarations.length === 1 && !(varKind !== tt._var && _init.declarations[0].init)) return this.parseForIn(node, _init);
|
||||
return this.parseFor(node, _init);
|
||||
}
|
||||
var refShorthandDefaultPos = { start: 0 };
|
||||
var 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);
|
||||
} else if (refShorthandDefaultPos.start) {
|
||||
this.unexpected(refShorthandDefaultPos.start);
|
||||
}
|
||||
return this.parseFor(node, init);
|
||||
};
|
||||
|
||||
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.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");
|
||||
};
|
||||
|
||||
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;) {
|
||||
if (this.type === tt._case || this.type === tt._default) {
|
||||
var 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();
|
||||
} else {
|
||||
if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses");
|
||||
sawDefault = true;
|
||||
cur.test = null;
|
||||
}
|
||||
this.expect(tt.colon);
|
||||
} else {
|
||||
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");
|
||||
};
|
||||
|
||||
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");
|
||||
};
|
||||
|
||||
// Reused empty array added for node fields that are always empty.
|
||||
|
||||
var empty = [];
|
||||
|
||||
pp.parseTryStatement = function (node) {
|
||||
this.next();
|
||||
node.block = this.parseBlock();
|
||||
node.handler = null;
|
||||
if (this.type === tt._catch) {
|
||||
var 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");
|
||||
}
|
||||
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");
|
||||
};
|
||||
|
||||
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 (var i = 0; i < this.labels.length; ++i) {
|
||||
if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared");
|
||||
}var kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null;
|
||||
this.labels.push({ name: maybeName, kind: kind });
|
||||
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) {
|
||||
var node = this.startNode(),
|
||||
first = true,
|
||||
oldStrict = undefined;
|
||||
node.body = [];
|
||||
this.expect(tt.braceL);
|
||||
while (!this.eat(tt.braceR)) {
|
||||
var stmt = this.parseStatement(true);
|
||||
node.body.push(stmt);
|
||||
if (first && allowStrict && this.isUseStrict(stmt)) {
|
||||
oldStrict = this.strict;
|
||||
this.setStrict(this.strict = true);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
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");
|
||||
};
|
||||
|
||||
// Parse a `for`/`in` and `for`/`of` loop, which are almost
|
||||
// same from parser's perspective.
|
||||
|
||||
pp.parseForIn = function (node, init) {
|
||||
var 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;
|
||||
for (;;) {
|
||||
var decl = this.startNode();
|
||||
this.parseVarHead(decl);
|
||||
if (this.eat(tt.eq)) {
|
||||
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");
|
||||
} else {
|
||||
decl.init = null;
|
||||
}
|
||||
node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
|
||||
if (!this.eat(tt.comma)) break;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
pp.parseVarHead = function (decl) {
|
||||
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.parseFunctionParams = function (node) {
|
||||
this.expect(tt.parenL);
|
||||
node.params = this.parseBindingList(tt.parenR, false, false);
|
||||
};
|
||||
|
||||
// Parse a class declaration or literal (depending on the
|
||||
// `isStatement` parameter).
|
||||
|
||||
pp.parseClass = function (node, isStatement) {
|
||||
this.next();
|
||||
node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null;
|
||||
node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null;
|
||||
var classBody = this.startNode();
|
||||
classBody.body = [];
|
||||
this.expect(tt.braceL);
|
||||
while (!this.eat(tt.braceR)) {
|
||||
if (this.eat(tt.semi)) continue;
|
||||
var method = this.startNode();
|
||||
var isGenerator = this.eat(tt.star);
|
||||
this.parsePropertyName(method);
|
||||
if (this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && method.key.name === "static") {
|
||||
if (isGenerator) this.unexpected();
|
||||
method["static"] = true;
|
||||
isGenerator = this.eat(tt.star);
|
||||
this.parsePropertyName(method);
|
||||
} else {
|
||||
method["static"] = false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
method.kind = "method";
|
||||
if (!method.computed && !isGenerator) {
|
||||
if (method.key.type === "Identifier") {
|
||||
if (this.type !== tt.parenL && (method.key.name === "get" || method.key.name === "set")) {
|
||||
method.kind = method.key.name;
|
||||
this.parsePropertyName(method);
|
||||
} else if (!method["static"] && method.key.name === "constructor") {
|
||||
method.kind = "constructor";
|
||||
}
|
||||
} else if (!method["static"] && method.key.type === "Literal" && method.key.value === "constructor") {
|
||||
method.kind = "constructor";
|
||||
}
|
||||
}
|
||||
method.value = this.parseMethod(isGenerator);
|
||||
classBody.body.push(this.finishNode(method, "MethodDefinition"));
|
||||
}
|
||||
node.body = this.finishNode(classBody, "ClassBody");
|
||||
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
|
||||
};
|
||||
|
||||
pp.parseClass = function (node, isStatement) {
|
||||
this.next();
|
||||
this.parseClassId(node, isStatement);
|
||||
this.parseClassSuper(node);
|
||||
var classBody = this.startNode();
|
||||
classBody.body = [];
|
||||
this.expect(tt.braceL);
|
||||
while (!this.eat(tt.braceR)) {
|
||||
if (this.eat(tt.semi)) continue;
|
||||
var method = this.startNode();
|
||||
var isGenerator = this.eat(tt.star),
|
||||
isAsync = false;
|
||||
this.parsePropertyName(method);
|
||||
if (this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && method.key.name === "static") {
|
||||
if (isGenerator) this.unexpected();
|
||||
method["static"] = true;
|
||||
isGenerator = this.eat(tt.star);
|
||||
this.parsePropertyName(method);
|
||||
} else {
|
||||
method["static"] = false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
method.kind = "method";
|
||||
if (!method.computed && !isGenerator) {
|
||||
if (method.key.type === "Identifier") {
|
||||
if (this.type !== tt.parenL && (method.key.name === "get" || method.key.name === "set")) {
|
||||
method.kind = method.key.name;
|
||||
this.parsePropertyName(method);
|
||||
} else if (!method["static"] && method.key.name === "constructor") {
|
||||
method.kind = "constructor";
|
||||
}
|
||||
} else if (!method["static"] && method.key.type === "Literal" && method.key.value === "constructor") {
|
||||
method.kind = "constructor";
|
||||
}
|
||||
}
|
||||
this.parseClassMethod(classBody, method, isGenerator, isAsync);
|
||||
}
|
||||
node.body = this.finishNode(classBody, "ClassBody");
|
||||
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
|
||||
};
|
||||
|
||||
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.parseClassSuper = function (node) {
|
||||
node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null;
|
||||
};
|
||||
|
||||
// Parses module export declaration.
|
||||
|
||||
pp.parseExport = function (node) {
|
||||
this.next();
|
||||
// export * from '...'
|
||||
if (this.eat(tt.star)) {
|
||||
this.expectContextual("from");
|
||||
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected();
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "ExportAllDeclaration");
|
||||
}
|
||||
if (this.eat(tt._default)) {
|
||||
// export default ...
|
||||
var expr = this.parseMaybeAssign();
|
||||
var needsSemi = false;
|
||||
if (expr.id) switch (expr.type) {
|
||||
case "FunctionExpression":
|
||||
expr.type = "FunctionDeclaration";break;
|
||||
case "ClassExpression":
|
||||
expr.type = "ClassDeclaration";break;
|
||||
default:
|
||||
needsSemi = true;
|
||||
}
|
||||
node.declaration = expr;
|
||||
if (needsSemi) this.semicolon();
|
||||
return this.finishNode(node, "ExportDefaultDeclaration");
|
||||
}
|
||||
// export var|const|let|function|class ...
|
||||
if (this.type.keyword || this.shouldParseExportDeclaration()) {
|
||||
node.declaration = this.parseStatement(true);
|
||||
node.specifiers = [];
|
||||
node.source = null;
|
||||
} else {
|
||||
// export { x, y as z } [from '...']
|
||||
node.declaration = null;
|
||||
node.specifiers = this.parseExportSpecifiers();
|
||||
if (this.eatContextual("from")) {
|
||||
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected();
|
||||
} else {
|
||||
node.source = null;
|
||||
}
|
||||
this.semicolon();
|
||||
}
|
||||
return this.finishNode(node, "ExportNamedDeclaration");
|
||||
};
|
||||
|
||||
pp.shouldParseExportDeclaration = function () {
|
||||
return this.options.features["es7.asyncFunctions"] && this.isContextual("async");
|
||||
};
|
||||
|
||||
// Parses a comma-separated list of module exports.
|
||||
|
||||
pp.parseExportSpecifiers = function () {
|
||||
var 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;
|
||||
|
||||
var node = this.startNode();
|
||||
node.local = this.parseIdent(this.type === tt._default);
|
||||
node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local;
|
||||
nodes.push(this.finishNode(node, "ExportSpecifier"));
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
// Parses import declaration.
|
||||
|
||||
pp.parseImport = function (node) {
|
||||
this.next();
|
||||
// import '...'
|
||||
if (this.type === tt.string) {
|
||||
node.specifiers = empty;
|
||||
node.source = this.parseExprAtom();
|
||||
node.kind = "";
|
||||
} else {
|
||||
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");
|
||||
};
|
||||
|
||||
// Parses a comma-separated list of module imports.
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
this.expect(tt.braceL);
|
||||
while (!this.eat(tt.braceR)) {
|
||||
if (!first) {
|
||||
this.expect(tt.comma);
|
||||
if (this.afterTrailingComma(tt.braceR)) break;
|
||||
} else first = false;
|
||||
|
||||
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"));
|
||||
}
|
||||
};
|
||||
|
||||
pp.parseImportSpecifierDefault = function (id, start) {
|
||||
var node = this.startNodeAt(start);
|
||||
node.local = id;
|
||||
this.checkLVal(node.local, true);
|
||||
return this.finishNode(node, "ImportDefaultSpecifier");
|
||||
};
|
||||
101
lib/acorn/src/tokencontext.js
Normal file
101
lib/acorn/src/tokencontext.js
Normal file
@@ -0,0 +1,101 @@
|
||||
"use strict";
|
||||
|
||||
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
|
||||
|
||||
exports.__esModule = true;
|
||||
// The algorithm used to determine whether a regexp can appear at a
|
||||
// given point in the program is loosely based on sweet.js' approach.
|
||||
// See https://github.com/mozilla/sweet.js/wiki/design
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var tt = require("./tokentype").types;
|
||||
|
||||
var lineBreak = require("./whitespace").lineBreak;
|
||||
|
||||
var TokContext = exports.TokContext = function TokContext(token, isExpr, preserveSpace, override) {
|
||||
_classCallCheck(this, TokContext);
|
||||
|
||||
this.token = token;
|
||||
this.isExpr = isExpr;
|
||||
this.preserveSpace = preserveSpace;
|
||||
this.override = override;
|
||||
};
|
||||
|
||||
var types = {
|
||||
b_stat: new TokContext("{", false),
|
||||
b_expr: new TokContext("{", true),
|
||||
b_tmpl: new TokContext("${", true),
|
||||
p_stat: new TokContext("(", false),
|
||||
p_expr: new TokContext("(", true),
|
||||
q_tmpl: new TokContext("`", true, true, function (p) {
|
||||
return p.readTmplToken();
|
||||
}),
|
||||
f_expr: new TokContext("function", true)
|
||||
};
|
||||
|
||||
exports.types = types;
|
||||
var pp = Parser.prototype;
|
||||
|
||||
pp.initialContext = function () {
|
||||
return [types.b_stat];
|
||||
};
|
||||
|
||||
pp.braceIsBlock = function (prevType) {
|
||||
var parent = undefined;
|
||||
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;
|
||||
};
|
||||
|
||||
pp.updateContext = function (prevType) {
|
||||
var update = undefined,
|
||||
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 () {
|
||||
var out = this.context.pop();
|
||||
if (out === types.b_stat && this.curContext() === types.f_expr) {
|
||||
this.context.pop();
|
||||
this.exprAllowed = false;
|
||||
} else if (out === types.b_tmpl) {
|
||||
this.exprAllowed = true;
|
||||
} else {
|
||||
this.exprAllowed = !(out && out.isExpr);
|
||||
}
|
||||
};
|
||||
|
||||
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.parenL.updateContext = function (prevType) {
|
||||
var 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._function.updateContext = function () {
|
||||
if (this.curContext() !== types.b_stat) this.context.push(types.f_expr);
|
||||
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;
|
||||
};
|
||||
|
||||
// tokExprAllowed stays unchanged
|
||||
753
lib/acorn/src/tokenize.js
Normal file
753
lib/acorn/src/tokenize.js
Normal file
@@ -0,0 +1,753 @@
|
||||
"use strict";
|
||||
|
||||
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
var _identifier = require("./identifier");
|
||||
|
||||
var isIdentifierStart = _identifier.isIdentifierStart;
|
||||
var isIdentifierChar = _identifier.isIdentifierChar;
|
||||
|
||||
var _tokentype = require("./tokentype");
|
||||
|
||||
var tt = _tokentype.types;
|
||||
var keywordTypes = _tokentype.keywords;
|
||||
|
||||
var Parser = require("./state").Parser;
|
||||
|
||||
var SourceLocation = require("./location").SourceLocation;
|
||||
|
||||
var _whitespace = require("./whitespace");
|
||||
|
||||
var lineBreak = _whitespace.lineBreak;
|
||||
var lineBreakG = _whitespace.lineBreakG;
|
||||
var isNewLine = _whitespace.isNewLine;
|
||||
var nonASCIIwhitespace = _whitespace.nonASCIIwhitespace;
|
||||
|
||||
// Object type used to represent tokens. Note that normally, tokens
|
||||
// simply exist as properties on the parser object. This is only
|
||||
// used for the onToken callback and the external tokenizer.
|
||||
|
||||
var Token = exports.Token = function Token(p) {
|
||||
_classCallCheck(this, Token);
|
||||
|
||||
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
|
||||
|
||||
var pp = Parser.prototype;
|
||||
|
||||
// Move to the next token
|
||||
|
||||
pp.next = function () {
|
||||
if (this.options.onToken) this.options.onToken(new Token(this));
|
||||
|
||||
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 () {
|
||||
var self = this;
|
||||
return { next: function next() {
|
||||
var token = self.getToken();
|
||||
return {
|
||||
done: token.type === tt.eof,
|
||||
value: token
|
||||
};
|
||||
} };
|
||||
};
|
||||
|
||||
// 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;
|
||||
if (this.options.locations) {
|
||||
while (this.pos < this.lineStart) {
|
||||
this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
|
||||
--this.curLine;
|
||||
}
|
||||
}
|
||||
this.nextToken();
|
||||
};
|
||||
|
||||
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 () {
|
||||
var 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);
|
||||
|
||||
if (curContext.override) return curContext.override(this);else this.readToken(this.fullCharCodeAtPos());
|
||||
};
|
||||
|
||||
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.getTokenFromCode(code);
|
||||
};
|
||||
|
||||
pp.fullCharCodeAtPos = function () {
|
||||
var code = this.input.charCodeAt(this.pos);
|
||||
if (code <= 55295 || code >= 57344) return code;
|
||||
var next = this.input.charCodeAt(this.pos + 1);
|
||||
return (code << 10) + next - 56613888;
|
||||
};
|
||||
|
||||
pp.skipBlockComment = function () {
|
||||
var startLoc = this.options.onComment && this.options.locations && this.curPosition();
|
||||
var 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;
|
||||
var match = undefined;
|
||||
while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) {
|
||||
++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());
|
||||
};
|
||||
|
||||
pp.skipLineComment = function (startSkip) {
|
||||
var start = this.pos;
|
||||
var startLoc = this.options.onComment && this.options.locations && this.curPosition();
|
||||
var 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);
|
||||
}
|
||||
if (this.options.onComment) this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, startLoc, this.options.locations && 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) {
|
||||
var ch = this.input.charCodeAt(this.pos);
|
||||
if (ch === 32) {
|
||||
// ' '
|
||||
++this.pos;
|
||||
} else if (ch === 13) {
|
||||
++this.pos;
|
||||
var 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) {
|
||||
// '/'
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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();
|
||||
var prevType = this.type;
|
||||
this.type = type;
|
||||
this.value = val;
|
||||
|
||||
this.updateContext(prevType);
|
||||
};
|
||||
|
||||
// ### Token reading
|
||||
|
||||
// This is the function that is called to fetch the next token. It
|
||||
// is somewhat obscure, because it works in character codes rather
|
||||
// than characters, and because operator parsing has been inlined
|
||||
// into it.
|
||||
//
|
||||
// All in the name of speed.
|
||||
//
|
||||
pp.readToken_dot = function () {
|
||||
var next = this.input.charCodeAt(this.pos + 1);
|
||||
if (next >= 48 && next <= 57) return this.readNumber(true);
|
||||
var 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);
|
||||
} else {
|
||||
++this.pos;
|
||||
return this.finishToken(tt.dot);
|
||||
}
|
||||
};
|
||||
|
||||
pp.readToken_slash = function () {
|
||||
// '/'
|
||||
var 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);
|
||||
|
||||
if (next === 42) {
|
||||
// '*'
|
||||
width++;
|
||||
next = this.input.charCodeAt(this.pos + 2);
|
||||
type = tt.exponent;
|
||||
}
|
||||
|
||||
if (next === 61) {
|
||||
width++;
|
||||
type = tt.assign;
|
||||
}
|
||||
|
||||
return this.finishOp(type, width);
|
||||
};
|
||||
|
||||
pp.readToken_pipe_amp = function (code) {
|
||||
// '|&'
|
||||
var 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 () {
|
||||
// '^'
|
||||
var next = this.input.charCodeAt(this.pos + 1);
|
||||
if (next === 61) return this.finishOp(tt.assign, 2);
|
||||
return this.finishOp(tt.bitwiseXOR, 1);
|
||||
};
|
||||
|
||||
pp.readToken_plus_min = function (code) {
|
||||
// '+-'
|
||||
var 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))) {
|
||||
// A `-->` line comment
|
||||
this.skipLineComment(3);
|
||||
this.skipSpace();
|
||||
return this.nextToken();
|
||||
}
|
||||
return this.finishOp(tt.incDec, 2);
|
||||
}
|
||||
if (next === 61) return this.finishOp(tt.assign, 2);
|
||||
return this.finishOp(tt.plusMin, 1);
|
||||
};
|
||||
|
||||
pp.readToken_lt_gt = function (code) {
|
||||
// '<>'
|
||||
var next = this.input.charCodeAt(this.pos + 1);
|
||||
var size = 1;
|
||||
if (next === code) {
|
||||
size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;
|
||||
if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1);
|
||||
return this.finishOp(tt.bitShift, size);
|
||||
}
|
||||
if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && this.input.charCodeAt(this.pos + 3) == 45) {
|
||||
if (this.inModule) unexpected();
|
||||
// `<!--`, an XML-style comment that should be interpreted as a line comment
|
||||
this.skipLineComment(4);
|
||||
this.skipSpace();
|
||||
return this.nextToken();
|
||||
}
|
||||
if (next === 61) size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2;
|
||||
return this.finishOp(tt.relational, size);
|
||||
};
|
||||
|
||||
pp.readToken_eq_excl = function (code) {
|
||||
// '=!'
|
||||
var next = this.input.charCodeAt(this.pos + 1);
|
||||
if (next === 61) return this.finishOp(tt.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2);
|
||||
if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) {
|
||||
// '=>'
|
||||
this.pos += 2;
|
||||
return this.finishToken(tt.arrow);
|
||||
}
|
||||
return this.finishOp(code === 61 ? tt.eq : tt.prefix, 1);
|
||||
};
|
||||
|
||||
pp.getTokenFromCode = function (code) {
|
||||
switch (code) {
|
||||
// The interpretation of a dot depends on whether it is followed
|
||||
// by a digit or another two dots.
|
||||
case 46:
|
||||
// '.'
|
||||
return this.readToken_dot();
|
||||
|
||||
// Punctuation tokens.
|
||||
case 40:
|
||||
++this.pos;return this.finishToken(tt.parenL);
|
||||
case 41:
|
||||
++this.pos;return this.finishToken(tt.parenR);
|
||||
case 59:
|
||||
++this.pos;return this.finishToken(tt.semi);
|
||||
case 44:
|
||||
++this.pos;return this.finishToken(tt.comma);
|
||||
case 91:
|
||||
++this.pos;return this.finishToken(tt.bracketL);
|
||||
case 93:
|
||||
++this.pos;return this.finishToken(tt.bracketR);
|
||||
case 123:
|
||||
++this.pos;return this.finishToken(tt.braceL);
|
||||
case 125:
|
||||
++this.pos;return this.finishToken(tt.braceR);
|
||||
case 58:
|
||||
++this.pos;return this.finishToken(tt.colon);
|
||||
case 63:
|
||||
++this.pos;return this.finishToken(tt.question);
|
||||
|
||||
case 96:
|
||||
// '`'
|
||||
if (this.options.ecmaVersion < 6) break;
|
||||
++this.pos;
|
||||
return this.finishToken(tt.backQuote);
|
||||
|
||||
case 48:
|
||||
// '0'
|
||||
var next = this.input.charCodeAt(this.pos + 1);
|
||||
if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number
|
||||
if (this.options.ecmaVersion >= 6) {
|
||||
if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number
|
||||
if (next === 98 || next === 66) return this.readRadixNumber(2); // '0b', '0B' - binary number
|
||||
}
|
||||
// Anything else beginning with a digit is an integer, octal
|
||||
// number, or float.
|
||||
case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:
|
||||
// 1-9
|
||||
return this.readNumber(false);
|
||||
|
||||
// Quotes produce strings.
|
||||
case 34:case 39:
|
||||
// '"', "'"
|
||||
return this.readString(code);
|
||||
|
||||
// Operators are parsed inline in tiny state machines. '=' (61) is
|
||||
// often referred to. `finishOp` simply skips the amount of
|
||||
// characters it is given as second argument, and returns a token
|
||||
// of the type given by its first argument.
|
||||
|
||||
case 47:
|
||||
// '/'
|
||||
return this.readToken_slash();
|
||||
|
||||
case 37:case 42:
|
||||
// '%*'
|
||||
return this.readToken_mult_modulo(code);
|
||||
|
||||
case 124:case 38:
|
||||
// '|&'
|
||||
return this.readToken_pipe_amp(code);
|
||||
|
||||
case 94:
|
||||
// '^'
|
||||
return this.readToken_caret();
|
||||
|
||||
case 43:case 45:
|
||||
// '+-'
|
||||
return this.readToken_plus_min(code);
|
||||
|
||||
case 60:case 62:
|
||||
// '<>'
|
||||
return this.readToken_lt_gt(code);
|
||||
|
||||
case 61:case 33:
|
||||
// '=!'
|
||||
return this.readToken_eq_excl(code);
|
||||
|
||||
case 126:
|
||||
// '~'
|
||||
return this.finishOp(tt.prefix, 1);
|
||||
}
|
||||
|
||||
this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'");
|
||||
};
|
||||
|
||||
pp.finishOp = function (type, size) {
|
||||
var str = this.input.slice(this.pos, this.pos + size);
|
||||
this.pos += size;
|
||||
return this.finishToken(type, str);
|
||||
};
|
||||
|
||||
var regexpUnicodeSupport = false;
|
||||
try {
|
||||
new RegExp("", "u");regexpUnicodeSupport = true;
|
||||
} catch (e) {}
|
||||
|
||||
// Parse a regular expression. Some context-awareness is necessary,
|
||||
// since a '/' inside a '[]' set does not end the expression.
|
||||
|
||||
pp.readRegexp = function () {
|
||||
var escaped = undefined,
|
||||
inClass = undefined,
|
||||
start = this.pos;
|
||||
for (;;) {
|
||||
if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression");
|
||||
var ch = this.input.charAt(this.pos);
|
||||
if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression");
|
||||
if (!escaped) {
|
||||
if (ch === "[") inClass = true;else if (ch === "]" && inClass) inClass = false;else if (ch === "/" && !inClass) break;
|
||||
escaped = ch === "\\";
|
||||
} else escaped = false;
|
||||
++this.pos;
|
||||
}
|
||||
var content = this.input.slice(start, this.pos);
|
||||
++this.pos;
|
||||
// Need to use `readWord1` because '\uXXXX' sequences are allowed
|
||||
// here (don't ask).
|
||||
var mods = this.readWord1();
|
||||
var tmp = content;
|
||||
if (mods) {
|
||||
var validFlags = /^[gmsiy]*$/;
|
||||
if (this.options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/;
|
||||
if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag");
|
||||
if (mods.indexOf("u") >= 0 && !regexpUnicodeSupport) {
|
||||
// Replace each astral symbol and every Unicode escape sequence that
|
||||
// possibly represents an astral symbol or a paired surrogate with a
|
||||
// single ASCII symbol to avoid throwing on regular expressions that
|
||||
// are only valid in combination with the `/u` flag.
|
||||
// Note: replacing with the ASCII symbol `x` might cause false
|
||||
// negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
|
||||
// perfectly valid pattern that is equivalent to `[a-b]`, but it would
|
||||
// be replaced by `[x-b]` which throws an error.
|
||||
tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|\\u\{([0-9a-fA-F]+)\}|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
|
||||
}
|
||||
}
|
||||
// Detect invalid regular expressions.
|
||||
try {
|
||||
new RegExp(tmp);
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) this.raise(start, "Error parsing regular expression: " + e.message);
|
||||
this.raise(e);
|
||||
}
|
||||
// Get a regular expression object for this pattern-flag pair, or `null` in
|
||||
// case the current environment doesn't support the flags it uses.
|
||||
var value = undefined;
|
||||
try {
|
||||
value = new RegExp(content, mods);
|
||||
} catch (err) {
|
||||
value = null;
|
||||
}
|
||||
return this.finishToken(tt.regexp, { pattern: content, flags: mods, value: value });
|
||||
};
|
||||
|
||||
// Read an integer in the given radix. Return null if zero digits
|
||||
// were read, the integer value otherwise. When `len` is given, this
|
||||
// will return `null` unless the integer has exactly `len` digits.
|
||||
|
||||
pp.readInt = function (radix, len) {
|
||||
var start = this.pos,
|
||||
total = 0;
|
||||
for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
|
||||
var code = this.input.charCodeAt(this.pos),
|
||||
val = undefined;
|
||||
if (code >= 97) val = code - 97 + 10; // a
|
||||
else if (code >= 65) val = code - 65 + 10; // A
|
||||
else if (code >= 48 && code <= 57) val = code - 48; // 0-9
|
||||
else val = Infinity;
|
||||
if (val >= radix) break;
|
||||
++this.pos;
|
||||
total = total * radix + val;
|
||||
}
|
||||
if (this.pos === start || len != null && this.pos - start !== len) return null;
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
pp.readRadixNumber = function (radix) {
|
||||
this.pos += 2; // 0x
|
||||
var val = this.readInt(radix);
|
||||
if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix);
|
||||
if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
|
||||
return this.finishToken(tt.num, val);
|
||||
};
|
||||
|
||||
// Read an integer, octal integer, or floating-point number.
|
||||
|
||||
pp.readNumber = function (startsWithDot) {
|
||||
var start = this.pos,
|
||||
isFloat = false,
|
||||
octal = this.input.charCodeAt(this.pos) === 48;
|
||||
if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number");
|
||||
if (this.input.charCodeAt(this.pos) === 46) {
|
||||
++this.pos;
|
||||
this.readInt(10);
|
||||
isFloat = true;
|
||||
}
|
||||
var next = this.input.charCodeAt(this.pos);
|
||||
if (next === 69 || next === 101) {
|
||||
// 'eE'
|
||||
next = this.input.charCodeAt(++this.pos);
|
||||
if (next === 43 || next === 45) ++this.pos; // '+-'
|
||||
if (this.readInt(10) === null) this.raise(start, "Invalid number");
|
||||
isFloat = true;
|
||||
}
|
||||
if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
|
||||
|
||||
var str = this.input.slice(start, this.pos),
|
||||
val = undefined;
|
||||
if (isFloat) val = parseFloat(str);else if (!octal || str.length === 1) val = parseInt(str, 10);else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number");else val = parseInt(str, 8);
|
||||
return this.finishToken(tt.num, val);
|
||||
};
|
||||
|
||||
// Read a string value, interpreting backslash-escapes.
|
||||
|
||||
pp.readCodePoint = function () {
|
||||
var ch = this.input.charCodeAt(this.pos),
|
||||
code = undefined;
|
||||
|
||||
if (ch === 123) {
|
||||
if (this.options.ecmaVersion < 6) this.unexpected();
|
||||
++this.pos;
|
||||
code = this.readHexChar(this.input.indexOf("}", this.pos) - this.pos);
|
||||
++this.pos;
|
||||
if (code > 1114111) this.unexpected();
|
||||
} else {
|
||||
code = this.readHexChar(4);
|
||||
}
|
||||
return code;
|
||||
};
|
||||
|
||||
function codePointToString(code) {
|
||||
// UTF-16 Decoding
|
||||
if (code <= 65535) return String.fromCharCode(code);
|
||||
return String.fromCharCode((code - 65536 >> 10) + 55296, (code - 65536 & 1023) + 56320);
|
||||
}
|
||||
|
||||
pp.readString = 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 === 92) {
|
||||
// '\'
|
||||
out += this.input.slice(chunkStart, this.pos);
|
||||
out += this.readEscapedChar();
|
||||
chunkStart = this.pos;
|
||||
} else {
|
||||
if (isNewLine(ch)) this.raise(this.start, "Unterminated string constant");
|
||||
++this.pos;
|
||||
}
|
||||
}
|
||||
out += this.input.slice(chunkStart, this.pos++);
|
||||
return this.finishToken(tt.string, out);
|
||||
};
|
||||
|
||||
// Reads template string tokens.
|
||||
|
||||
pp.readTmplToken = function () {
|
||||
var out = "",
|
||||
chunkStart = this.pos;
|
||||
for (;;) {
|
||||
if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template");
|
||||
var ch = this.input.charCodeAt(this.pos);
|
||||
if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) {
|
||||
// '`', '${'
|
||||
if (this.pos === this.start && this.type === tt.template) {
|
||||
if (ch === 36) {
|
||||
this.pos += 2;
|
||||
return this.finishToken(tt.dollarBraceL);
|
||||
} else {
|
||||
++this.pos;
|
||||
return this.finishToken(tt.backQuote);
|
||||
}
|
||||
}
|
||||
out += this.input.slice(chunkStart, this.pos);
|
||||
return this.finishToken(tt.template, out);
|
||||
}
|
||||
if (ch === 92) {
|
||||
// '\'
|
||||
out += this.input.slice(chunkStart, this.pos);
|
||||
out += this.readEscapedChar();
|
||||
chunkStart = this.pos;
|
||||
} else if (isNewLine(ch)) {
|
||||
out += this.input.slice(chunkStart, this.pos);
|
||||
++this.pos;
|
||||
if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {
|
||||
++this.pos;
|
||||
out += "\n";
|
||||
} else {
|
||||
out += String.fromCharCode(ch);
|
||||
}
|
||||
if (this.options.locations) {
|
||||
++this.curLine;
|
||||
this.lineStart = this.pos;
|
||||
}
|
||||
chunkStart = this.pos;
|
||||
} else {
|
||||
++this.pos;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Used to read escaped characters
|
||||
|
||||
pp.readEscapedChar = function () {
|
||||
var ch = this.input.charCodeAt(++this.pos);
|
||||
var octal = /^[0-7]+/.exec(this.input.slice(this.pos, this.pos + 3));
|
||||
if (octal) octal = octal[0];
|
||||
while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, -1);
|
||||
if (octal === "0") octal = null;
|
||||
++this.pos;
|
||||
if (octal) {
|
||||
if (this.strict) this.raise(this.pos - 2, "Octal literal in strict mode");
|
||||
this.pos += octal.length - 1;
|
||||
return String.fromCharCode(parseInt(octal, 8));
|
||||
} else {
|
||||
switch (ch) {
|
||||
case 110:
|
||||
return "\n"; // 'n' -> '\n'
|
||||
case 114:
|
||||
return "\r"; // 'r' -> '\r'
|
||||
case 120:
|
||||
return String.fromCharCode(this.readHexChar(2)); // 'x'
|
||||
case 117:
|
||||
return codePointToString(this.readCodePoint()); // 'u'
|
||||
case 116:
|
||||
return "\t"; // 't' -> '\t'
|
||||
case 98:
|
||||
return "\b"; // 'b' -> '\b'
|
||||
case 118:
|
||||
return "\u000b"; // 'v' -> '\u000b'
|
||||
case 102:
|
||||
return "\f"; // 'f' -> '\f'
|
||||
case 48:
|
||||
return "\u0000"; // 0 -> '\0'
|
||||
case 13:
|
||||
if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n'
|
||||
case 10:
|
||||
// ' \n'
|
||||
if (this.options.locations) {
|
||||
this.lineStart = this.pos;++this.curLine;
|
||||
}
|
||||
return "";
|
||||
default:
|
||||
return String.fromCharCode(ch);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Used to read character escape sequences ('\x', '\u', '\U').
|
||||
|
||||
pp.readHexChar = function (len) {
|
||||
var n = this.readInt(16, len);
|
||||
if (n === null) this.raise(this.start, "Bad character escape sequence");
|
||||
return n;
|
||||
};
|
||||
|
||||
// Used to signal to callers of `readWord1` whether the word
|
||||
// contained any escape sequences. This is needed because words with
|
||||
// escape sequences must not be interpreted as keywords.
|
||||
|
||||
var containsEsc;
|
||||
|
||||
// Read an identifier, and return it as a string. Sets `containsEsc`
|
||||
// to whether the word contained a '\u' escape.
|
||||
//
|
||||
// Incrementally adds only escaped chars, adding other chunks as-is
|
||||
// as a micro-optimization.
|
||||
|
||||
pp.readWord1 = function () {
|
||||
containsEsc = false;
|
||||
var word = "",
|
||||
first = true,
|
||||
chunkStart = this.pos;
|
||||
var astral = this.options.ecmaVersion >= 6;
|
||||
while (this.pos < this.input.length) {
|
||||
var ch = this.fullCharCodeAtPos();
|
||||
if (isIdentifierChar(ch, astral)) {
|
||||
this.pos += ch <= 65535 ? 1 : 2;
|
||||
} else if (ch === 92) {
|
||||
// "\"
|
||||
containsEsc = true;
|
||||
word += this.input.slice(chunkStart, this.pos);
|
||||
var escStart = this.pos;
|
||||
if (this.input.charCodeAt(++this.pos) != 117) // "u"
|
||||
this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX");
|
||||
++this.pos;
|
||||
var esc = this.readCodePoint();
|
||||
if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral)) this.raise(escStart, "Invalid Unicode escape");
|
||||
word += codePointToString(esc);
|
||||
chunkStart = this.pos;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
return word + this.input.slice(chunkStart, this.pos);
|
||||
};
|
||||
|
||||
// Read an identifier or keyword token. Will check for reserved
|
||||
// words when necessary.
|
||||
|
||||
pp.readWord = function () {
|
||||
var word = this.readWord1();
|
||||
var type = tt.name;
|
||||
if ((this.options.ecmaVersion >= 6 || !containsEsc) && this.isKeyword(word)) type = keywordTypes[word];
|
||||
return this.finishToken(type, word);
|
||||
};
|
||||
156
lib/acorn/src/tokentype.js
Normal file
156
lib/acorn/src/tokentype.js
Normal file
@@ -0,0 +1,156 @@
|
||||
"use strict";
|
||||
|
||||
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
|
||||
|
||||
exports.__esModule = true;
|
||||
// ## Token types
|
||||
|
||||
// The assignment of fine-grained, information-carrying type objects
|
||||
// allows the tokenizer to store the information it has about a
|
||||
// token in a way that is very cheap for the parser to look up.
|
||||
|
||||
// All token type variables start with an underscore, to make them
|
||||
// easy to recognize.
|
||||
|
||||
// The `beforeExpr` property is used to disambiguate between regular
|
||||
// expressions and divisions. It is set on all token types that can
|
||||
// be followed by an expression (thus, a slash after them would be a
|
||||
// regular expression).
|
||||
//
|
||||
// `isLoop` marks a keyword as starting a loop, which is important
|
||||
// to know when parsing a label, in order to allow or disallow
|
||||
// continue jumps to that label.
|
||||
|
||||
var TokenType = exports.TokenType = function TokenType(label) {
|
||||
var conf = arguments[1] === undefined ? {} : arguments[1];
|
||||
|
||||
_classCallCheck(this, TokenType);
|
||||
|
||||
this.label = label;
|
||||
this.keyword = conf.keyword;
|
||||
this.beforeExpr = !!conf.beforeExpr;
|
||||
this.startsExpr = !!conf.startsExpr;
|
||||
this.rightAssociative = !!conf.rightAssociative;
|
||||
this.isLoop = !!conf.isLoop;
|
||||
this.isAssign = !!conf.isAssign;
|
||||
this.prefix = !!conf.prefix;
|
||||
this.postfix = !!conf.postfix;
|
||||
this.binop = conf.binop || null;
|
||||
this.updateContext = null;
|
||||
};
|
||||
|
||||
function binop(name, prec) {
|
||||
return new TokenType(name, { beforeExpr: true, binop: prec });
|
||||
}
|
||||
var beforeExpr = { beforeExpr: true },
|
||||
startsExpr = { startsExpr: true };
|
||||
|
||||
var types = {
|
||||
num: new TokenType("num", startsExpr),
|
||||
regexp: new TokenType("regexp", startsExpr),
|
||||
string: new TokenType("string", startsExpr),
|
||||
name: new TokenType("name", startsExpr),
|
||||
eof: new TokenType("eof"),
|
||||
|
||||
// Punctuation token types.
|
||||
bracketL: new TokenType("[", { beforeExpr: true, startsExpr: true }),
|
||||
bracketR: new TokenType("]"),
|
||||
braceL: new TokenType("{", { beforeExpr: true, startsExpr: true }),
|
||||
braceR: new TokenType("}"),
|
||||
parenL: new TokenType("(", { beforeExpr: true, startsExpr: true }),
|
||||
parenR: new TokenType(")"),
|
||||
comma: new TokenType(",", beforeExpr),
|
||||
semi: new TokenType(";", beforeExpr),
|
||||
colon: new TokenType(":", beforeExpr),
|
||||
dot: new TokenType("."),
|
||||
question: new TokenType("?", beforeExpr),
|
||||
arrow: new TokenType("=>", beforeExpr),
|
||||
template: new TokenType("template"),
|
||||
ellipsis: new TokenType("...", beforeExpr),
|
||||
backQuote: new TokenType("`", startsExpr),
|
||||
dollarBraceL: new TokenType("${", { beforeExpr: true, startsExpr: true }),
|
||||
|
||||
// Operators. These carry several kinds of properties to help the
|
||||
// parser use them properly (the presence of these properties is
|
||||
// what categorizes them as operators).
|
||||
//
|
||||
// `binop`, when present, specifies that this operator is a binary
|
||||
// operator, and will refer to its precedence.
|
||||
//
|
||||
// `prefix` and `postfix` mark the operator as a prefix or postfix
|
||||
// unary operator.
|
||||
//
|
||||
// `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
|
||||
// binary operators with a very low precedence, that should result
|
||||
// in AssignmentExpression nodes.
|
||||
|
||||
eq: new TokenType("=", { beforeExpr: true, isAssign: true }),
|
||||
assign: new TokenType("_=", { beforeExpr: true, isAssign: true }),
|
||||
incDec: new TokenType("++/--", { prefix: true, postfix: true, startsExpr: true }),
|
||||
prefix: new TokenType("prefix", { beforeExpr: true, prefix: true, startsExpr: true }),
|
||||
logicalOR: binop("||", 1),
|
||||
logicalAND: binop("&&", 2),
|
||||
bitwiseOR: binop("|", 3),
|
||||
bitwiseXOR: binop("^", 4),
|
||||
bitwiseAND: binop("&", 5),
|
||||
equality: binop("==/!=", 6),
|
||||
relational: binop("</>", 7),
|
||||
bitShift: binop("<</>>", 8),
|
||||
plusMin: new TokenType("+/-", { beforeExpr: true, binop: 9, prefix: true, startsExpr: true }),
|
||||
modulo: binop("%", 10),
|
||||
star: binop("*", 10),
|
||||
slash: binop("/", 10),
|
||||
exponent: new TokenType("**", { beforeExpr: true, binop: 11, rightAssociative: true })
|
||||
};
|
||||
|
||||
exports.types = types;
|
||||
// Map keyword names to token types.
|
||||
|
||||
var keywords = {};
|
||||
|
||||
exports.keywords = keywords;
|
||||
// Succinct definitions of keyword token types
|
||||
function kw(name) {
|
||||
var options = arguments[1] === undefined ? {} : arguments[1];
|
||||
|
||||
options.keyword = name;
|
||||
keywords[name] = types["_" + name] = new TokenType(name, options);
|
||||
}
|
||||
|
||||
kw("break");
|
||||
kw("case", beforeExpr);
|
||||
kw("catch");
|
||||
kw("continue");
|
||||
kw("debugger");
|
||||
kw("default");
|
||||
kw("do", { isLoop: true });
|
||||
kw("else", beforeExpr);
|
||||
kw("finally");
|
||||
kw("for", { isLoop: true });
|
||||
kw("function");
|
||||
kw("if");
|
||||
kw("return", beforeExpr);
|
||||
kw("switch");
|
||||
kw("throw", beforeExpr);
|
||||
kw("try");
|
||||
kw("var");
|
||||
kw("let");
|
||||
kw("const");
|
||||
kw("while", { isLoop: true });
|
||||
kw("with");
|
||||
kw("new", { beforeExpr: true, startsExpr: true });
|
||||
kw("this", startsExpr);
|
||||
kw("super", startsExpr);
|
||||
kw("class");
|
||||
kw("extends", beforeExpr);
|
||||
kw("export");
|
||||
kw("import");
|
||||
kw("yield", { beforeExpr: true, startsExpr: true });
|
||||
kw("null", startsExpr);
|
||||
kw("true", startsExpr);
|
||||
kw("false", startsExpr);
|
||||
kw("in", { beforeExpr: true, binop: 7 });
|
||||
kw("instanceof", { beforeExpr: true, binop: 7 });
|
||||
kw("typeof", { beforeExpr: true, prefix: true, startsExpr: true });
|
||||
kw("void", { beforeExpr: true, prefix: true, startsExpr: true });
|
||||
kw("delete", { beforeExpr: true, prefix: true, startsExpr: true });
|
||||
16
lib/acorn/src/util.js
Normal file
16
lib/acorn/src/util.js
Normal file
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
exports.isArray = isArray;
|
||||
|
||||
// Checks if an object has a property.
|
||||
|
||||
exports.has = has;
|
||||
exports.__esModule = true;
|
||||
|
||||
function isArray(obj) {
|
||||
return Object.prototype.toString.call(obj) === "[object Array]";
|
||||
}
|
||||
|
||||
function has(obj, propName) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, propName);
|
||||
}
|
||||
19
lib/acorn/src/whitespace.js
Normal file
19
lib/acorn/src/whitespace.js
Normal file
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
|
||||
exports.isNewLine = isNewLine;
|
||||
exports.__esModule = true;
|
||||
// Matches a whole line break (where CRLF is considered a single
|
||||
// line break). Used to count lines.
|
||||
|
||||
var lineBreak = /\r\n?|\n|\u2028|\u2029/;
|
||||
exports.lineBreak = lineBreak;
|
||||
var lineBreakG = new RegExp(lineBreak.source, "g");
|
||||
|
||||
exports.lineBreakG = lineBreakG;
|
||||
|
||||
function isNewLine(code) {
|
||||
return code === 10 || code === 13 || code === 8232 || code == 8233;
|
||||
}
|
||||
|
||||
var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
|
||||
exports.nonASCIIwhitespace = nonASCIIwhitespace;
|
||||
112
lib/acorn/test/.subl38b.tmp
Normal file
112
lib/acorn/test/.subl38b.tmp
Normal file
@@ -0,0 +1,112 @@
|
||||
(function() {
|
||||
var driver, acorn;
|
||||
|
||||
if (typeof require !== "undefined") {
|
||||
driver = require("./driver.js");
|
||||
require("./tests.js");
|
||||
require("./tests-harmony.js");
|
||||
require("babel/register")
|
||||
acorn = require("../src")
|
||||
} else {
|
||||
driver = window;
|
||||
acorn = window.acorn;
|
||||
}
|
||||
|
||||
var htmlLog = typeof document === "object" && document.getElementById('log');
|
||||
var htmlGroup = htmlLog;
|
||||
|
||||
function group(name) {
|
||||
if (htmlGroup) {
|
||||
var parentGroup = htmlGroup;
|
||||
htmlGroup = document.createElement("ul");
|
||||
var item = document.createElement("li");
|
||||
item.textContent = name;
|
||||
item.appendChild(htmlGroup);
|
||||
parentGroup.appendChild(item);
|
||||
}
|
||||
if (typeof console === "object" && console.group) {
|
||||
console.group(name);
|
||||
}
|
||||
}
|
||||
|
||||
function groupEnd() {
|
||||
if (htmlGroup) {
|
||||
htmlGroup = htmlGroup.parentElement.parentElement;
|
||||
}
|
||||
if (typeof console === "object" && console.groupEnd) {
|
||||
console.groupEnd(name);
|
||||
}
|
||||
}
|
||||
|
||||
function log(title, message) {
|
||||
if (htmlGroup) {
|
||||
var elem = document.createElement("li");
|
||||
elem.innerHTML = "<b>" + title + "</b> " + message;
|
||||
htmlGroup.appendChild(elem);
|
||||
}
|
||||
if (typeof console === "object") console.log(title, message);
|
||||
}
|
||||
|
||||
var stats, modes = {
|
||||
Normal: {
|
||||
config: {
|
||||
parse: acorn.parse
|
||||
}
|
||||
},
|
||||
Loose: {
|
||||
config: {
|
||||
parse: acorn.parse_dammit,
|
||||
loose: true,
|
||||
filter: function (test) {
|
||||
var opts = test.options || {};
|
||||
if (opts.loose === false) return false;
|
||||
return (opts.ecmaVersion || 5) <= 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function report(state, code, message) {
|
||||
if (state != "ok") {++stats.failed; log(code, message);}
|
||||
++stats.testsRun;
|
||||
}
|
||||
|
||||
group("Errors");
|
||||
|
||||
for (var name in modes) {
|
||||
group(name);
|
||||
var mode = modes[name];
|
||||
stats = mode.stats = {testsRun: 0, failed: 0};
|
||||
var t0 = +new Date;
|
||||
driver.runTests(mode.config, report);
|
||||
mode.stats.duration = +new Date - t0;
|
||||
groupEnd();
|
||||
}
|
||||
|
||||
groupEnd();
|
||||
|
||||
function outputStats(name, stats) {
|
||||
log(name + ":", stats.testsRun + " tests run in " + stats.duration + "ms; " +
|
||||
(stats.failed ? stats.failed + " failures." : "all passed."));
|
||||
}
|
||||
|
||||
var total = {testsRun: 0, failed: 0, duration: 0};
|
||||
|
||||
group("Stats");
|
||||
|
||||
for (var name in modes) {
|
||||
var stats = modes[name].stats;
|
||||
outputStats(name + " parser", stats);
|
||||
for (var key in stats) total[key] += stats[key];
|
||||
}
|
||||
|
||||
outputStats("Total", total);
|
||||
|
||||
groupEnd();
|
||||
|
||||
if (total.failed && typeof process === "object") {
|
||||
process.stdout.write("", function() {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
})();
|
||||
96
lib/acorn/test/bench.html
Normal file
96
lib/acorn/test/bench.html
Normal file
@@ -0,0 +1,96 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Acorn benchmark</title>
|
||||
<script src="../acorn.js"></script>
|
||||
<script src="compare/esprima.js"></script>
|
||||
<script src="compare/traceur.js"></script>
|
||||
<script src="jquery-string.js"></script>
|
||||
<script src="codemirror-string.js"></script>
|
||||
<style>
|
||||
td { text-align: right; padding-right: 20px; }
|
||||
th { text-align: left; padding-right: 40px; }
|
||||
body { max-width: 50em; padding: 1em 2em; }
|
||||
h1 { font-size: 150%; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<h1>Acorn/Esprima/Traceur speed comparison</h1>
|
||||
|
||||
<p>This will run each of the three ES6 parsers on the source code of
|
||||
jQuery 1.11.1 and CodeMirror 3.0b1 for five seconds, and show a table
|
||||
indicating the number of lines parsed per second. Note that Traceur
|
||||
always stores location data, and is thus not fairly compared by the
|
||||
benchmark <em>without</em> location data.<p>
|
||||
|
||||
<p>Also note that having the developer tools open in Chrome, or
|
||||
Firebug in Firefox <em>heavily</em> influences the numbers you get. In
|
||||
Chrome, the effect even lingers (in the tab) after you close the
|
||||
developer tools. Load in a fresh tab to get (halfway) stable
|
||||
numbers.</p>
|
||||
|
||||
<button onclick="run(false)">Compare <strong>without</strong> location data</button>
|
||||
<button onclick="run(true)">Compare <strong>with</strong> location data</button>
|
||||
<button onclick="run(false, true)">Run only Acorn</button>
|
||||
<span id="running"></span>
|
||||
|
||||
<script>
|
||||
var sourceFileName = 'source.js';
|
||||
|
||||
function runAcorn(code, locations) {
|
||||
acorn.parse(code, {ecmaVersion: 6, locations: locations, sourceFile: sourceFileName});
|
||||
}
|
||||
function runEsprima(code, locations) {
|
||||
esprima.parse(code, {loc: locations, source: sourceFileName});
|
||||
}
|
||||
function runTraceur(code) {
|
||||
var file = new traceur.syntax.SourceFile(sourceFileName, code);
|
||||
var parser = new traceur.syntax.Parser(file);
|
||||
parser.parseScript();
|
||||
}
|
||||
|
||||
var totalLines = codemirror30.split("\n").length + jquery111.split("\n").length;
|
||||
|
||||
var nowHost = (typeof performance === 'object' && 'now' in performance) ? performance : Date;
|
||||
|
||||
function benchmark(runner, locations) {
|
||||
// Give it a chance to warm up (first runs are usually outliers)
|
||||
runner(jquery111, locations);
|
||||
runner(codemirror30, locations);
|
||||
var t0 = nowHost.now(), t1, lines = 0;
|
||||
for (;;) {
|
||||
runner(jquery111, locations);
|
||||
runner(codemirror30, locations);
|
||||
lines += totalLines;
|
||||
t1 = nowHost.now();
|
||||
if (t1 - t0 > 5000) break;
|
||||
}
|
||||
return lines / ((t1 - t0) / 1000);
|
||||
}
|
||||
|
||||
function showOutput(values) {
|
||||
var html = "<hr><table>";
|
||||
for (var i = 0; i < values.length; ++i)
|
||||
html += "<tr><th>" + values[i].name + "</td><td>" + Math.round(values[i].score) + " lines per second</td><td>" +
|
||||
Math.round(values[i].score * 100 / values[0].score) + "%</td></tr>";
|
||||
document.body.appendChild(document.createElement("div")).innerHTML = html;
|
||||
}
|
||||
|
||||
function run(locations, acornOnly) {
|
||||
var running = document.getElementById("running");
|
||||
running.innerHTML = "Running benchmark...";
|
||||
var data = [{name: "Acorn", runner: runAcorn},
|
||||
{name: "Esprima", runner: runEsprima},
|
||||
{name: "Traceur", runner: runTraceur}];
|
||||
if (acornOnly) data.length = 1;
|
||||
var pos = 0;
|
||||
function next() {
|
||||
data[pos].score = benchmark(data[pos].runner, locations);
|
||||
if (++pos == data.length) {
|
||||
running.innerHTML = "";
|
||||
showOutput(data);
|
||||
} else setTimeout(next, 100);
|
||||
}
|
||||
setTimeout(next, 50);
|
||||
}
|
||||
</script>
|
||||
1
lib/acorn/test/codemirror-string.js
Normal file
1
lib/acorn/test/codemirror-string.js
Normal file
File diff suppressed because one or more lines are too long
1
lib/acorn/test/compare/esprima.js
Normal file
1
lib/acorn/test/compare/esprima.js
Normal file
File diff suppressed because one or more lines are too long
1
lib/acorn/test/compare/traceur.js
Normal file
1
lib/acorn/test/compare/traceur.js
Normal file
File diff suppressed because one or more lines are too long
114
lib/acorn/test/driver.js
Normal file
114
lib/acorn/test/driver.js
Normal file
@@ -0,0 +1,114 @@
|
||||
"use strict";
|
||||
|
||||
(function (exports) {
|
||||
var tests = [];
|
||||
|
||||
exports.test = function (code, ast, options) {
|
||||
tests.push({ code: code, ast: ast, options: options });
|
||||
};
|
||||
exports.testFail = function (code, message, options) {
|
||||
tests.push({ code: code, error: message, options: options });
|
||||
};
|
||||
exports.testAssert = function (code, assert, options) {
|
||||
tests.push({ code: code, assert: assert, options: options });
|
||||
};
|
||||
|
||||
exports.runTests = function (config, callback) {
|
||||
var parse = config.parse;
|
||||
|
||||
for (var i = 0; i < tests.length; ++i) {
|
||||
var test = tests[i];
|
||||
if (config.filter && !config.filter(test)) continue;
|
||||
var testOpts = test.options || { locations: true };
|
||||
var expected = {};
|
||||
if (expected.onComment = testOpts.onComment) testOpts.onComment = [];
|
||||
if (expected.onToken = testOpts.onToken) testOpts.onToken = [];
|
||||
|
||||
try {
|
||||
var ast = parse(test.code, testOpts);
|
||||
} catch (e) {
|
||||
if (!(e instanceof SyntaxError)) {
|
||||
console.log(e.stack);throw e;
|
||||
}
|
||||
if (test.error) {
|
||||
if (e.message == test.error) callback("ok", test.code);else callback("fail", test.code, "Expected error message: " + test.error + "\nGot error message: " + e.message);
|
||||
} else {
|
||||
callback("error", test.code, e.message || e.toString());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (test.error) {
|
||||
if (config.loose) callback("ok", test.code);else callback("fail", test.code, "Expected error message: " + test.error + "\nBut parsing succeeded.");
|
||||
} else if (test.assert) {
|
||||
var error = test.assert(ast);
|
||||
if (error) callback("fail", test.code, "\n Assertion failed:\n " + error);else callback("ok", test.code);
|
||||
} else {
|
||||
var mis = misMatch(test.ast, ast);
|
||||
for (var name in expected) {
|
||||
if (mis) break;
|
||||
if (expected[name]) {
|
||||
mis = misMatch(expected[name], testOpts[name]);
|
||||
testOpts[name] = expected[name];
|
||||
}
|
||||
}
|
||||
if (mis) callback("fail", test.code, mis);else callback("ok", test.code);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function ppJSON(v) {
|
||||
return v instanceof RegExp ? v.toString() : JSON.stringify(v, null, 2);
|
||||
}
|
||||
function addPath(str, pt) {
|
||||
if (str.charAt(str.length - 1) == ")") return str.slice(0, str.length - 1) + "/" + pt + ")";
|
||||
return str + " (" + pt + ")";
|
||||
}
|
||||
|
||||
var misMatch = exports.misMatch = function (exp, act) {
|
||||
if (!exp || !act || typeof exp != "object" || typeof act != "object") {
|
||||
if (exp !== act && typeof exp != "function") return ppJSON(exp) + " !== " + ppJSON(act);
|
||||
} else if (exp instanceof RegExp || act instanceof RegExp) {
|
||||
var left = ppJSON(exp),
|
||||
right = ppJSON(act);
|
||||
if (left !== right) return left + " !== " + right;
|
||||
} else if (exp.splice) {
|
||||
if (!act.slice) return ppJSON(exp) + " != " + ppJSON(act);
|
||||
if (act.length != exp.length) return "array length mismatch " + exp.length + " != " + act.length;
|
||||
for (var i = 0; i < act.length; ++i) {
|
||||
var mis = misMatch(exp[i], act[i]);
|
||||
if (mis) return addPath(mis, i);
|
||||
}
|
||||
} else {
|
||||
for (var prop in exp) {
|
||||
var mis = misMatch(exp[prop], act[prop]);
|
||||
if (mis) return addPath(mis, prop);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function mangle(ast) {
|
||||
if (typeof ast != "object" || !ast) return;
|
||||
if (ast.slice) {
|
||||
for (var i = 0; i < ast.length; ++i) mangle(ast[i]);
|
||||
} else {
|
||||
var loc = ast.start && ast.end && { start: ast.start, end: ast.end };
|
||||
if (loc) {
|
||||
delete ast.start;delete ast.end;
|
||||
}
|
||||
for (var name in ast) if (ast.hasOwnProperty(name)) mangle(ast[name]);
|
||||
if (loc) ast.loc = loc;
|
||||
}
|
||||
}
|
||||
|
||||
exports.printTests = function () {
|
||||
var out = "";
|
||||
for (var i = 0; i < tests.length; ++i) {
|
||||
if (tests[i].error) continue;
|
||||
mangle(tests[i].ast);
|
||||
out += "test(" + JSON.stringify(tests[i].code) + ", " + JSON.stringify(tests[i].ast, null, 2) + ");\n\n";
|
||||
}
|
||||
document.body.innerHTML = "";
|
||||
document.body.appendChild(document.createElement("pre")).appendChild(document.createTextNode(out));
|
||||
};
|
||||
})(typeof exports == "undefined" ? window : exports);
|
||||
14
lib/acorn/test/index.html
Normal file
14
lib/acorn/test/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Acorn test suite</title>
|
||||
<script src="../dist/acorn.js"></script>
|
||||
<script src="../dist/acorn_loose.js"></script>
|
||||
<script src="driver.js"></script>
|
||||
<script src="tests.js" charset="utf-8"></script>
|
||||
<script src="tests-harmony.js" charset="utf-8"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ul id="log"></ul>
|
||||
<script src="run.js"></script>
|
||||
</body>
|
||||
1
lib/acorn/test/jquery-string.js
vendored
Normal file
1
lib/acorn/test/jquery-string.js
vendored
Normal file
File diff suppressed because one or more lines are too long
108
lib/acorn/test/run.js
Normal file
108
lib/acorn/test/run.js
Normal file
@@ -0,0 +1,108 @@
|
||||
"use strict";
|
||||
|
||||
(function () {
|
||||
var driver, acorn;
|
||||
|
||||
if (typeof require !== "undefined") {
|
||||
driver = require("./driver.js");
|
||||
require("./tests.js");
|
||||
require("./tests-harmony.js");
|
||||
require("./tests-flow.js");
|
||||
require("./tests-jsx.js");
|
||||
require("./tests-babel.js");
|
||||
require("babel/register");
|
||||
acorn = require("../src");
|
||||
} else {
|
||||
driver = window;
|
||||
acorn = window.acorn;
|
||||
}
|
||||
|
||||
var htmlLog = typeof document === "object" && document.getElementById("log");
|
||||
var htmlGroup = htmlLog;
|
||||
|
||||
function group(name) {
|
||||
if (htmlGroup) {
|
||||
var parentGroup = htmlGroup;
|
||||
htmlGroup = document.createElement("ul");
|
||||
var item = document.createElement("li");
|
||||
item.textContent = name;
|
||||
item.appendChild(htmlGroup);
|
||||
parentGroup.appendChild(item);
|
||||
}
|
||||
if (typeof console === "object" && console.group) {
|
||||
console.group(name);
|
||||
}
|
||||
}
|
||||
|
||||
function groupEnd() {
|
||||
if (htmlGroup) {
|
||||
htmlGroup = htmlGroup.parentElement.parentElement;
|
||||
}
|
||||
if (typeof console === "object" && console.groupEnd) {
|
||||
console.groupEnd(name);
|
||||
}
|
||||
}
|
||||
|
||||
function log(title, message) {
|
||||
if (htmlGroup) {
|
||||
var elem = document.createElement("li");
|
||||
elem.innerHTML = "<b>" + title + "</b> " + message;
|
||||
htmlGroup.appendChild(elem);
|
||||
}
|
||||
if (typeof console === "object") console.log(title, message);
|
||||
}
|
||||
|
||||
var stats,
|
||||
modes = {
|
||||
Normal: {
|
||||
config: {
|
||||
parse: acorn.parse
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function report(state, code, message) {
|
||||
if (state != "ok") {
|
||||
++stats.failed;log(code, message);
|
||||
}
|
||||
++stats.testsRun;
|
||||
}
|
||||
|
||||
group("Errors");
|
||||
|
||||
for (var name in modes) {
|
||||
group(name);
|
||||
var mode = modes[name];
|
||||
stats = mode.stats = { testsRun: 0, failed: 0 };
|
||||
var t0 = +new Date();
|
||||
driver.runTests(mode.config, report);
|
||||
mode.stats.duration = +new Date() - t0;
|
||||
groupEnd();
|
||||
}
|
||||
|
||||
groupEnd();
|
||||
|
||||
function outputStats(name, stats) {
|
||||
log(name + ":", stats.testsRun + " tests run in " + stats.duration + "ms; " + (stats.failed ? stats.failed + " failures." : "all passed."));
|
||||
}
|
||||
|
||||
var total = { testsRun: 0, failed: 0, duration: 0 };
|
||||
|
||||
group("Stats");
|
||||
|
||||
for (var name in modes) {
|
||||
var stats = modes[name].stats;
|
||||
outputStats(name + " parser", stats);
|
||||
for (var key in stats) total[key] += stats[key];
|
||||
}
|
||||
|
||||
outputStats("Total", total);
|
||||
|
||||
groupEnd();
|
||||
|
||||
if (total.failed && typeof process === "object") {
|
||||
process.stdout.write("", function () {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
})();
|
||||
1894
lib/acorn/test/tests-babel.js
Normal file
1894
lib/acorn/test/tests-babel.js
Normal file
File diff suppressed because it is too large
Load Diff
1
lib/acorn/test/tests-flow.js
Normal file
1
lib/acorn/test/tests-flow.js
Normal file
File diff suppressed because one or more lines are too long
1
lib/acorn/test/tests-harmony.js
Normal file
1
lib/acorn/test/tests-harmony.js
Normal file
File diff suppressed because one or more lines are too long
3239
lib/acorn/test/tests-jsx.js
Normal file
3239
lib/acorn/test/tests-jsx.js
Normal file
File diff suppressed because it is too large
Load Diff
1
lib/acorn/test/tests.js
Normal file
1
lib/acorn/test/tests.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user