Begin transition of Babel to a more scalable architecture, async flow to allow for RPC and better build system for multiple packages

This commit is contained in:
Sebastian McKenzie 2015-07-11 12:39:54 +01:00
parent 04a29f8344
commit 423d8c510d
33 changed files with 66106 additions and 2805 deletions

32
AUTHORS
View File

@ -1,32 +0,0 @@
List of Acorn contributors. Updated before every release.
Alistair Braidwood
Aparajita Fishman
Arian Stolwijk
Artem Govorov
Brandon Mills
Charles Hughes
Conrad Irwin
David Bonnet
impinball
Ingvar Stepanyan
Jiaxing Wang
Johannes Herr
Jürg Lehni
keeyipchan
krator
Marijn Haverbeke
Martin Carlberg
Mathias Bynens
Mathieu 'p01' Henri
Max Schaefer
Mihai Bazon
Mike Rennie
Oskar Schöldström
Paul Harper
Peter Rust
PlNG
r-e-d
Rich Harris
Sebastian McKenzie
zsjforcn

19
LICENSE
View File

@ -1,19 +0,0 @@
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

7
README.md Normal file
View File

@ -0,0 +1,7 @@
<p align="center">
<img alt="babylon" src="https://raw.githubusercontent.com/babel/logo/master/babylon.png" width="700">
</p>
<p align="center">
Babylon is a streaming parser for <a href="https://github.com/babel/babel">Babel</a>.
</p>

View File

@ -1,6 +0,0 @@
export * from "./src/index";
import "./plugins/flow";
import inject from "acorn-jsx/inject";
import * as acorn from "./src/index";
inject(acorn);

50
package.json Executable file → Normal file
View File

@ -1,44 +1,12 @@
{
"name": "acorn",
"description": "ECMAScript parser",
"homepage": "https://github.com/marijnh/acorn",
"main": "index.js",
"version": "1.0.0",
"engines": {
"node": ">=0.4.0"
},
"maintainers": [
{
"name": "Marijn Haverbeke",
"email": "marijnh@gmail.com",
"web": "http://marijnhaverbeke.nl"
},
{
"name": "Ingvar Stepanyan",
"email": "me@rreverser.com",
"web": "http://rreverser.com/"
}
],
"repository": {
"type": "git",
"url": "https://github.com/marijnh/acorn.git"
},
"licenses": [
{
"type": "MIT",
"url": "https://raw.githubusercontent.com/marijnh/acorn/master/LICENSE"
}
],
"scripts": {
"test": "node test/run.js",
"prepublish": "node bin/prepublish.sh"
},
"bin": {
"acorn": "./bin/acorn"
},
"devDependencies": {
"babelify": "^5.0.4",
"browserify": "^9.0.3",
"unicode-7.0.0": "~0.1.5"
"name": "babylon",
"description": "",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://babeljs.io/",
"license": "MIT",
"repository": "babel/babel",
"main": "lib/index.js",
"dependencies": {
"bluebird": "^2.9.33"
}
}

View File

@ -1,830 +0,0 @@
var acorn = require("../src/index")
var pp = acorn.Parser.prototype
var tt = acorn.tokTypes
pp.isRelational = function (op) {
return this.type === tt.relational && this.value === op
}
pp.expectRelational = function (op) {
if (this.isRelational(op)) {
this.next()
} else {
this.unexpected()
}
}
pp.flow_parseTypeInitialiser = function (tok) {
var oldInType = this.inType
this.inType = true
this.expect(tok || tt.colon)
var type = this.flow_parseType()
this.inType = oldInType
return type;
}
pp.flow_parseDeclareClass = function (node) {
this.next()
this.flow_parseInterfaceish(node, true)
return this.finishNode(node, "DeclareClass")
}
pp.flow_parseDeclareFunction = function (node) {
this.next()
var id = node.id = this.parseIdent()
var typeNode = this.startNode()
var typeContainer = this.startNode()
if (this.isRelational("<")) {
typeNode.typeParameters = this.flow_parseTypeParameterDeclaration()
} else {
typeNode.typeParameters = null
}
this.expect(tt.parenL)
var tmp = this.flow_parseFunctionTypeParams()
typeNode.params = tmp.params
typeNode.rest = tmp.rest
this.expect(tt.parenR)
typeNode.returnType = this.flow_parseTypeInitialiser()
typeContainer.typeAnnotation = this.finishNode(typeNode, "FunctionTypeAnnotation")
id.typeAnnotation = this.finishNode(typeContainer, "TypeAnnotation")
this.finishNode(id, id.type)
this.semicolon()
return this.finishNode(node, "DeclareFunction")
}
pp.flow_parseDeclare = function (node) {
if (this.type === tt._class) {
return this.flow_parseDeclareClass(node)
} else if (this.type === tt._function) {
return this.flow_parseDeclareFunction(node)
} else if (this.type === tt._var) {
return this.flow_parseDeclareVariable(node)
} else if (this.isContextual("module")) {
return this.flow_parseDeclareModule(node)
} else {
this.unexpected()
}
}
pp.flow_parseDeclareVariable = function (node) {
this.next()
node.id = this.flow_parseTypeAnnotatableIdentifier()
this.semicolon()
return this.finishNode(node, "DeclareVariable")
}
pp.flow_parseDeclareModule = function (node) {
this.next()
if (this.type === tt.string) {
node.id = this.parseExprAtom()
} else {
node.id = this.parseIdent()
}
var bodyNode = node.body = this.startNode()
var body = bodyNode.body = []
this.expect(tt.braceL)
while (this.type !== tt.braceR) {
var node2 = this.startNode()
// todo: declare check
this.next()
body.push(this.flow_parseDeclare(node2))
}
this.expect(tt.braceR)
this.finishNode(bodyNode, "BlockStatement")
return this.finishNode(node, "DeclareModule")
}
// Interfaces
pp.flow_parseInterfaceish = function (node, allowStatic) {
node.id = this.parseIdent()
if (this.isRelational("<")) {
node.typeParameters = this.flow_parseTypeParameterDeclaration()
} else {
node.typeParameters = null
}
node.extends = []
if (this.eat(tt._extends)) {
do {
node.extends.push(this.flow_parseInterfaceExtends())
} while(this.eat(tt.comma))
}
node.body = this.flow_parseObjectType(allowStatic)
}
pp.flow_parseInterfaceExtends = function () {
var node = this.startNode()
node.id = this.parseIdent()
if (this.isRelational("<")) {
node.typeParameters = this.flow_parseTypeParameterInstantiation()
} else {
node.typeParameters = null
}
return this.finishNode(node, "InterfaceExtends")
}
pp.flow_parseInterface = function (node) {
this.flow_parseInterfaceish(node, false)
return this.finishNode(node, "InterfaceDeclaration")
}
// Type aliases
pp.flow_parseTypeAlias = function (node) {
node.id = this.parseIdent()
if (this.isRelational("<")) {
node.typeParameters = this.flow_parseTypeParameterDeclaration()
} else {
node.typeParameters = null
}
node.right = this.flow_parseTypeInitialiser(tt.eq)
this.semicolon()
return this.finishNode(node, "TypeAlias")
}
// Type annotations
pp.flow_parseTypeParameterDeclaration = function () {
var node = this.startNode()
node.params = []
this.expectRelational("<")
while (!this.isRelational(">")) {
node.params.push(this.flow_parseTypeAnnotatableIdentifier())
if (!this.isRelational(">")) {
this.expect(tt.comma)
}
}
this.expectRelational(">")
return this.finishNode(node, "TypeParameterDeclaration")
}
pp.flow_parseTypeParameterInstantiation = function () {
var node = this.startNode(), oldInType = this.inType
node.params = []
this.inType = true
this.expectRelational("<")
while (!this.isRelational(">")) {
node.params.push(this.flow_parseType())
if (!this.isRelational(">")) {
this.expect(tt.comma)
}
}
this.expectRelational(">")
this.inType = oldInType
return this.finishNode(node, "TypeParameterInstantiation")
}
pp.flow_parseObjectPropertyKey = function () {
return (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true)
}
pp.flow_parseObjectTypeIndexer = function (node, isStatic) {
node.static = isStatic
this.expect(tt.bracketL)
node.id = this.flow_parseObjectPropertyKey()
node.key = this.flow_parseTypeInitialiser()
this.expect(tt.bracketR)
node.value = this.flow_parseTypeInitialiser()
this.flow_objectTypeSemicolon()
return this.finishNode(node, "ObjectTypeIndexer")
}
pp.flow_parseObjectTypeMethodish = function (node) {
node.params = []
node.rest = null
node.typeParameters = null
if (this.isRelational("<")) {
node.typeParameters = this.flow_parseTypeParameterDeclaration()
}
this.expect(tt.parenL)
while (this.type === tt.name) {
node.params.push(this.flow_parseFunctionTypeParam())
if (this.type !== tt.parenR) {
this.expect(tt.comma)
}
}
if (this.eat(tt.ellipsis)) {
node.rest = this.flow_parseFunctionTypeParam()
}
this.expect(tt.parenR)
node.returnType = this.flow_parseTypeInitialiser()
return this.finishNode(node, "FunctionTypeAnnotation")
}
pp.flow_parseObjectTypeMethod = function (start, isStatic, key) {
var node = this.startNodeAt(start)
node.value = this.flow_parseObjectTypeMethodish(this.startNodeAt(start))
node.static = isStatic
node.key = key
node.optional = false
this.flow_objectTypeSemicolon()
return this.finishNode(node, "ObjectTypeProperty")
}
pp.flow_parseObjectTypeCallProperty = function (node, isStatic) {
var valueNode = this.startNode()
node.static = isStatic
node.value = this.flow_parseObjectTypeMethodish(valueNode)
this.flow_objectTypeSemicolon()
return this.finishNode(node, "ObjectTypeCallProperty")
}
pp.flow_parseObjectType = function (allowStatic) {
var nodeStart = this.startNode()
var node
var optional = false
var property
var propertyKey
var propertyTypeAnnotation
var token
var isStatic
nodeStart.callProperties = []
nodeStart.properties = []
nodeStart.indexers = []
this.expect(tt.braceL)
while (this.type !== tt.braceR) {
var start = this.markPosition()
node = this.startNode()
if (allowStatic && this.isContextual("static")) {
this.next()
isStatic = true
}
if (this.type === tt.bracketL) {
nodeStart.indexers.push(this.flow_parseObjectTypeIndexer(node, isStatic))
} else if (this.type === tt.parenL || this.isRelational("<")) {
nodeStart.callProperties.push(this.flow_parseObjectTypeCallProperty(node, allowStatic))
} else {
if (isStatic && this.type === tt.colon) {
propertyKey = this.parseIdent()
} else {
propertyKey = this.flow_parseObjectPropertyKey()
}
if (this.isRelational("<") || this.type === tt.parenL) {
// This is a method property
nodeStart.properties.push(this.flow_parseObjectTypeMethod(start, isStatic, propertyKey))
} else {
if (this.eat(tt.question)) {
optional = true
}
node.key = propertyKey
node.value = this.flow_parseTypeInitialiser()
node.optional = optional
node.static = isStatic
this.flow_objectTypeSemicolon()
nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty"))
}
}
}
this.expect(tt.braceR)
return this.finishNode(nodeStart, "ObjectTypeAnnotation")
}
pp.flow_objectTypeSemicolon = function () {
if (!this.eat(tt.semi) && !this.eat(tt.comma) && this.type !== tt.braceR) {
this.unexpected()
}
}
pp.flow_parseGenericType = function (start, id) {
var node = this.startNodeAt(start)
node.typeParameters = null
node.id = id
while (this.eat(tt.dot)) {
var node2 = this.startNodeAt(start)
node2.qualification = node.id
node2.id = this.parseIdent()
node.id = this.finishNode(node2, "QualifiedTypeIdentifier")
}
if (this.isRelational("<")) {
node.typeParameters = this.flow_parseTypeParameterInstantiation()
}
return this.finishNode(node, "GenericTypeAnnotation")
}
pp.flow_parseTypeofType = function () {
var node = this.startNode()
this.expect(tt._typeof)
node.argument = this.flow_parsePrimaryType()
return this.finishNode(node, "TypeofTypeAnnotation")
}
pp.flow_parseTupleType = function () {
var node = this.startNode()
node.types = []
this.expect(tt.bracketL)
// We allow trailing commas
while (this.pos < this.input.length && this.type !== tt.bracketR) {
node.types.push(this.flow_parseType())
if (this.type === tt.bracketR) break
this.expect(tt.comma)
}
this.expect(tt.bracketR)
return this.finishNode(node, "TupleTypeAnnotation")
}
pp.flow_parseFunctionTypeParam = function () {
var optional = false
var node = this.startNode()
node.name = this.parseIdent()
if (this.eat(tt.question)) {
optional = true
}
node.optional = optional
node.typeAnnotation = this.flow_parseTypeInitialiser()
return this.finishNode(node, "FunctionTypeParam")
}
pp.flow_parseFunctionTypeParams = function () {
var ret = { params: [], rest: null }
while (this.type === tt.name) {
ret.params.push(this.flow_parseFunctionTypeParam())
if (this.type !== tt.parenR) {
this.expect(tt.comma)
}
}
if (this.eat(tt.ellipsis)) {
ret.rest = this.flow_parseFunctionTypeParam()
}
return ret
}
pp.flow_identToTypeAnnotation = function (start, node, id) {
switch (id.name) {
case "any":
return this.finishNode(node, "AnyTypeAnnotation")
case "void":
return this.finishNode(node, "VoidTypeAnnotation")
case "bool":
case "boolean":
return this.finishNode(node, "BooleanTypeAnnotation")
case "mixed":
return this.finishNode(node, "MixedTypeAnnotation")
case "number":
return this.finishNode(node, "NumberTypeAnnotation")
case "string":
return this.finishNode(node, "StringTypeAnnotation")
default:
return this.flow_parseGenericType(start, id)
}
}
// The parsing of types roughly parallels the parsing of expressions, and
// primary types are kind of like primary expressions...they're the
// primitives with which other types are constructed.
pp.flow_parsePrimaryType = function () {
var typeIdentifier = null
var params = null
var returnType = null
var start = this.markPosition()
var node = this.startNode()
var rest = null
var tmp
var typeParameters
var token
var type
var isGroupedType = false
switch (this.type) {
case tt.name:
return this.flow_identToTypeAnnotation(start, node, this.parseIdent())
case tt.braceL:
return this.flow_parseObjectType()
case tt.bracketL:
return this.flow_parseTupleType()
case tt.relational:
if (this.value === "<") {
node.typeParameters = this.flow_parseTypeParameterDeclaration()
this.expect(tt.parenL)
tmp = this.flow_parseFunctionTypeParams()
node.params = tmp.params
node.rest = tmp.rest
this.expect(tt.parenR)
this.expect(tt.arrow)
node.returnType = this.flow_parseType()
return this.finishNode(node, "FunctionTypeAnnotation")
}
case tt.parenL:
this.next()
// Check to see if this is actually a grouped type
if (this.type !== tt.parenR && this.type !== tt.ellipsis) {
if (this.type === tt.name) {
var token = this.lookahead().type
isGroupedType = token !== tt.question && token !== tt.colon
} else {
isGroupedType = true
}
}
if (isGroupedType) {
type = this.flow_parseType()
this.expect(tt.parenR)
// If we see a => next then someone was probably confused about
// function types, so we can provide a better error message
if (this.eat(tt.arrow)) {
this.raise(node,
"Unexpected token =>. It looks like " +
"you are trying to write a function type, but you ended up " +
"writing a grouped type followed by an =>, which is a syntax " +
"error. Remember, function type parameters are named so function " +
"types look like (name1: type1, name2: type2) => returnType. You " +
"probably wrote (type1) => returnType"
)
}
return type
}
tmp = this.flow_parseFunctionTypeParams()
node.params = tmp.params
node.rest = tmp.rest
this.expect(tt.parenR)
this.expect(tt.arrow)
node.returnType = this.flow_parseType()
node.typeParameters = null
return this.finishNode(node, "FunctionTypeAnnotation")
case tt.string:
node.value = this.value
node.raw = this.input.slice(this.start, this.end)
this.next()
return this.finishNode(node, "StringLiteralTypeAnnotation")
default:
if (this.type.keyword === "typeof") {
return this.flow_parseTypeofType()
}
}
this.unexpected()
}
pp.flow_parsePostfixType = function () {
var node = this.startNode()
var type = node.elementType = this.flow_parsePrimaryType()
if (this.type === tt.bracketL) {
this.expect(tt.bracketL)
this.expect(tt.bracketR)
return this.finishNode(node, "ArrayTypeAnnotation")
}
return type
}
pp.flow_parsePrefixType = function () {
var node = this.startNode()
if (this.eat(tt.question)) {
node.typeAnnotation = this.flow_parsePrefixType()
return this.finishNode(node, "NullableTypeAnnotation")
}
return this.flow_parsePostfixType()
}
pp.flow_parseIntersectionType = function () {
var node = this.startNode()
var type = this.flow_parsePrefixType()
node.types = [type]
while (this.eat(tt.bitwiseAND)) {
node.types.push(this.flow_parsePrefixType())
}
return node.types.length === 1 ? type : this.finishNode(node, "IntersectionTypeAnnotation")
}
pp.flow_parseUnionType = function () {
var node = this.startNode()
var type = this.flow_parseIntersectionType()
node.types = [type]
while (this.eat(tt.bitwiseOR)) {
node.types.push(this.flow_parseIntersectionType())
}
return node.types.length === 1 ? type : this.finishNode(node, "UnionTypeAnnotation")
}
pp.flow_parseType = function () {
var oldInType = this.inType
this.inType = true
var type = this.flow_parseUnionType()
this.inType = oldInType
return type
}
pp.flow_parseTypeAnnotation = function () {
var node = this.startNode()
node.typeAnnotation = this.flow_parseTypeInitialiser()
return this.finishNode(node, "TypeAnnotation")
}
pp.flow_parseTypeAnnotatableIdentifier = function (requireTypeAnnotation, canBeOptionalParam) {
var node = this.startNode()
var ident = this.parseIdent()
var isOptionalParam = false
if (canBeOptionalParam && this.eat(tt.question)) {
this.expect(tt.question)
isOptionalParam = true
}
if (requireTypeAnnotation || this.type === tt.colon) {
ident.typeAnnotation = this.flow_parseTypeAnnotation()
this.finishNode(ident, ident.type)
}
if (isOptionalParam) {
ident.optional = true
this.finishNode(ident, ident.type)
}
return ident
}
acorn.plugins.flow = function (instance) {
// function name(): string {}
instance.extend("parseFunctionBody", function (inner) {
return function (node, allowExpression) {
if (this.type === tt.colon) {
node.returnType = this.flow_parseTypeAnnotation()
}
return inner.call(this, node, allowExpression)
}
})
instance.extend("parseStatement", function (inner) {
return function(declaration, topLevel) {
// strict mode handling of `interface` since it's a reserved word
if (this.strict && this.type === tt.name && this.value === "interface") {
var node = this.startNode()
this.next()
return this.flow_parseInterface(node)
} else {
return inner.call(this, declaration, topLevel)
}
}
})
instance.extend("parseExpressionStatement", function (inner) {
return function (node, expr) {
if (expr.type === "Identifier") {
if (expr.name === "declare") {
if (this.type === tt._class || this.type === tt.name || this.type === tt._function || this.type === tt._var) {
return this.flow_parseDeclare(node)
}
} else if (this.type === tt.name) {
if (expr.name === "interface") {
return this.flow_parseInterface(node)
} else if (expr.name === "type") {
return this.flow_parseTypeAlias(node)
}
}
}
return inner.call(this, node, expr)
}
})
instance.extend("shouldParseExportDeclaration", function (inner) {
return function () {
return this.isContextual("type") || inner.call(this)
}
})
instance.extend("parseParenItem", function (inner) {
return function (node, start) {
if (this.type === tt.colon) {
var typeCastNode = this.startNodeAt(start)
typeCastNode.expression = node
typeCastNode.typeAnnotation = this.flow_parseTypeAnnotation()
return this.finishNode(typeCastNode, "TypeCastExpression")
} else {
return node
}
}
})
instance.extend("parseClassId", function (inner) {
return function (node, isStatement) {
inner.call(this, node, isStatement)
if (this.isRelational("<")) {
node.typeParameters = this.flow_parseTypeParameterDeclaration()
}
}
})
// don't consider `void` to be a keyword as then it'll use the void token type
// and set startExpr
instance.extend("isKeyword", function (inner) {
return function(name) {
if (this.inType && name === "void") {
return false
} else {
return inner.call(this, name)
}
}
})
instance.extend("readToken", function (inner) {
return function(code) {
if (this.inType && (code === 62 || code === 60)) {
return this.finishOp(tt.relational, 1)
} else {
return inner.call(this, code)
}
}
})
instance.extend("jsx_readToken", function (inner) {
return function () {
if (!this.inType) return inner.call(this)
}
})
instance.extend("parseParenArrowList", function (inner) {
return function (start, exprList, isAsync) {
for (var i = 0; i < exprList.length; i++) {
var listItem = exprList[i]
if (listItem.type === "TypeCastExpression") {
var expr = listItem.expression
expr.typeAnnotation = listItem.typeAnnotation
exprList[i] = expr
}
}
return inner.call(this, start, exprList, isAsync)
}
})
instance.extend("parseClassProperty", function (inner) {
return function (node) {
if (this.type === tt.colon) {
node.typeAnnotation = this.flow_parseTypeAnnotation()
}
return inner.call(this, node)
}
})
instance.extend("isClassProperty", function (inner) {
return function () {
return this.type === tt.colon || inner.call(this)
}
})
instance.extend("parseClassMethod", function (inner) {
return function (classBody, method, isGenerator, isAsync) {
var typeParameters
if (this.isRelational("<")) {
typeParameters = this.flow_parseTypeParameterDeclaration()
}
method.value = this.parseMethod(isGenerator, isAsync)
method.value.typeParameters = typeParameters
classBody.body.push(this.finishNode(method, "MethodDefinition"))
}
})
instance.extend("parseClassSuper", function (inner) {
return function (node, isStatement) {
inner.call(this, node, isStatement)
if (node.superClass && this.isRelational("<")) {
node.superTypeParameters = this.flow_parseTypeParameterInstantiation()
}
if (this.isContextual("implements")) {
this.next()
var implemented = node.implements = []
do {
var node = this.startNode()
node.id = this.parseIdent()
if (this.isRelational("<")) {
node.typeParameters = this.flow_parseTypeParameterInstantiation()
} else {
node.typeParameters = null
}
implemented.push(this.finishNode(node, "ClassImplements"))
} while(this.eat(tt.comma))
}
}
})
instance.extend("parseObjPropValue", function (inner) {
return function (prop) {
var typeParameters
if (this.isRelational("<")) {
typeParameters = this.flow_parseTypeParameterDeclaration()
if (this.type !== tt.parenL) this.unexpected()
}
inner.apply(this, arguments)
prop.value.typeParameters = typeParameters
}
})
instance.extend("parseAssignableListItemTypes", function (inner) {
return function (param) {
if (this.eat(tt.question)) {
param.optional = true
}
if (this.type === tt.colon) {
param.typeAnnotation = this.flow_parseTypeAnnotation()
}
this.finishNode(param, param.type)
return param
}
})
instance.extend("parseImportSpecifiers", function (inner) {
return function (node) {
node.isType = false
if (this.isContextual("type")) {
var start = this.markPosition()
var typeId = this.parseIdent()
if ((this.type === tt.name && this.value !== "from") || this.type === tt.braceL || this.type === tt.star) {
node.isType = true
} else {
node.specifiers.push(this.parseImportSpecifierDefault(typeId, start))
if (this.isContextual("from")) return
this.eat(tt.comma)
}
}
inner.call(this, node)
}
})
// function foo<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)
}
}
})
}

File diff suppressed because it is too large Load Diff

View File

@ -8,10 +8,10 @@
// It starts by sorting the words by length.
function makePredicate(words) {
words = words.split(" ")
return function(str) {
return words.indexOf(str) >= 0
}
words = words.split(" ");
return function (str) {
return words.indexOf(str) >= 0;
};
}
// Reserved word lists for various dialects of the language
@ -22,16 +22,16 @@ export const reservedWords = {
6: makePredicate("enum await"),
strict: makePredicate("implements interface let package private protected public static yield"),
strictBind: makePredicate("eval arguments")
}
};
// And the keywords
var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"
var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";
export const keywords = {
5: makePredicate(ecma5AndLessKeywords),
6: makePredicate(ecma5AndLessKeywords + " let const class extends export import yield super")
}
};
// ## Character categories
@ -41,57 +41,75 @@ export const keywords = {
// code point above 128.
// Generated by `tools/generate-identifier-regex.js`.
let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b2\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"
let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2d\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"
let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b2\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2d\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";
const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]")
const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]")
const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
// These are a run-length and offset encoded representation of the
// >0xffff code points that are a valid part of identifiers. The
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by tools/generate-identifier-regex.js
var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541]
var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239]
var astralIdentifierStartCodes = [
0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 17, 26, 6, 37, 11, 29, 3, 35, 5,
7, 2, 4, 43, 157, 99, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30,
98, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 26, 45, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14,
17, 111, 72, 955, 52, 76, 44, 33, 24, 27, 35, 42, 34, 4, 0, 13, 47, 15, 3, 22, 0, 38, 17, 2, 24, 133, 46, 39, 7,
3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 32, 4, 287, 47, 21, 1, 2, 0, 185, 46, 82, 47, 21, 0, 60, 42, 502, 63, 32, 0,
449, 56, 1288, 920, 104, 110, 2962, 1070, 13266, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 881, 68, 12,
0, 67, 12, 16481, 1, 3071, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2,
64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2,
24, 2, 30, 2, 24, 2, 7, 4149, 196, 1340, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2,
0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2,
4, 2, 16, 4421, 42710, 42, 4148, 12, 221, 16355, 541
];
var astralIdentifierCodes = [
509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 1306, 2, 54, 14, 32, 9, 16, 3, 46, 10,
54, 9, 7, 2, 37, 13, 2, 9, 52, 0, 13, 2, 49, 13, 16, 9, 83, 11, 168, 11, 6, 9, 8, 2, 57, 0, 2, 6, 3, 1, 3, 2, 10,
0, 11, 1, 3, 6, 4, 4, 316, 19, 13, 9, 214, 6, 3, 8, 112, 16, 16, 9, 82, 12, 9, 9, 535, 9, 20855, 9, 135, 4, 60, 6,
26, 9, 1016, 45, 17, 3, 19723, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 4305, 6, 792618, 239
];
// This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.
function isInAstralSet(code, set) {
let pos = 0x10000
let pos = 0x10000;
for (let i = 0; i < set.length; i += 2) {
pos += set[i]
if (pos > code) return false
pos += set[i + 1]
if (pos >= code) return true
pos += set[i];
if (pos > code) return false;
pos += set[i + 1];
if (pos >= code) return true;
}
}
// Test whether a given character code starts an identifier.
export function isIdentifierStart(code, astral) {
if (code < 65) return code === 36
if (code < 91) return true
if (code < 97) return code === 95
if (code < 123) return true
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code))
if (astral === false) return false
return isInAstralSet(code, astralIdentifierStartCodes)
if (code < 65) return code === 36;
if (code < 91) return true;
if (code < 97) return code === 95;
if (code < 123) return true;
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
if (astral === false) return false;
return isInAstralSet(code, astralIdentifierStartCodes);
}
// Test whether a given character is part of an identifier.
export function isIdentifierChar(code, astral) {
if (code < 48) return code === 36
if (code < 58) return true
if (code < 65) return false
if (code < 91) return true
if (code < 97) return code === 95
if (code < 123) return true
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code))
if (astral === false) return false
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)
if (code < 48) return code === 36;
if (code < 58) return true;
if (code < 65) return false;
if (code < 91) return true;
if (code < 97) return code === 95;
if (code < 123) return true;
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
if (astral === false) return false;
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
}

View File

@ -19,26 +19,31 @@
// [dammit]: acorn_loose.js
// [walk]: util/walk.js
import {Parser} from "./state"
import {getOptions} from "./options"
import "./parseutil"
import "./statement"
import "./lval"
import "./expression"
import "./lookahead"
import {Parser, plugins} from "./state";
import {getOptions} from "./options";
import "./parseutil";
import "./statement";
import "./lval";
import "./expression";
import "./lookahead";
import "./tokentype";
import "./tokencontext";
export {Parser, plugins} from "./state"
export {defaultOptions} from "./options"
export {SourceLocation} from "./location"
export {getLineInfo} from "./location"
export {Node} from "./node"
export {TokenType, types as tokTypes} from "./tokentype"
export {TokContext, types as tokContexts} from "./tokencontext"
export {isIdentifierChar, isIdentifierStart} from "./identifier"
export {Token} from "./tokenize"
export {isNewLine, lineBreak, lineBreakG} from "./whitespace"
export {Parser, plugins} from "./state";
export {defaultOptions} from "./options";
export {SourceLocation} from "./location";
export {getLineInfo} from "./location";
export {Node} from "./node";
export {TokenType, types as tokTypes} from "./tokentype";
export {TokContext, types as tokContexts} from "./tokencontext";
export {isIdentifierChar, isIdentifierStart} from "./identifier";
export {Token} from "./tokenize";
export {isNewLine, lineBreak, lineBreakG} from "./whitespace";
export const version = "1.0.0"
import flowPlugin from "./plugins/flow";
import jsxPlugin from "./plugins/jsx";
plugins.flow = flowPlugin;
plugins.jsx = jsxPlugin;
// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and
@ -48,32 +53,5 @@ export const version = "1.0.0"
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
export function parse(input, options) {
let p = parser(options, input)
let startPos = p.options.locations ? [p.pos, p.curPosition()] : p.pos
p.nextToken()
return p.parseTopLevel(p.options.program || p.startNodeAt(startPos))
}
// This function tries to parse a single expression at a given
// offset in a string. Useful for parsing mixed-language formats
// that embed JavaScript expressions.
export function parseExpressionAt(input, pos, options) {
let p = parser(options, input, pos)
p.nextToken()
return p.parseExpression()
}
// Acorn is organized as a tokenizer and a recursive-descent parser.
// The `tokenize` export provides an interface to the tokenizer.
// Because the tokenizer is optimized for being efficiently used by
// the Acorn parser itself, this interface is somewhat crude and not
// very modular.
export function tokenizer(input, options) {
return parser(options, input)
}
function parser(options, input) {
return new Parser(getOptions(options), String(input))
return new Parser(getOptions(options), input).parse();
}

View File

@ -1,25 +1,25 @@
import {Parser} from "./state"
import {lineBreakG} from "./whitespace"
import {Parser} from "./state";
import {lineBreakG} from "./whitespace";
// These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties.
export class Position {
constructor(line, col) {
this.line = line
this.column = col
this.line = line;
this.column = col;
}
offset(n) {
return new Position(this.line, this.column + n)
return new Position(this.line, this.column + n);
}
}
export class SourceLocation {
constructor(p, start, end) {
this.start = start
this.end = end
if (p.sourceFile !== null) this.source = p.sourceFile
this.start = start;
this.end = end;
if (p.sourceFile !== null) this.source = p.sourceFile;
}
}
@ -30,19 +30,19 @@ export class SourceLocation {
// into.
export function getLineInfo(input, offset) {
for (let line = 1, cur = 0;;) {
lineBreakG.lastIndex = cur
let match = lineBreakG.exec(input)
for (let line = 1, cur = 0; ;) {
lineBreakG.lastIndex = cur;
let match = lineBreakG.exec(input);
if (match && match.index < offset) {
++line
cur = match.index + match[0].length
++line;
cur = match.index + match[0].length;
} else {
return new Position(line, offset - cur)
return new Position(line, offset - cur);
}
}
}
const pp = Parser.prototype
const pp = Parser.prototype;
// This function is used to raise exceptions on parse errors. It
// takes an offset integer (into the current `input`) to indicate
@ -50,18 +50,18 @@ const pp = Parser.prototype
// of the error message, and then raises a `SyntaxError` with that
// message.
pp.raise = function(pos, message) {
let loc = getLineInfo(this.input, pos)
message += " (" + loc.line + ":" + loc.column + ")"
let err = new SyntaxError(message)
err.pos = pos; err.loc = loc; err.raisedAt = this.pos
throw err
}
pp.raise = function (pos, message) {
let loc = getLineInfo(this.input, pos);
message += ` (${loc.line}:${loc.column})`;
let err = new SyntaxError(message);
err.pos = pos;
err.loc = loc;
err.raisedAt = this.pos;
throw err;
};
pp.curPosition = function() {
return new Position(this.curLine, this.pos - this.lineStart)
}
pp.markPosition = function() {
return this.options.locations ? [this.start, this.startLoc] : this.start
}
pp.curPosition = function () {
if (this.options.locations) {
return new Position(this.curLine, this.pos - this.lineStart);
}
};

View File

@ -1,6 +1,6 @@
import {Parser} from "./state"
import {Parser} from "./state";
const pp = Parser.prototype
const pp = Parser.prototype;
var STATE_KEYS = [
"lastTokStartLoc",
@ -27,21 +27,21 @@ var STATE_KEYS = [
];
pp.getState = function () {
var state = {}
var state = {};
for (var i = 0; i < STATE_KEYS.length; i++) {
var key = STATE_KEYS[i]
state[key] = this[key]
var key = STATE_KEYS[i];
state[key] = this[key];
}
state.context = this.context.slice()
state.labels = this.labels.slice()
return state
state.context = this.context.slice();
state.labels = this.labels.slice();
return state;
};
pp.lookahead = function() {
pp.lookahead = function () {
var old = this.getState();
this.isLookahead = true
this.next()
this.isLookahead = false
this.isLookahead = true;
this.next();
this.isLookahead = false;
var curr = this.getState();
for (var key in old) this[key] = old[key];
return curr;

View File

@ -1,205 +1,210 @@
import {types as tt} from "./tokentype"
import {Parser} from "./state"
import {reservedWords} from "./identifier"
import {has} from "./util"
import {types as tt} from "./tokentype";
import {Parser} from "./state";
import {reservedWords} from "./identifier";
import {has} from "./util";
const pp = Parser.prototype
const pp = Parser.prototype;
// Convert existing expression atom to assignable pattern
// if possible.
pp.toAssignable = function(node, isBinding) {
pp.toAssignable = function (node, isBinding) {
if (this.options.ecmaVersion >= 6 && node) {
switch (node.type) {
case "Identifier":
case "ObjectPattern":
case "ArrayPattern":
case "AssignmentPattern":
break
break;
case "ObjectExpression":
node.type = "ObjectPattern"
node.type = "ObjectPattern";
for (let i = 0; i < node.properties.length; i++) {
let prop = node.properties[i]
let prop = node.properties[i];
if (prop.type === "SpreadProperty") continue;
if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter")
this.toAssignable(prop.value, isBinding)
if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter");
this.toAssignable(prop.value, isBinding);
}
break
break;
case "ArrayExpression":
node.type = "ArrayPattern"
this.toAssignableList(node.elements, isBinding)
break
node.type = "ArrayPattern";
this.toAssignableList(node.elements, isBinding);
break;
case "AssignmentExpression":
if (node.operator === "=") {
node.type = "AssignmentPattern"
delete node.operator
node.type = "AssignmentPattern";
delete node.operator;
} else {
this.raise(node.left.end, "Only '=' operator can be used for specifying default value.")
this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
}
break
break;
case "MemberExpression":
if (!isBinding) break
if (!isBinding) break;
default:
this.raise(node.start, "Assigning to rvalue")
this.raise(node.start, "Assigning to rvalue");
}
}
return node
}
return node;
};
// Convert list of expression atoms to binding list.
pp.toAssignableList = function(exprList, isBinding) {
let end = exprList.length
pp.toAssignableList = function (exprList, isBinding) {
let end = exprList.length;
if (end) {
let last = exprList[end - 1]
if (last && last.type == "RestElement") {
--end
} else if (last && last.type == "SpreadElement") {
last.type = "RestElement"
let arg = last.argument
this.toAssignable(arg, isBinding)
if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern")
this.unexpected(arg.start)
--end
let last = exprList[end - 1];
if (last && last.type === "RestElement") {
--end;
} else if (last && last.type === "SpreadElement") {
last.type = "RestElement";
let arg = last.argument;
this.toAssignable(arg, isBinding);
if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") {
this.unexpected(arg.start);
}
--end;
}
}
for (let i = 0; i < end; i++) {
let elt = exprList[i]
if (elt) this.toAssignable(elt, isBinding)
let elt = exprList[i];
if (elt) this.toAssignable(elt, isBinding);
}
return exprList
}
return exprList;
};
// Parses spread element.
pp.parseSpread = function(refShorthandDefaultPos) {
let node = this.startNode()
this.next()
node.argument = this.parseMaybeAssign(refShorthandDefaultPos)
return this.finishNode(node, "SpreadElement")
}
pp.parseSpread = function (refShorthandDefaultPos) {
let node = this.startNode();
this.next();
node.argument = this.parseMaybeAssign(refShorthandDefaultPos);
return this.finishNode(node, "SpreadElement");
};
pp.parseRest = function() {
let node = this.startNode()
this.next()
node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected()
return this.finishNode(node, "RestElement")
}
pp.parseRest = function () {
let node = this.startNode();
this.next();
node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected();
return this.finishNode(node, "RestElement");
};
// Parses lvalue (assignable) atom.
pp.parseBindingAtom = function() {
if (this.options.ecmaVersion < 6) return this.parseIdent()
pp.parseBindingAtom = function () {
if (this.options.ecmaVersion < 6) return this.parseIdent();
switch (this.type) {
case tt.name:
return this.parseIdent()
return this.parseIdent();
case tt.bracketL:
let node = this.startNode()
this.next()
node.elements = this.parseBindingList(tt.bracketR, true, true)
return this.finishNode(node, "ArrayPattern")
let node = this.startNode();
this.next();
node.elements = this.parseBindingList(tt.bracketR, true, true);
return this.finishNode(node, "ArrayPattern");
case tt.braceL:
return this.parseObj(true)
return this.parseObj(true);
default:
this.unexpected()
this.unexpected();
}
}
};
pp.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
var elts = [], first = true
pp.parseBindingList = function (close, allowEmpty, allowTrailingComma) {
var elts = [], first = true;
while (!this.eat(close)) {
if (first) first = false
else this.expect(tt.comma)
if (first) first = false;
else this.expect(tt.comma);
if (allowEmpty && this.type === tt.comma) {
elts.push(null)
elts.push(null);
} else if (allowTrailingComma && this.afterTrailingComma(close)) {
break
break;
} else if (this.type === tt.ellipsis) {
elts.push(this.parseAssignableListItemTypes(this.parseRest()))
this.expect(close)
break
elts.push(this.parseAssignableListItemTypes(this.parseRest()));
this.expect(close);
break;
} else {
var left = this.parseMaybeDefault()
this.parseAssignableListItemTypes(left)
elts.push(this.parseMaybeDefault(null, left))
var left = this.parseMaybeDefault();
this.parseAssignableListItemTypes(left);
elts.push(this.parseMaybeDefault(null, null, left));
}
}
return elts
}
return elts;
};
pp.parseAssignableListItemTypes = function(param) {
return param
}
pp.parseAssignableListItemTypes = function (param) {
return param;
};
// Parses assignment pattern around given atom if possible.
pp.parseMaybeDefault = function(startPos, left) {
startPos = startPos || this.markPosition()
left = left || this.parseBindingAtom()
if (!this.eat(tt.eq)) return left
let node = this.startNodeAt(startPos)
node.operator = "="
node.left = left
node.right = this.parseMaybeAssign()
return this.finishNode(node, "AssignmentPattern")
}
pp.parseMaybeDefault = function (startPos, startLoc, left) {
startLoc = startLoc || this.startLoc;
startPos = startPos || this.start;
left = left || this.parseBindingAtom();
if (!this.eat(tt.eq)) return left;
let node = this.startNodeAt(startPos, startLoc);
node.operator = "=";
node.left = left;
node.right = this.parseMaybeAssign();
return this.finishNode(node, "AssignmentPattern");
};
// Verify that a node is an lval — something that can be assigned
// to.
pp.checkLVal = function(expr, isBinding, checkClashes) {
pp.checkLVal = function (expr, isBinding, checkClashes) {
switch (expr.type) {
case "Identifier":
if (this.strict && (reservedWords.strictBind(expr.name) || reservedWords.strict(expr.name)))
this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode")
this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode");
if (checkClashes) {
if (has(checkClashes, expr.name))
this.raise(expr.start, "Argument name clash in strict mode")
checkClashes[expr.name] = true
if (checkClashes[expr.name]) {
this.raise(expr.start, "Argument name clash in strict mode");
} else {
checkClashes[expr.name] = true;
}
}
break
break;
case "MemberExpression":
if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression")
break
if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression");
break;
case "ObjectPattern":
for (let i = 0; i < expr.properties.length; i++) {
var prop = expr.properties[i];
if (prop.type === "Property") prop = prop.value;
this.checkLVal(prop, isBinding, checkClashes)
this.checkLVal(prop, isBinding, checkClashes);
}
break
break;
case "ArrayPattern":
for (let i = 0; i < expr.elements.length; i++) {
let elem = expr.elements[i]
if (elem) this.checkLVal(elem, isBinding, checkClashes)
let elem = expr.elements[i];
if (elem) this.checkLVal(elem, isBinding, checkClashes);
}
break
break;
case "AssignmentPattern":
this.checkLVal(expr.left, isBinding, checkClashes)
break
this.checkLVal(expr.left, isBinding, checkClashes);
break;
case "SpreadProperty":
case "RestElement":
this.checkLVal(expr.argument, isBinding, checkClashes)
break
this.checkLVal(expr.argument, isBinding, checkClashes);
break;
case "ParenthesizedExpression":
this.checkLVal(expr.expression, isBinding, checkClashes)
break
this.checkLVal(expr.expression, isBinding, checkClashes);
break;
default:
this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue")
this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue");
}
}
};

View File

@ -1,57 +1,48 @@
import {Parser} from "./state"
import {SourceLocation} from "./location"
import {Parser} from "./state";
import {SourceLocation} from "./location";
// Start an AST node, attaching a start offset.
const pp = Parser.prototype
const pp = Parser.prototype;
export class Node {}
pp.startNode = function() {
let node = new Node
node.start = this.start
if (this.options.locations)
node.loc = new SourceLocation(this, this.startLoc)
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [this.start, 0]
return node
export class Node {
constructor(parser, pos, loc) {
this.type = "";
this.start = pos;
this.end = 0;
if (parser.options.locations)
this.loc = new SourceLocation(parser, loc);
if (parser.options.directSourceFile)
this.sourceFile = parser.options.directSourceFile;
if (parser.options.ranges)
this.range = [pos, 0];
}
}
pp.startNodeAt = function(pos) {
let node = new Node, start = pos
if (this.options.locations) {
node.loc = new SourceLocation(this, start[1])
start = pos[0]
}
node.start = start
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [start, 0]
return node
pp.startNode = function () {
return new Node(this, this.start, this.startLoc);
};
pp.startNodeAt = function (pos, loc) {
return new Node(this, pos, loc);
};
function finishNodeAt(node, type, pos, loc) {
node.type = type;
node.end = pos;
if (this.options.locations) node.loc.end = loc;
if (this.options.ranges) node.range[1] = pos;
return node;
}
// Finish an AST node, adding `type` and `end` properties.
pp.finishNode = function(node, type) {
node.type = type
node.end = this.lastTokEnd
if (this.options.locations)
node.loc.end = this.lastTokEndLoc
if (this.options.ranges)
node.range[1] = this.lastTokEnd
return node
}
pp.finishNode = function (node, type) {
return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc);
};
// Finish node at given position
pp.finishNodeAt = function(node, type, pos) {
if (this.options.locations) { node.loc.end = pos[1]; pos = pos[0] }
node.type = type
node.end = pos
if (this.options.ranges)
node.range[1] = pos
return node
}
pp.finishNodeAt = function (node, type, pos, loc) {
return finishNodeAt.call(this, node, type, pos, loc);
};

View File

@ -1,5 +1,5 @@
import {has, isArray} from "./util"
import {SourceLocation} from "./location"
import {has} from "./util";
import {SourceLocation} from "./location";
// A second optional argument can be given to further configure
// the parser process. These options are recognized:
@ -85,38 +85,37 @@ export const defaultOptions = {
// Babel-specific options
features: {},
strictMode: null
}
};
// Interpret and default an options object
export function getOptions(opts) {
let options = {}
let options = {};
for (let opt in defaultOptions)
options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]
options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt];
if (isArray(options.onToken)) {
let tokens = options.onToken
options.onToken = (token) => tokens.push(token)
if (Array.isArray(options.onToken)) {
let tokens = options.onToken;
options.onToken = (token) => tokens.push(token);
}
if (isArray(options.onComment))
options.onComment = pushComment(options, options.onComment)
if (Array.isArray(options.onComment))
options.onComment = pushComment(options, options.onComment);
return options
return options;
}
function pushComment(options, array) {
return function (block, text, start, end, startLoc, endLoc) {
let comment = {
type: block ? 'Block' : 'Line',
type: block ? "Block" : "Line",
value: text,
start: start,
end: end
}
};
if (options.locations)
comment.loc = new SourceLocation(this, startLoc, endLoc)
comment.loc = new SourceLocation(this, startLoc, endLoc);
if (options.ranges)
comment.range = [start, end]
array.push(comment)
}
comment.range = [start, end];
array.push(comment);
};
}

View File

@ -1,89 +1,89 @@
import {types as tt} from "./tokentype"
import {Parser} from "./state"
import {lineBreak} from "./whitespace"
import {types as tt} from "./tokentype";
import {Parser} from "./state";
import {lineBreak} from "./whitespace";
const pp = Parser.prototype
const pp = Parser.prototype;
// ## Parser utilities
// Test whether a statement node is the string literal `"use strict"`.
pp.isUseStrict = function(stmt) {
pp.isUseStrict = function (stmt) {
return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" &&
stmt.expression.type === "Literal" && stmt.expression.value === "use strict"
}
stmt.expression.type === "Literal" && stmt.expression.value === "use strict";
};
// Predicate that tests whether the next token is of the given
// type, and if yes, consumes it as a side effect.
pp.eat = function(type) {
pp.eat = function (type) {
if (this.type === type) {
this.next()
return true
this.next();
return true;
} else {
return false
return false;
}
}
};
// Tests whether parsed token is a contextual keyword.
pp.isContextual = function(name) {
return this.type === tt.name && this.value === name
}
pp.isContextual = function (name) {
return this.type === tt.name && this.value === name;
};
// Consumes contextual keyword if possible.
pp.eatContextual = function(name) {
return this.value === name && this.eat(tt.name)
}
pp.eatContextual = function (name) {
return this.value === name && this.eat(tt.name);
};
// Asserts that following token is given contextual keyword.
pp.expectContextual = function(name) {
if (!this.eatContextual(name)) this.unexpected()
}
pp.expectContextual = function (name) {
if (!this.eatContextual(name)) this.unexpected();
};
// Test whether a semicolon can be inserted at the current position.
pp.canInsertSemicolon = function() {
pp.canInsertSemicolon = function () {
return this.type === tt.eof ||
this.type === tt.braceR ||
lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
}
lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
};
pp.insertSemicolon = function() {
pp.insertSemicolon = function () {
if (this.canInsertSemicolon()) {
if (this.options.onInsertedSemicolon)
this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc)
return true
this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc);
return true;
}
}
};
// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.
pp.semicolon = function() {
if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected()
}
pp.semicolon = function () {
if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected();
};
pp.afterTrailingComma = function(tokType) {
if (this.type == tokType) {
pp.afterTrailingComma = function (tokType) {
if (this.type === tokType) {
if (this.options.onTrailingComma)
this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc)
this.next()
return true
this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc);
this.next();
return true;
}
}
};
// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error.
pp.expect = function(type) {
this.eat(type) || this.unexpected()
}
pp.expect = function (type) {
return this.eat(type) || this.unexpected();
};
// Raise an unexpected token error.
pp.unexpected = function(pos) {
this.raise(pos != null ? pos : this.start, "Unexpected token")
}
pp.unexpected = function (pos) {
this.raise(pos != null ? pos : this.start, "Unexpected token");
};

823
src/plugins/flow.js Normal file
View File

@ -0,0 +1,823 @@
import { types as tt } from "../tokentype";
import { Parser } from "../state";
var pp = Parser.prototype;
pp.isRelational = function (op) {
return this.type === tt.relational && this.value === op;
};
pp.expectRelational = function (op) {
if (this.isRelational(op)) {
this.next();
} else {
this.unexpected();
}
};
pp.flowParseTypeInitialiser = function (tok) {
var oldInType = this.inType;
this.inType = true;
this.expect(tok || tt.colon);
var type = this.flowParseType();
this.inType = oldInType;
return type;
};
pp.flowParseDeclareClass = function (node) {
this.next();
this.flowParseInterfaceish(node, true);
return this.finishNode(node, "DeclareClass");
};
pp.flowParseDeclareFunction = function (node) {
this.next();
var id = node.id = this.parseIdent();
var typeNode = this.startNode();
var typeContainer = this.startNode();
if (this.isRelational("<")) {
typeNode.typeParameters = this.flowParseTypeParameterDeclaration();
} else {
typeNode.typeParameters = null;
}
this.expect(tt.parenL);
var tmp = this.flowParseFunctionTypeParams();
typeNode.params = tmp.params;
typeNode.rest = tmp.rest;
this.expect(tt.parenR);
typeNode.returnType = this.flowParseTypeInitialiser();
typeContainer.typeAnnotation = this.finishNode(typeNode, "FunctionTypeAnnotation");
id.typeAnnotation = this.finishNode(typeContainer, "TypeAnnotation");
this.finishNode(id, id.type);
this.semicolon();
return this.finishNode(node, "DeclareFunction");
};
pp.flowParseDeclare = function (node) {
if (this.type === tt._class) {
return this.flowParseDeclareClass(node);
} else if (this.type === tt._function) {
return this.flowParseDeclareFunction(node);
} else if (this.type === tt._var) {
return this.flowParseDeclareVariable(node);
} else if (this.isContextual("module")) {
return this.flowParseDeclareModule(node);
} else {
this.unexpected();
}
};
pp.flowParseDeclareVariable = function (node) {
this.next();
node.id = this.flowParseTypeAnnotatableIdentifier();
this.semicolon();
return this.finishNode(node, "DeclareVariable");
};
pp.flowParseDeclareModule = function (node) {
this.next();
if (this.type === tt.string) {
node.id = this.parseExprAtom();
} else {
node.id = this.parseIdent();
}
var bodyNode = node.body = this.startNode();
var body = bodyNode.body = [];
this.expect(tt.braceL);
while (this.type !== tt.braceR) {
var node2 = this.startNode();
// todo: declare check
this.next();
body.push(this.flowParseDeclare(node2));
}
this.expect(tt.braceR);
this.finishNode(bodyNode, "BlockStatement");
return this.finishNode(node, "DeclareModule");
};
// Interfaces
pp.flowParseInterfaceish = function (node, allowStatic) {
node.id = this.parseIdent();
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
} else {
node.typeParameters = null;
}
node.extends = [];
if (this.eat(tt._extends)) {
do {
node.extends.push(this.flowParseInterfaceExtends());
} while(this.eat(tt.comma));
}
node.body = this.flowParseObjectType(allowStatic);
};
pp.flowParseInterfaceExtends = function () {
var node = this.startNode();
node.id = this.parseIdent();
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterInstantiation();
} else {
node.typeParameters = null;
}
return this.finishNode(node, "InterfaceExtends");
};
pp.flowParseInterface = function (node) {
this.flowParseInterfaceish(node, false);
return this.finishNode(node, "InterfaceDeclaration");
};
// Type aliases
pp.flowParseTypeAlias = function (node) {
node.id = this.parseIdent();
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
} else {
node.typeParameters = null;
}
node.right = this.flowParseTypeInitialiser(tt.eq);
this.semicolon();
return this.finishNode(node, "TypeAlias");
};
// Type annotations
pp.flowParseTypeParameterDeclaration = function () {
var node = this.startNode();
node.params = [];
this.expectRelational("<");
while (!this.isRelational(">")) {
node.params.push(this.flowParseTypeAnnotatableIdentifier());
if (!this.isRelational(">")) {
this.expect(tt.comma);
}
}
this.expectRelational(">");
return this.finishNode(node, "TypeParameterDeclaration");
};
pp.flowParseTypeParameterInstantiation = function () {
var node = this.startNode(), oldInType = this.inType;
node.params = [];
this.inType = true;
this.expectRelational("<");
while (!this.isRelational(">")) {
node.params.push(this.flowParseType());
if (!this.isRelational(">")) {
this.expect(tt.comma);
}
}
this.expectRelational(">");
this.inType = oldInType;
return this.finishNode(node, "TypeParameterInstantiation");
};
pp.flowParseObjectPropertyKey = function () {
return (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true);
};
pp.flowParseObjectTypeIndexer = function (node, isStatic) {
node.static = isStatic;
this.expect(tt.bracketL);
node.id = this.flowParseObjectPropertyKey();
node.key = this.flowParseTypeInitialiser();
this.expect(tt.bracketR);
node.value = this.flowParseTypeInitialiser();
this.flowObjectTypeSemicolon();
return this.finishNode(node, "ObjectTypeIndexer");
};
pp.flowParseObjectTypeMethodish = function (node) {
node.params = [];
node.rest = null;
node.typeParameters = null;
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
}
this.expect(tt.parenL);
while (this.type === tt.name) {
node.params.push(this.flowParseFunctionTypeParam());
if (this.type !== tt.parenR) {
this.expect(tt.comma);
}
}
if (this.eat(tt.ellipsis)) {
node.rest = this.flowParseFunctionTypeParam();
}
this.expect(tt.parenR);
node.returnType = this.flowParseTypeInitialiser();
return this.finishNode(node, "FunctionTypeAnnotation");
};
pp.flowParseObjectTypeMethod = function (startPos, startLoc, isStatic, key) {
var node = this.startNodeAt(startPos, startLoc);
node.value = this.flowParseObjectTypeMethodish(this.startNodeAt(startPos, startLoc));
node.static = isStatic;
node.key = key;
node.optional = false;
this.flowObjectTypeSemicolon();
return this.finishNode(node, "ObjectTypeProperty");
};
pp.flowParseObjectTypeCallProperty = function (node, isStatic) {
var valueNode = this.startNode();
node.static = isStatic;
node.value = this.flowParseObjectTypeMethodish(valueNode);
this.flowObjectTypeSemicolon();
return this.finishNode(node, "ObjectTypeCallProperty");
};
pp.flowParseObjectType = function (allowStatic) {
var nodeStart = this.startNode();
var node;
var optional = false;
var propertyKey;
var isStatic;
nodeStart.callProperties = [];
nodeStart.properties = [];
nodeStart.indexers = [];
this.expect(tt.braceL);
while (this.type !== tt.braceR) {
var startPos = this.start, startLoc = this.startLoc;
node = this.startNode();
if (allowStatic && this.isContextual("static")) {
this.next();
isStatic = true;
}
if (this.type === tt.bracketL) {
nodeStart.indexers.push(this.flowParseObjectTypeIndexer(node, isStatic));
} else if (this.type === tt.parenL || this.isRelational("<")) {
nodeStart.callProperties.push(this.flowParseObjectTypeCallProperty(node, allowStatic));
} else {
if (isStatic && this.type === tt.colon) {
propertyKey = this.parseIdent();
} else {
propertyKey = this.flowParseObjectPropertyKey();
}
if (this.isRelational("<") || this.type === tt.parenL) {
// This is a method property
nodeStart.properties.push(this.flowParseObjectTypeMethod(startPos, startLoc, isStatic, propertyKey));
} else {
if (this.eat(tt.question)) {
optional = true;
}
node.key = propertyKey;
node.value = this.flowParseTypeInitialiser();
node.optional = optional;
node.static = isStatic;
this.flowObjectTypeSemicolon();
nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty"));
}
}
}
this.expect(tt.braceR);
return this.finishNode(nodeStart, "ObjectTypeAnnotation");
};
pp.flowObjectTypeSemicolon = function () {
if (!this.eat(tt.semi) && !this.eat(tt.comma) && this.type !== tt.braceR) {
this.unexpected();
}
};
pp.flowParseGenericType = function (startPos, startLoc, id) {
var node = this.startNodeAt(startPos, startLoc);
node.typeParameters = null;
node.id = id;
while (this.eat(tt.dot)) {
var node2 = this.startNodeAt(startPos, startLoc);
node2.qualification = node.id;
node2.id = this.parseIdent();
node.id = this.finishNode(node2, "QualifiedTypeIdentifier");
}
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterInstantiation();
}
return this.finishNode(node, "GenericTypeAnnotation");
};
pp.flowParseTypeofType = function () {
var node = this.startNode();
this.expect(tt._typeof);
node.argument = this.flowParsePrimaryType();
return this.finishNode(node, "TypeofTypeAnnotation");
};
pp.flowParseTupleType = function () {
var node = this.startNode();
node.types = [];
this.expect(tt.bracketL);
// We allow trailing commas
while (this.pos < this.input.length && this.type !== tt.bracketR) {
node.types.push(this.flowParseType());
if (this.type === tt.bracketR) break;
this.expect(tt.comma);
}
this.expect(tt.bracketR);
return this.finishNode(node, "TupleTypeAnnotation");
};
pp.flowParseFunctionTypeParam = function () {
var optional = false;
var node = this.startNode();
node.name = this.parseIdent();
if (this.eat(tt.question)) {
optional = true;
}
node.optional = optional;
node.typeAnnotation = this.flowParseTypeInitialiser();
return this.finishNode(node, "FunctionTypeParam");
};
pp.flowParseFunctionTypeParams = function () {
var ret = { params: [], rest: null };
while (this.type === tt.name) {
ret.params.push(this.flowParseFunctionTypeParam());
if (this.type !== tt.parenR) {
this.expect(tt.comma);
}
}
if (this.eat(tt.ellipsis)) {
ret.rest = this.flowParseFunctionTypeParam();
}
return ret;
};
pp.flowIdentToTypeAnnotation = function (startPos, startLoc, node, id) {
switch (id.name) {
case "any":
return this.finishNode(node, "AnyTypeAnnotation");
case "void":
return this.finishNode(node, "VoidTypeAnnotation");
case "bool":
case "boolean":
return this.finishNode(node, "BooleanTypeAnnotation");
case "mixed":
return this.finishNode(node, "MixedTypeAnnotation");
case "number":
return this.finishNode(node, "NumberTypeAnnotation");
case "string":
return this.finishNode(node, "StringTypeAnnotation");
default:
return this.flowParseGenericType(startPos, startLoc, id);
}
};
// The parsing of types roughly parallels the parsing of expressions, and
// primary types are kind of like primary expressions...they're the
// primitives with which other types are constructed.
pp.flowParsePrimaryType = function () {
var startPos = this.start, startLoc = this.startLoc;
var node = this.startNode();
var tmp;
var type;
var isGroupedType = false;
switch (this.type) {
case tt.name:
return this.flowIdentToTypeAnnotation(startPos, startLoc, node, this.parseIdent());
case tt.braceL:
return this.flowParseObjectType();
case tt.bracketL:
return this.flowParseTupleType();
case tt.relational:
if (this.value === "<") {
node.typeParameters = this.flowParseTypeParameterDeclaration();
this.expect(tt.parenL);
tmp = this.flowParseFunctionTypeParams();
node.params = tmp.params;
node.rest = tmp.rest;
this.expect(tt.parenR);
this.expect(tt.arrow);
node.returnType = this.flowParseType();
return this.finishNode(node, "FunctionTypeAnnotation");
}
case tt.parenL:
this.next();
// Check to see if this is actually a grouped type
if (this.type !== tt.parenR && this.type !== tt.ellipsis) {
if (this.type === tt.name) {
var token = this.lookahead().type;
isGroupedType = token !== tt.question && token !== tt.colon;
} else {
isGroupedType = true;
}
}
if (isGroupedType) {
type = this.flowParseType();
this.expect(tt.parenR);
// If we see a => next then someone was probably confused about
// function types, so we can provide a better error message
if (this.eat(tt.arrow)) {
this.raise(node,
"Unexpected token =>. It looks like " +
"you are trying to write a function type, but you ended up " +
"writing a grouped type followed by an =>, which is a syntax " +
"error. Remember, function type parameters are named so function " +
"types look like (name1: type1, name2: type2) => returnType. You " +
"probably wrote (type1) => returnType"
);
}
return type;
}
tmp = this.flowParseFunctionTypeParams();
node.params = tmp.params;
node.rest = tmp.rest;
this.expect(tt.parenR);
this.expect(tt.arrow);
node.returnType = this.flowParseType();
node.typeParameters = null;
return this.finishNode(node, "FunctionTypeAnnotation");
case tt.string:
node.value = this.value;
node.raw = this.input.slice(this.start, this.end);
this.next();
return this.finishNode(node, "StringLiteralTypeAnnotation");
default:
if (this.type.keyword === "typeof") {
return this.flowParseTypeofType();
}
}
this.unexpected();
};
pp.flowParsePostfixType = function () {
var node = this.startNode();
var type = node.elementType = this.flowParsePrimaryType();
if (this.type === tt.bracketL) {
this.expect(tt.bracketL);
this.expect(tt.bracketR);
return this.finishNode(node, "ArrayTypeAnnotation");
} else {
return type;
}
};
pp.flowParsePrefixType = function () {
var node = this.startNode();
if (this.eat(tt.question)) {
node.typeAnnotation = this.flowParsePrefixType();
return this.finishNode(node, "NullableTypeAnnotation");
} else {
return this.flowParsePostfixType();
}
};
pp.flowParseIntersectionType = function () {
var node = this.startNode();
var type = this.flowParsePrefixType();
node.types = [type];
while (this.eat(tt.bitwiseAND)) {
node.types.push(this.flowParsePrefixType());
}
return node.types.length === 1 ? type : this.finishNode(node, "IntersectionTypeAnnotation");
};
pp.flowParseUnionType = function () {
var node = this.startNode();
var type = this.flowParseIntersectionType();
node.types = [type];
while (this.eat(tt.bitwiseOR)) {
node.types.push(this.flowParseIntersectionType());
}
return node.types.length === 1 ? type : this.finishNode(node, "UnionTypeAnnotation");
};
pp.flowParseType = function () {
var oldInType = this.inType;
this.inType = true;
var type = this.flowParseUnionType();
this.inType = oldInType;
return type;
};
pp.flowParseTypeAnnotation = function () {
var node = this.startNode();
node.typeAnnotation = this.flowParseTypeInitialiser();
return this.finishNode(node, "TypeAnnotation");
};
pp.flowParseTypeAnnotatableIdentifier = function (requireTypeAnnotation, canBeOptionalParam) {
var ident = this.parseIdent();
var isOptionalParam = false;
if (canBeOptionalParam && this.eat(tt.question)) {
this.expect(tt.question);
isOptionalParam = true;
}
if (requireTypeAnnotation || this.type === tt.colon) {
ident.typeAnnotation = this.flowParseTypeAnnotation();
this.finishNode(ident, ident.type);
}
if (isOptionalParam) {
ident.optional = true;
this.finishNode(ident, ident.type);
}
return ident;
};
export default function (instance) {
// function name(): string {}
instance.extend("parseFunctionBody", function (inner) {
return function (node, allowExpression) {
if (this.type === tt.colon) {
node.returnType = this.flowParseTypeAnnotation();
}
return inner.call(this, node, allowExpression);
};
});
instance.extend("parseStatement", function (inner) {
return function (declaration, topLevel) {
// strict mode handling of `interface` since it's a reserved word
if (this.strict && this.type === tt.name && this.value === "interface") {
var node = this.startNode();
this.next();
return this.flowParseInterface(node);
} else {
return inner.call(this, declaration, topLevel);
}
};
});
instance.extend("parseExpressionStatement", function (inner) {
return function (node, expr) {
if (expr.type === "Identifier") {
if (expr.name === "declare") {
if (this.type === tt._class || this.type === tt.name || this.type === tt._function || this.type === tt._var) {
return this.flowParseDeclare(node);
}
} else if (this.type === tt.name) {
if (expr.name === "interface") {
return this.flowParseInterface(node);
} else if (expr.name === "type") {
return this.flowParseTypeAlias(node);
}
}
}
return inner.call(this, node, expr);
};
});
instance.extend("shouldParseExportDeclaration", function (inner) {
return function () {
return this.isContextual("type") || inner.call(this);
};
});
instance.extend("parseParenItem", function () {
return function (node, startLoc, startPos) {
if (this.type === tt.colon) {
var typeCastNode = this.startNodeAt(startLoc, startPos);
typeCastNode.expression = node;
typeCastNode.typeAnnotation = this.flowParseTypeAnnotation();
return this.finishNode(typeCastNode, "TypeCastExpression");
} else {
return node;
}
};
});
instance.extend("parseClassId", function (inner) {
return function (node, isStatement) {
inner.call(this, node, isStatement);
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
}
};
});
// don't consider `void` to be a keyword as then it'll use the void token type
// and set startExpr
instance.extend("isKeyword", function (inner) {
return function (name) {
if (this.inType && name === "void") {
return false;
} else {
return inner.call(this, name);
}
};
});
instance.extend("readToken", function (inner) {
return function (code) {
if (this.inType && (code === 62 || code === 60)) {
return this.finishOp(tt.relational, 1);
} else {
return inner.call(this, code);
}
};
});
instance.extend("jsx_readToken", function (inner) {
return function () {
if (!this.inType) return inner.call(this);
};
});
instance.extend("parseParenArrowList", function (inner) {
return function (startPos, startLoc, exprList, isAsync) {
for (var i = 0; i < exprList.length; i++) {
var listItem = exprList[i];
if (listItem.type === "TypeCastExpression") {
var expr = listItem.expression;
expr.typeAnnotation = listItem.typeAnnotation;
exprList[i] = expr;
}
}
return inner.call(this, startPos, startLoc, exprList, isAsync);
};
});
instance.extend("parseClassProperty", function (inner) {
return function (node) {
if (this.type === tt.colon) {
node.typeAnnotation = this.flowParseTypeAnnotation();
}
return inner.call(this, node);
};
});
instance.extend("isClassProperty", function (inner) {
return function () {
return this.type === tt.colon || inner.call(this);
};
});
instance.extend("parseClassMethod", function () {
return function (classBody, method, isGenerator, isAsync) {
var typeParameters;
if (this.isRelational("<")) {
typeParameters = this.flowParseTypeParameterDeclaration();
}
method.value = this.parseMethod(isGenerator, isAsync);
method.value.typeParameters = typeParameters;
classBody.body.push(this.finishNode(method, "MethodDefinition"));
};
});
instance.extend("parseClassSuper", function (inner) {
return function (node, isStatement) {
inner.call(this, node, isStatement);
if (node.superClass && this.isRelational("<")) {
node.superTypeParameters = this.flowParseTypeParameterInstantiation();
}
if (this.isContextual("implements")) {
this.next();
var implemented = node.implements = [];
do {
let node = this.startNode();
node.id = this.parseIdent();
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterInstantiation();
} else {
node.typeParameters = null;
}
implemented.push(this.finishNode(node, "ClassImplements"));
} while(this.eat(tt.comma))
}
};
});
instance.extend("parseObjPropValue", function (inner) {
return function (prop) {
var typeParameters;
if (this.isRelational("<")) {
typeParameters = this.flowParseTypeParameterDeclaration();
if (this.type !== tt.parenL) this.unexpected();
}
inner.apply(this, arguments);
prop.value.typeParameters = typeParameters;
};
});
instance.extend("parseAssignableListItemTypes", function () {
return function (param) {
if (this.eat(tt.question)) {
param.optional = true;
}
if (this.type === tt.colon) {
param.typeAnnotation = this.flowParseTypeAnnotation();
}
this.finishNode(param, param.type);
return param;
};
});
instance.extend("parseImportSpecifiers", function (inner) {
return function (node) {
node.isType = false;
if (this.isContextual("type")) {
var startPos = this.start, startLoc = this.startLoc;
var typeId = this.parseIdent();
if ((this.type === tt.name && this.value !== "from") || this.type === tt.braceL || this.type === tt.star) {
node.isType = true;
} else {
node.specifiers.push(this.parseImportSpecifierDefault(typeId, startPos, startLoc));
if (this.isContextual("from")) return;
this.eat(tt.comma);
}
}
inner.call(this, node);
};
});
// function foo<T>() {}
instance.extend("parseFunctionParams", function (inner) {
return function (node) {
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
}
inner.call(this, node);
};
});
// var foo: string = bar
instance.extend("parseVarHead", function (inner) {
return function (decl) {
inner.call(this, decl);
if (this.type === tt.colon) {
decl.id.typeAnnotation = this.flowParseTypeAnnotation();
this.finishNode(decl.id, decl.id.type);
}
};
});
};

421
src/plugins/jsx/index.js Normal file
View File

@ -0,0 +1,421 @@
import XHTMLEntities from "./xhtml";
import { TokenType, types as tt } from "../../tokentype";
import { TokContext, types as tc } from "../../tokencontext";
import { Parser } from "../../state";
import { isIdentifierChar, isIdentifierStart } from "../../identifier";
import { isNewLine, lineBreak, lineBreakG } from "../../whitespace";
const HEX_NUMBER = /^[\da-fA-F]+$/;
const DECIMAL_NUMBER = /^\d+$/;
tc.j_oTag = new TokContext("<tag", false);
tc.j_cTag = new TokContext("</tag", false);
tc.j_expr = new TokContext("<tag>...</tag>", true, true);
tt.jsxName = new TokenType("jsxName");
tt.jsxText = new TokenType("jsxText", { beforeExpr: true });
tt.jsxTagStart = new TokenType("jsxTagStart");
tt.jsxTagEnd = new TokenType("jsxTagEnd");
tt.jsxTagStart.updateContext = function() {
this.context.push(tc.j_expr); // treat as beginning of JSX expression
this.context.push(tc.j_oTag); // start opening tag context
this.exprAllowed = false;
};
tt.jsxTagEnd.updateContext = function(prevType) {
var out = this.context.pop();
if (out === tc.j_oTag && prevType === tt.slash || out === tc.j_cTag) {
this.context.pop();
this.exprAllowed = this.curContext() === tc.j_expr;
} else {
this.exprAllowed = true;
}
};
var pp = Parser.prototype;
// Reads inline JSX contents token.
pp.jsxReadToken = function() {
var out = "", chunkStart = this.pos;
for (;;) {
if (this.pos >= this.input.length)
this.raise(this.start, "Unterminated JSX contents");
var ch = this.input.charCodeAt(this.pos);
switch (ch) {
case 60: // "<"
case 123: // "{"
if (this.pos === this.start) {
if (ch === 60 && this.exprAllowed) {
++this.pos;
return this.finishToken(tt.jsxTagStart);
}
return this.getTokenFromCode(ch);
}
out += this.input.slice(chunkStart, this.pos);
return this.finishToken(tt.jsxText, out);
case 38: // "&"
out += this.input.slice(chunkStart, this.pos);
out += this.jsxReadEntity();
chunkStart = this.pos;
break;
default:
if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.pos);
out += this.jsxReadNewLine(true);
chunkStart = this.pos;
} else {
++this.pos;
}
}
}
};
pp.jsxReadNewLine = function(normalizeCRLF) {
var ch = this.input.charCodeAt(this.pos);
var out;
++this.pos;
if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {
++this.pos;
out = normalizeCRLF ? "\n" : "\r\n";
} else {
out = String.fromCharCode(ch);
}
if (this.options.locations) {
++this.curLine;
this.lineStart = this.pos;
}
return out;
};
pp.jsxReadString = function(quote) {
var out = "", chunkStart = ++this.pos;
for (;;) {
if (this.pos >= this.input.length)
this.raise(this.start, "Unterminated string constant");
var ch = this.input.charCodeAt(this.pos);
if (ch === quote) break;
if (ch === 38) { // "&"
out += this.input.slice(chunkStart, this.pos);
out += this.jsxReadEntity();
chunkStart = this.pos;
} else if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.pos);
out += this.jsxReadNewLine(false);
chunkStart = this.pos;
} else {
++this.pos;
}
}
out += this.input.slice(chunkStart, this.pos++);
return this.finishToken(tt.string, out);
};
pp.jsxReadEntity = function() {
var str = "", count = 0, entity;
var ch = this.input[this.pos];
if (ch !== "&")
this.raise(this.pos, "Entity must start with an ampersand");
var startPos = ++this.pos;
while (this.pos < this.input.length && count++ < 10) {
ch = this.input[this.pos++];
if (ch === ";") {
if (str[0] === "#") {
if (str[1] === "x") {
str = str.substr(2);
if (HEX_NUMBER.test(str))
entity = String.fromCharCode(parseInt(str, 16));
} else {
str = str.substr(1);
if (DECIMAL_NUMBER.test(str))
entity = String.fromCharCode(parseInt(str, 10));
}
} else {
entity = XHTMLEntities[str];
}
break;
}
str += ch;
}
if (!entity) {
this.pos = startPos;
return "&";
}
return entity;
};
// Read a JSX identifier (valid tag or attribute name).
//
// Optimized version since JSX identifiers can"t contain
// escape characters and so can be read as single slice.
// Also assumes that first character was already checked
// by isIdentifierStart in readToken.
pp.jsxReadWord = function() {
var ch, start = this.pos;
do {
ch = this.input.charCodeAt(++this.pos);
} while (isIdentifierChar(ch) || ch === 45); // "-"
return this.finishToken(tt.jsxName, this.input.slice(start, this.pos));
};
// Transforms JSX element name to string.
function getQualifiedJSXName(object) {
if (object.type === "JSXIdentifier")
return object.name;
if (object.type === "JSXNamespacedName")
return object.namespace.name + ":" + object.name.name;
if (object.type === "JSXMemberExpression")
return getQualifiedJSXName(object.object) + "." +
getQualifiedJSXName(object.property);
}
// Parse next token as JSX identifier
pp.jsxParseIdentifier = function() {
var node = this.startNode();
if (this.type === tt.jsxName)
node.name = this.value;
else if (this.type.keyword)
node.name = this.type.keyword;
else
this.unexpected();
this.next();
return this.finishNode(node, "JSXIdentifier");
};
// Parse namespaced identifier.
pp.jsxParseNamespacedName = function() {
var startPos = this.start, startLoc = this.startLoc;
var name = this.jsxParseIdentifier();
if (!this.eat(tt.colon)) return name;
var node = this.startNodeAt(startPos, startLoc);
node.namespace = name;
node.name = this.jsxParseIdentifier();
return this.finishNode(node, "JSXNamespacedName");
};
// Parses element name in any form - namespaced, member
// or single identifier.
pp.jsxParseElementName = function() {
var startPos = this.start, startLoc = this.startLoc;
var node = this.jsxParseNamespacedName();
while (this.eat(tt.dot)) {
var newNode = this.startNodeAt(startPos, startLoc);
newNode.object = node;
newNode.property = this.jsxParseIdentifier();
node = this.finishNode(newNode, "JSXMemberExpression");
}
return node;
};
// Parses any type of JSX attribute value.
pp.jsxParseAttributeValue = function() {
switch (this.type) {
case tt.braceL:
var node = this.jsxParseExpressionContainer();
if (node.expression.type === "JSXEmptyExpression")
this.raise(node.start, "JSX attributes must only be assigned a non-empty expression");
return node;
case tt.jsxTagStart:
case tt.string:
return this.parseExprAtom();
default:
this.raise(this.start, "JSX value should be either an expression or a quoted JSX text");
}
};
// JSXEmptyExpression is unique type since it doesn"t actually parse anything,
// and so it should start at the end of last read token (left brace) and finish
// at the beginning of the next one (right brace).
pp.jsxParseEmptyExpression = function() {
var tmp = this.start;
this.start = this.lastTokEnd;
this.lastTokEnd = tmp;
tmp = this.startLoc;
this.startLoc = this.lastTokEndLoc;
this.lastTokEndLoc = tmp;
return this.finishNode(this.startNode(), "JSXEmptyExpression");
};
// Parses JSX expression enclosed into curly brackets.
pp.jsxParseExpressionContainer = function() {
var node = this.startNode();
this.next();
node.expression = this.type === tt.braceR
? this.jsxParseEmptyExpression()
: this.parseExpression();
this.expect(tt.braceR);
return this.finishNode(node, "JSXExpressionContainer");
};
// Parses following JSX attribute name-value pair.
pp.jsxParseAttribute = function() {
var node = this.startNode();
if (this.eat(tt.braceL)) {
this.expect(tt.ellipsis);
node.argument = this.parseMaybeAssign();
this.expect(tt.braceR);
return this.finishNode(node, "JSXSpreadAttribute");
}
node.name = this.jsxParseNamespacedName();
node.value = this.eat(tt.eq) ? this.jsxParseAttributeValue() : null;
return this.finishNode(node, "JSXAttribute");
};
// Parses JSX opening tag starting after "<".
pp.jsxParseOpeningElementAt = function(startPos, startLoc) {
var node = this.startNodeAt(startPos, startLoc);
node.attributes = [];
node.name = this.jsxParseElementName();
while (this.type !== tt.slash && this.type !== tt.jsxTagEnd)
node.attributes.push(this.jsxParseAttribute());
node.selfClosing = this.eat(tt.slash);
this.expect(tt.jsxTagEnd);
return this.finishNode(node, "JSXOpeningElement");
};
// Parses JSX closing tag starting after "</".
pp.jsxParseClosingElementAt = function(startPos, startLoc) {
var node = this.startNodeAt(startPos, startLoc);
node.name = this.jsxParseElementName();
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.jsxParseElementAt = function(startPos, startLoc) {
var node = this.startNodeAt(startPos, startLoc);
var children = [];
var openingElement = this.jsxParseOpeningElementAt(startPos, startLoc);
var closingElement = null;
if (!openingElement.selfClosing) {
contents: for (;;) {
switch (this.type) {
case tt.jsxTagStart:
startPos = this.start; startLoc = this.startLoc;
this.next();
if (this.eat(tt.slash)) {
closingElement = this.jsxParseClosingElementAt(startPos, startLoc);
break contents;
}
children.push(this.jsxParseElementAt(startPos, startLoc));
break;
case tt.jsxText:
children.push(this.parseExprAtom());
break;
case tt.braceL:
children.push(this.jsxParseExpressionContainer());
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;
if (this.type === tt.relational && this.value === "<") {
this.raise(this.start, "Adjacent JSX elements must be wrapped in an enclosing tag");
}
return this.finishNode(node, "JSXElement");
};
// Parses entire JSX element from current position.
pp.jsxParseElement = function() {
var startPos = this.start, startLoc = this.startLoc;
this.next();
return this.jsxParseElementAt(startPos, startLoc);
};
export default function(instance) {
instance.extend("parseExprAtom", function(inner) {
return function(refShortHandDefaultPos) {
if (this.type === tt.jsxText)
return this.parseLiteral(this.value);
else if (this.type === tt.jsxTagStart)
return this.jsxParseElement();
else
return inner.call(this, refShortHandDefaultPos);
};
});
instance.extend("readToken", function(inner) {
return function(code) {
var context = this.curContext();
if (context === tc.j_expr) return this.jsxReadToken();
if (context === tc.j_oTag || context === tc.j_cTag) {
if (isIdentifierStart(code)) return this.jsxReadWord();
if (code === 62) {
++this.pos;
return this.finishToken(tt.jsxTagEnd);
}
if ((code === 34 || code === 39) && context === tc.j_oTag)
return this.jsxReadString(code);
}
if (code === 60 && this.exprAllowed) {
++this.pos;
return this.finishToken(tt.jsxTagStart);
}
return inner.call(this, code);
};
});
instance.extend("updateContext", function(inner) {
return function(prevType) {
if (this.type === tt.braceL) {
var curContext = this.curContext();
if (curContext === tc.j_oTag) this.context.push(tc.b_expr);
else if (curContext === tc.j_expr) this.context.push(tc.b_tmpl);
else inner.call(this, prevType);
this.exprAllowed = true;
} else if (this.type === tt.slash && prevType === tt.jsxTagStart) {
this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
this.context.push(tc.j_cTag); // reconsider as closing tag context
this.exprAllowed = false;
} else {
return inner.call(this, prevType);
}
};
});
};

255
src/plugins/jsx/xhtml.js Normal file
View File

@ -0,0 +1,255 @@
export default {
quot: "\u0022",
amp: "&",
apos: "\u0027",
lt: "<",
gt: ">",
nbsp: "\u00A0",
iexcl: "\u00A1",
cent: "\u00A2",
pound: "\u00A3",
curren: "\u00A4",
yen: "\u00A5",
brvbar: "\u00A6",
sect: "\u00A7",
uml: "\u00A8",
copy: "\u00A9",
ordf: "\u00AA",
laquo: "\u00AB",
not: "\u00AC",
shy: "\u00AD",
reg: "\u00AE",
macr: "\u00AF",
deg: "\u00B0",
plusmn: "\u00B1",
sup2: "\u00B2",
sup3: "\u00B3",
acute: "\u00B4",
micro: "\u00B5",
para: "\u00B6",
middot: "\u00B7",
cedil: "\u00B8",
sup1: "\u00B9",
ordm: "\u00BA",
raquo: "\u00BB",
frac14: "\u00BC",
frac12: "\u00BD",
frac34: "\u00BE",
iquest: "\u00BF",
Agrave: "\u00C0",
Aacute: "\u00C1",
Acirc: "\u00C2",
Atilde: "\u00C3",
Auml: "\u00C4",
Aring: "\u00C5",
AElig: "\u00C6",
Ccedil: "\u00C7",
Egrave: "\u00C8",
Eacute: "\u00C9",
Ecirc: "\u00CA",
Euml: "\u00CB",
Igrave: "\u00CC",
Iacute: "\u00CD",
Icirc: "\u00CE",
Iuml: "\u00CF",
ETH: "\u00D0",
Ntilde: "\u00D1",
Ograve: "\u00D2",
Oacute: "\u00D3",
Ocirc: "\u00D4",
Otilde: "\u00D5",
Ouml: "\u00D6",
times: "\u00D7",
Oslash: "\u00D8",
Ugrave: "\u00D9",
Uacute: "\u00DA",
Ucirc: "\u00DB",
Uuml: "\u00DC",
Yacute: "\u00DD",
THORN: "\u00DE",
szlig: "\u00DF",
agrave: "\u00E0",
aacute: "\u00E1",
acirc: "\u00E2",
atilde: "\u00E3",
auml: "\u00E4",
aring: "\u00E5",
aelig: "\u00E6",
ccedil: "\u00E7",
egrave: "\u00E8",
eacute: "\u00E9",
ecirc: "\u00EA",
euml: "\u00EB",
igrave: "\u00EC",
iacute: "\u00ED",
icirc: "\u00EE",
iuml: "\u00EF",
eth: "\u00F0",
ntilde: "\u00F1",
ograve: "\u00F2",
oacute: "\u00F3",
ocirc: "\u00F4",
otilde: "\u00F5",
ouml: "\u00F6",
divide: "\u00F7",
oslash: "\u00F8",
ugrave: "\u00F9",
uacute: "\u00FA",
ucirc: "\u00FB",
uuml: "\u00FC",
yacute: "\u00FD",
thorn: "\u00FE",
yuml: "\u00FF",
OElig: "\u0152",
oelig: "\u0153",
Scaron: "\u0160",
scaron: "\u0161",
Yuml: "\u0178",
fnof: "\u0192",
circ: "\u02C6",
tilde: "\u02DC",
Alpha: "\u0391",
Beta: "\u0392",
Gamma: "\u0393",
Delta: "\u0394",
Epsilon: "\u0395",
Zeta: "\u0396",
Eta: "\u0397",
Theta: "\u0398",
Iota: "\u0399",
Kappa: "\u039A",
Lambda: "\u039B",
Mu: "\u039C",
Nu: "\u039D",
Xi: "\u039E",
Omicron: "\u039F",
Pi: "\u03A0",
Rho: "\u03A1",
Sigma: "\u03A3",
Tau: "\u03A4",
Upsilon: "\u03A5",
Phi: "\u03A6",
Chi: "\u03A7",
Psi: "\u03A8",
Omega: "\u03A9",
alpha: "\u03B1",
beta: "\u03B2",
gamma: "\u03B3",
delta: "\u03B4",
epsilon: "\u03B5",
zeta: "\u03B6",
eta: "\u03B7",
theta: "\u03B8",
iota: "\u03B9",
kappa: "\u03BA",
lambda: "\u03BB",
mu: "\u03BC",
nu: "\u03BD",
xi: "\u03BE",
omicron: "\u03BF",
pi: "\u03C0",
rho: "\u03C1",
sigmaf: "\u03C2",
sigma: "\u03C3",
tau: "\u03C4",
upsilon: "\u03C5",
phi: "\u03C6",
chi: "\u03C7",
psi: "\u03C8",
omega: "\u03C9",
thetasym: "\u03D1",
upsih: "\u03D2",
piv: "\u03D6",
ensp: "\u2002",
emsp: "\u2003",
thinsp: "\u2009",
zwnj: "\u200C",
zwj: "\u200D",
lrm: "\u200E",
rlm: "\u200F",
ndash: "\u2013",
mdash: "\u2014",
lsquo: "\u2018",
rsquo: "\u2019",
sbquo: "\u201A",
ldquo: "\u201C",
rdquo: "\u201D",
bdquo: "\u201E",
dagger: "\u2020",
Dagger: "\u2021",
bull: "\u2022",
hellip: "\u2026",
permil: "\u2030",
prime: "\u2032",
Prime: "\u2033",
lsaquo: "\u2039",
rsaquo: "\u203A",
oline: "\u203E",
frasl: "\u2044",
euro: "\u20AC",
image: "\u2111",
weierp: "\u2118",
real: "\u211C",
trade: "\u2122",
alefsym: "\u2135",
larr: "\u2190",
uarr: "\u2191",
rarr: "\u2192",
darr: "\u2193",
harr: "\u2194",
crarr: "\u21B5",
lArr: "\u21D0",
uArr: "\u21D1",
rArr: "\u21D2",
dArr: "\u21D3",
hArr: "\u21D4",
forall: "\u2200",
part: "\u2202",
exist: "\u2203",
empty: "\u2205",
nabla: "\u2207",
isin: "\u2208",
notin: "\u2209",
ni: "\u220B",
prod: "\u220F",
sum: "\u2211",
minus: "\u2212",
lowast: "\u2217",
radic: "\u221A",
prop: "\u221D",
infin: "\u221E",
ang: "\u2220",
and: "\u2227",
or: "\u2228",
cap: "\u2229",
cup: "\u222A",
"int": "\u222B",
there4: "\u2234",
sim: "\u223C",
cong: "\u2245",
asymp: "\u2248",
ne: "\u2260",
equiv: "\u2261",
le: "\u2264",
ge: "\u2265",
sub: "\u2282",
sup: "\u2283",
nsub: "\u2284",
sube: "\u2286",
supe: "\u2287",
oplus: "\u2295",
otimes: "\u2297",
perp: "\u22A5",
sdot: "\u22C5",
lceil: "\u2308",
rceil: "\u2309",
lfloor: "\u230A",
rfloor: "\u230B",
lang: "\u2329",
rang: "\u232A",
loz: "\u25CA",
spades: "\u2660",
clubs: "\u2663",
hearts: "\u2665",
diams: "\u2666"
};

View File

@ -1,79 +1,88 @@
import {reservedWords, keywords} from "./identifier"
import {types as tt} from "./tokentype"
import {lineBreak} from "./whitespace"
import {reservedWords, keywords} from "./identifier";
import {types as tt} from "./tokentype";
import {lineBreak} from "./whitespace";
export function Parser(options, input, startPos) {
this.options = options
this.sourceFile = this.options.sourceFile || null
this.isKeyword = keywords[this.options.ecmaVersion >= 6 ? 6 : 5]
this.isReservedWord = reservedWords[this.options.ecmaVersion]
this.input = input
this.loadPlugins(this.options.plugins)
this.options = options;
this.sourceFile = this.options.sourceFile || null;
this.isKeyword = keywords[this.options.ecmaVersion >= 6 ? 6 : 5];
this.isReservedWord = reservedWords[this.options.ecmaVersion];
this.input = input;
this.loadPlugins(this.options.plugins);
// Set up token state
// The current position of the tokenizer in the input.
if (startPos) {
this.pos = startPos
this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos))
this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length
this.pos = startPos;
this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos));
this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;
} else {
this.pos = this.lineStart = 0
this.curLine = 1
this.pos = this.lineStart = 0;
this.curLine = 1;
}
// Properties of the current token:
// Its type
this.type = tt.eof
this.type = tt.eof;
// For tokens that include more information than their type, the value
this.value = null
this.value = null;
// Its start and end offset
this.start = this.end = this.pos
this.start = this.end = this.pos;
// And, if locations are used, the {line, column} object
// corresponding to those offsets
this.startLoc = this.endLoc = null
this.startLoc = this.endLoc = this.curPosition();
// Position information for the previous token
this.lastTokEndLoc = this.lastTokStartLoc = null
this.lastTokStart = this.lastTokEnd = this.pos
this.lastTokEndLoc = this.lastTokStartLoc = null;
this.lastTokStart = this.lastTokEnd = this.pos;
// The context stack is used to superficially track syntactic
// context to predict whether a regular expression is allowed in a
// given position.
this.context = this.initialContext()
this.exprAllowed = true
this.context = this.initialContext();
this.exprAllowed = true;
// Figure out if it's a module code.
this.inModule = this.options.sourceType === "module"
this.strict = this.options.strictMode === false ? false : this.inModule
this.inModule = this.options.sourceType === "module";
this.strict = this.options.strictMode === false ? false : this.inModule;
// Used to signify the start of a potential arrow function
this.potentialArrowAt = -1
this.potentialArrowAt = -1;
// Flags to track whether we are in a function, a generator.
this.inFunction = this.inGenerator = false
this.inFunction = this.inGenerator = false;
// Labels in scope.
this.labels = []
this.labels = [];
this.decorators = []
this.decorators = [];
// If enabled, skip leading hashbang line.
if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === '#!')
this.skipLineComment(2)
if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === "#!") {
this.skipLineComment(2);
}
}
Parser.prototype.extend = function(name, f) {
this[name] = f(this[name])
}
Parser.prototype.extend = function (name, f) {
this[name] = f(this[name]);
};
// Registered plugins
export const plugins = {}
export const plugins = {};
Parser.prototype.loadPlugins = function(plugins) {
Parser.prototype.loadPlugins = function (plugins) {
for (let name in plugins) {
let plugin = exports.plugins[name]
if (!plugin) throw new Error("Plugin '" + name + "' not found")
plugin(this, plugins[name])
let plugin = exports.plugins[name];
if (!plugin) throw new Error(`Plugin '${name}' not found`);
plugin(this, plugins[name]);
}
}
};
Parser.prototype.parse = function () {
return new Promise((resolve) => {
let node = this.options.program || this.startNode();
this.nextToken();
resolve(this.parseTopLevel(node));
});
};

File diff suppressed because it is too large Load Diff

View File

@ -2,16 +2,16 @@
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design
import {Parser} from "./state"
import {types as tt} from "./tokentype"
import {lineBreak} from "./whitespace"
import {Parser} from "./state";
import {types as tt} from "./tokentype";
import {lineBreak} from "./whitespace";
export class TokContext {
constructor(token, isExpr, preserveSpace, override) {
this.token = token
this.isExpr = isExpr
this.preserveSpace = preserveSpace
this.override = override
this.token = token;
this.isExpr = !!isExpr;
this.preserveSpace = !!preserveSpace;
this.override = override;
}
}
@ -23,85 +23,96 @@ export const types = {
p_expr: new TokContext("(", true),
q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()),
f_expr: new TokContext("function", true)
}
};
const pp = Parser.prototype
const pp = Parser.prototype;
pp.initialContext = function() {
return [types.b_stat]
}
pp.initialContext = function () {
return [types.b_stat];
};
pp.braceIsBlock = function (prevType) {
if (prevType === tt.colon) {
let parent = this.curContext();
if (parent === types.b_stat || parent === types.b_expr)
return !parent.isExpr;
}
pp.braceIsBlock = function(prevType) {
let parent
if (prevType === tt.colon && (parent = this.curContext()).token == "{")
return !parent.isExpr
if (prevType === tt._return)
return lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof)
return true
if (prevType == tt.braceL)
return this.curContext() === types.b_stat
return !this.exprAllowed
}
return lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
pp.updateContext = function(prevType) {
let update, type = this.type
if (type.keyword && prevType == tt.dot)
this.exprAllowed = false
else if (update = type.updateContext)
update.call(this, prevType)
else
this.exprAllowed = type.beforeExpr
}
if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof)
return true;
if (prevType === tt.braceL)
return this.curContext() === types.b_stat;
return !this.exprAllowed;
};
pp.updateContext = function (prevType) {
let update, type = this.type;
if (type.keyword && prevType === tt.dot) {
this.exprAllowed = false;
} else if (update = type.updateContext) {
update.call(this, prevType);
} else {
this.exprAllowed = type.beforeExpr;
}
};
// Token-specific context update code
tt.parenR.updateContext = tt.braceR.updateContext = function() {
if (this.context.length == 1) {
this.exprAllowed = true
return
tt.parenR.updateContext = tt.braceR.updateContext = function () {
if (this.context.length === 1) {
this.exprAllowed = true;
return;
}
let out = this.context.pop()
let out = this.context.pop();
if (out === types.b_stat && this.curContext() === types.f_expr) {
this.context.pop()
this.exprAllowed = false
this.context.pop();
this.exprAllowed = false;
} else if (out === types.b_tmpl) {
this.exprAllowed = true
this.exprAllowed = true;
} else {
this.exprAllowed = !out.isExpr
this.exprAllowed = !out.isExpr;
}
}
};
tt.braceL.updateContext = function(prevType) {
this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr)
this.exprAllowed = true
}
tt.braceL.updateContext = function (prevType) {
this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);
this.exprAllowed = true;
};
tt.dollarBraceL.updateContext = function() {
this.context.push(types.b_tmpl)
this.exprAllowed = true
}
tt.dollarBraceL.updateContext = function () {
this.context.push(types.b_tmpl);
this.exprAllowed = true;
};
tt.parenL.updateContext = function(prevType) {
let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while
this.context.push(statementParens ? types.p_stat : types.p_expr)
this.exprAllowed = true
}
tt.parenL.updateContext = function (prevType) {
let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while;
this.context.push(statementParens ? types.p_stat : types.p_expr);
this.exprAllowed = true;
};
tt.incDec.updateContext = function() {
tt.incDec.updateContext = function () {
// tokExprAllowed stays unchanged
}
};
tt._function.updateContext = function() {
if (this.curContext() !== types.b_stat)
this.context.push(types.f_expr)
this.exprAllowed = false
}
tt._function.updateContext = function () {
if (this.curContext() !== types.b_stat) {
this.context.push(types.f_expr);
}
tt.backQuote.updateContext = function() {
if (this.curContext() === types.q_tmpl)
this.context.pop()
else
this.context.push(types.q_tmpl)
this.exprAllowed = false
}
this.exprAllowed = false;
};
tt.backQuote.updateContext = function () {
if (this.curContext() === types.q_tmpl) {
this.context.pop();
} else {
this.context.push(types.q_tmpl);
}
this.exprAllowed = false;
};

File diff suppressed because it is too large Load Diff

View File

@ -18,24 +18,24 @@
export class TokenType {
constructor(label, conf = {}) {
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
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})
return new TokenType(name, {beforeExpr: true, binop: prec});
}
const beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true}
const beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true};
export const types = {
num: new TokenType("num", startsExpr),
@ -95,52 +95,52 @@ export const types = {
star: binop("*", 10),
slash: binop("/", 10),
exponent: new TokenType("**", {beforeExpr: true, binop: 11, rightAssociative: true})
}
};
// Map keyword names to token types.
export const keywords = {}
export const keywords = {};
// Succinct definitions of keyword token types
function kw(name, options = {}) {
options.keyword = name
keywords[name] = types["_" + name] = new TokenType(name, options)
options.keyword = name;
keywords[name] = types["_" + name] = new TokenType(name, options);
}
kw("break")
kw("case", beforeExpr)
kw("catch")
kw("continue")
kw("debugger")
kw("default", beforeExpr)
kw("do", {isLoop: true})
kw("else", beforeExpr)
kw("finally")
kw("for", {isLoop: true})
kw("function", startsExpr)
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})
kw("break");
kw("case", beforeExpr);
kw("catch");
kw("continue");
kw("debugger");
kw("default", beforeExpr);
kw("do", {isLoop: true});
kw("else", beforeExpr);
kw("finally");
kw("for", {isLoop: true});
kw("function", startsExpr);
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});

View File

@ -1,9 +1,5 @@
export function isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]"
}
// Checks if an object has a property.
export function has(obj, propName) {
return Object.prototype.hasOwnProperty.call(obj, propName)
return Object.prototype.hasOwnProperty.call(obj, propName);
}

View File

@ -1,12 +1,12 @@
// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.
export const lineBreak = /\r\n?|\n|\u2028|\u2029/
export const lineBreakG = new RegExp(lineBreak.source, "g")
export const lineBreak = /\r\n?|\n|\u2028|\u2029/;
export const lineBreakG = new RegExp(lineBreak.source, "g");
export function isNewLine(code) {
return code === 10 || code === 13 || code === 0x2028 || code == 0x2029
return code === 10 || code === 13 || code === 0x2028 || code === 0x2029;
}
export const nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/
export const nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;

7
test/_browser.js Normal file
View File

@ -0,0 +1,7 @@
if (process.browser) {
require("./tests");
require("./tests-jsx");
require("./tests-harmony");
require("./tests-flow");
require("./tests-babel");
}

87
test/driver.js Executable file
View File

@ -0,0 +1,87 @@
var parse = require("../lib").parse;
exports.test = function(code, ast, options) {
buildTest({code: code, ast: ast, options: options});
};
exports.testFail = function(code, message, options) {
buildTest({code: code, error: message, options: options});
};
exports.testAssert = function(code, assert, options) {
buildTest({code: code, assert: assert, options: options});
};
function buildTest(config) {
test(config.code, function () {
return runTest(config);
});
}
function runTest(test) {
if (test.filter && !test.filter(test)) return;
var testOpts = test.options || {locations: true};
var expected = {};
if (expected.onComment = testOpts.onComment)
testOpts.onComment = []
if (expected.onToken = testOpts.onToken)
testOpts.onToken = [];
return parse(test.code, testOpts).then(function (ast) {
if (test.error) {
throw new Error("Expected error message: " + test.error + ". But parsing succeeded.");
} else if (test.assert) {
var error = test.assert(ast);
if (error) throw new Error("Assertion failed: " + error);
} 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) throw new Error(mis);
}
}, function (err) {
if (test.error) {
if (err.message === test.error) {
return;
} else {
throw new Error("Expected error message: " + test.error + ". Got error message: " + err.message);
}
}
throw err;
});
};
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);
}
}
};

2
test/mocha.opts Normal file
View File

@ -0,0 +1,2 @@
--reporter dot
--ui tdd

3433
test/tests-babel.js Normal file

File diff suppressed because it is too large Load Diff

11245
test/tests-flow.js Normal file

File diff suppressed because it is too large Load Diff

15331
test/tests-harmony.js Executable file

File diff suppressed because it is too large Load Diff

3654
test/tests-jsx.js Normal file

File diff suppressed because it is too large Load Diff

28853
test/tests.js Executable file

File diff suppressed because it is too large Load Diff