more architectural changes
This commit is contained in:
parent
423d8c510d
commit
ff6620c8ea
10
README.md
10
README.md
@ -3,5 +3,13 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Babylon is a streaming parser for <a href="https://github.com/babel/babel">Babel</a>.
|
||||
Babylon is a JavaScript parser used in <a href="https://github.com/babel/babel">Babel</a>.
|
||||
</p>
|
||||
|
||||
----
|
||||
|
||||
## Credits
|
||||
|
||||
Heavily based on [acorn](https://github.com/marijnh/acorn) and [acorn-jsx](https://github.com/RReverser/acorn-jsx).
|
||||
Significant diversions expected to occur in the future such as streaming, EBNF definitions, sweet.js integration,
|
||||
interspacial parsing, comment attachment etc.
|
||||
|
||||
@ -5,8 +5,5 @@
|
||||
"homepage": "https://babeljs.io/",
|
||||
"license": "MIT",
|
||||
"repository": "babel/babel",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"bluebird": "^2.9.33"
|
||||
}
|
||||
"main": "lib/index.js"
|
||||
}
|
||||
|
||||
@ -16,10 +16,9 @@
|
||||
//
|
||||
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
|
||||
|
||||
import {types as tt} from "./tokentype";
|
||||
import {Parser} from "./state";
|
||||
import {reservedWords} from "./identifier";
|
||||
import {has} from "./util";
|
||||
import { types as tt } from "./tokentype";
|
||||
import { Parser } from "./state";
|
||||
import { reservedWords } from "./identifier";
|
||||
|
||||
const pp = Parser.prototype;
|
||||
|
||||
@ -29,14 +28,15 @@ const pp = Parser.prototype;
|
||||
// strict mode, init properties are also not allowed to be repeated.
|
||||
|
||||
pp.checkPropClash = function (prop, propHash) {
|
||||
if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))
|
||||
return;
|
||||
if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) return;
|
||||
|
||||
let key = prop.key, name;
|
||||
switch (key.type) {
|
||||
case "Identifier": name = key.name; break;
|
||||
case "Literal": name = String(key.value); break;
|
||||
default: return;
|
||||
case "Identifier": name = key.name; break;
|
||||
case "Literal": name = String(key.value); break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
let kind = prop.kind;
|
||||
if (this.options.ecmaVersion >= 6) {
|
||||
if (name === "__proto__" && kind === "init") {
|
||||
@ -45,6 +45,7 @@ pp.checkPropClash = function (prop, propHash) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let other;
|
||||
if (propHash[name]) {
|
||||
other = propHash[name];
|
||||
@ -196,10 +197,11 @@ pp.parseMaybeUnary = function (refShorthandDefaultPos) {
|
||||
this.next();
|
||||
node.argument = this.parseMaybeUnary();
|
||||
if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
|
||||
if (update) this.checkLVal(node.argument);
|
||||
else if (this.strict && node.operator === "delete" &&
|
||||
node.argument.type === "Identifier")
|
||||
if (update) {
|
||||
this.checkLVal(node.argument);
|
||||
} else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") {
|
||||
this.raise(node.start, "Deleting local variable in strict mode");
|
||||
}
|
||||
return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
|
||||
}
|
||||
let startPos = this.start, startLoc = this.startLoc;
|
||||
@ -666,13 +668,13 @@ pp.parseObjPropValue = function (prop, startPos, startLoc, isGenerator, isAsync,
|
||||
(this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) ||
|
||||
(!this.options.allowReserved && this.isReservedWord(prop.key.name)))
|
||||
this.raise(prop.key.start, "Binding " + prop.key.name);
|
||||
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
|
||||
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
|
||||
} else if (this.type === tt.eq && refShorthandDefaultPos) {
|
||||
if (!refShorthandDefaultPos.start)
|
||||
refShorthandDefaultPos.start = this.start;
|
||||
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
|
||||
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
|
||||
} else {
|
||||
prop.value = prop.key;
|
||||
prop.value = prop.key.__clone();
|
||||
}
|
||||
prop.shorthand = true;
|
||||
} else {
|
||||
|
||||
52
src/index.js
52
src/index.js
@ -1,26 +1,5 @@
|
||||
// Acorn is a tiny, fast JavaScript parser written in JavaScript.
|
||||
//
|
||||
// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
|
||||
// various contributors and released under an MIT license.
|
||||
//
|
||||
// Git repositories for Acorn are available at
|
||||
//
|
||||
// http://marijnhaverbeke.nl/git/acorn
|
||||
// https://github.com/marijnh/acorn.git
|
||||
//
|
||||
// Please use the [github bug tracker][ghbt] to report issues.
|
||||
//
|
||||
// [ghbt]: https://github.com/marijnh/acorn/issues
|
||||
//
|
||||
// This file defines the main parser interface. The library also comes
|
||||
// with a [error-tolerant parser][dammit] and an
|
||||
// [abstract syntax tree walker][walk], defined in other files.
|
||||
//
|
||||
// [dammit]: acorn_loose.js
|
||||
// [walk]: util/walk.js
|
||||
|
||||
import {Parser, plugins} from "./state";
|
||||
import {getOptions} from "./options";
|
||||
import { Parser, plugins } from "./state";
|
||||
import { getOptions } from "./options";
|
||||
import "./parseutil";
|
||||
import "./statement";
|
||||
import "./lval";
|
||||
@ -29,29 +8,22 @@ 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";
|
||||
|
||||
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
|
||||
// returns an abstract syntax tree as specified by [Mozilla parser
|
||||
// API][api].
|
||||
//
|
||||
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
|
||||
|
||||
export function parse(input, options) {
|
||||
return new Parser(getOptions(options), input).parse();
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
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.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {Parser} from "./state";
|
||||
import { Parser } from "./state";
|
||||
|
||||
const pp = Parser.prototype;
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
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";
|
||||
|
||||
const pp = Parser.prototype;
|
||||
|
||||
|
||||
30
src/node.js
30
src/node.js
@ -1,5 +1,5 @@
|
||||
import {Parser} from "./state";
|
||||
import {SourceLocation} from "./location";
|
||||
import { Parser } from "./state";
|
||||
import { SourceLocation } from "./location";
|
||||
|
||||
// Start an AST node, attaching a start offset.
|
||||
|
||||
@ -10,12 +10,26 @@ export class Node {
|
||||
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];
|
||||
|
||||
if (parser) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__clone() {
|
||||
var node2 = new Node;
|
||||
for (var key in this) node2[key] = this[key];
|
||||
return node2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {has} 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:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
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;
|
||||
|
||||
|
||||
@ -820,4 +820,4 @@ export default function (instance) {
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ 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";
|
||||
import { isNewLine } from "../../whitespace";
|
||||
|
||||
const HEX_NUMBER = /^[\da-fA-F]+$/;
|
||||
const DECIMAL_NUMBER = /^\d+$/;
|
||||
@ -40,37 +40,39 @@ var pp = Parser.prototype;
|
||||
pp.jsxReadToken = function() {
|
||||
var out = "", chunkStart = this.pos;
|
||||
for (;;) {
|
||||
if (this.pos >= this.input.length)
|
||||
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);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
return this.finishToken(tt.jsxText, out);
|
||||
|
||||
case 38: // "&"
|
||||
out += this.input.slice(chunkStart, this.pos);
|
||||
out += this.jsxReadEntity();
|
||||
chunkStart = this.pos;
|
||||
} else {
|
||||
++this.pos;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isNewLine(ch)) {
|
||||
out += this.input.slice(chunkStart, this.pos);
|
||||
out += this.jsxReadNewLine(true);
|
||||
chunkStart = this.pos;
|
||||
} else {
|
||||
++this.pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -96,8 +98,10 @@ pp.jsxReadNewLine = function(normalizeCRLF) {
|
||||
pp.jsxReadString = function(quote) {
|
||||
var out = "", chunkStart = ++this.pos;
|
||||
for (;;) {
|
||||
if (this.pos >= this.input.length)
|
||||
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) { // "&"
|
||||
@ -119,8 +123,8 @@ pp.jsxReadString = function(quote) {
|
||||
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");
|
||||
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++];
|
||||
@ -168,27 +172,30 @@ pp.jsxReadWord = function() {
|
||||
// Transforms JSX element name to string.
|
||||
|
||||
function getQualifiedJSXName(object) {
|
||||
if (object.type === "JSXIdentifier")
|
||||
if (object.type === "JSXIdentifier") {
|
||||
return object.name;
|
||||
}
|
||||
|
||||
if (object.type === "JSXNamespacedName")
|
||||
if (object.type === "JSXNamespacedName") {
|
||||
return object.namespace.name + ":" + object.name.name;
|
||||
}
|
||||
|
||||
if (object.type === "JSXMemberExpression")
|
||||
return getQualifiedJSXName(object.object) + "." +
|
||||
getQualifiedJSXName(object.property);
|
||||
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)
|
||||
if (this.type === tt.jsxName) {
|
||||
node.name = this.value;
|
||||
else if (this.type.keyword)
|
||||
} else if (this.type.keyword) {
|
||||
node.name = this.type.keyword;
|
||||
else
|
||||
} else {
|
||||
this.unexpected();
|
||||
}
|
||||
this.next();
|
||||
return this.finishNode(node, "JSXIdentifier");
|
||||
};
|
||||
@ -199,6 +206,7 @@ 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();
|
||||
@ -224,18 +232,20 @@ pp.jsxParseElementName = function() {
|
||||
|
||||
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.braceL:
|
||||
var node = this.jsxParseExpressionContainer();
|
||||
if (node.expression.type === "JSXEmptyExpression") {
|
||||
this.raise(node.start, "JSX attributes must only be assigned a non-empty expression");
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
|
||||
case tt.jsxTagStart:
|
||||
case tt.string:
|
||||
return this.parseExprAtom();
|
||||
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");
|
||||
default:
|
||||
this.raise(this.start, "JSX value should be either an expression or a quoted JSX text");
|
||||
}
|
||||
};
|
||||
|
||||
@ -261,9 +271,11 @@ pp.jsxParseEmptyExpression = function() {
|
||||
pp.jsxParseExpressionContainer = function() {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
node.expression = this.type === tt.braceR
|
||||
? this.jsxParseEmptyExpression()
|
||||
: this.parseExpression();
|
||||
if (this.type === tt.braceR) {
|
||||
node.expression = this.jsxParseEmptyExpression();
|
||||
} else {
|
||||
node.expression = this.parseExpression();
|
||||
}
|
||||
this.expect(tt.braceR);
|
||||
return this.finishNode(node, "JSXExpressionContainer");
|
||||
};
|
||||
@ -289,8 +301,9 @@ 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)
|
||||
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");
|
||||
@ -317,32 +330,33 @@ pp.jsxParseElementAt = function(startPos, startLoc) {
|
||||
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.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.jsxText:
|
||||
children.push(this.parseExprAtom());
|
||||
break;
|
||||
|
||||
case tt.braceL:
|
||||
children.push(this.jsxParseExpressionContainer());
|
||||
break;
|
||||
case tt.braceL:
|
||||
children.push(this.jsxParseExpressionContainer());
|
||||
break;
|
||||
|
||||
default:
|
||||
this.unexpected();
|
||||
default:
|
||||
this.unexpected();
|
||||
}
|
||||
}
|
||||
if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
|
||||
this.raise(
|
||||
closingElement.start,
|
||||
"Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">");
|
||||
"Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,4 +432,4 @@ export default function(instance) {
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
14
src/state.js
14
src/state.js
@ -1,6 +1,6 @@
|
||||
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;
|
||||
@ -80,9 +80,7 @@ Parser.prototype.loadPlugins = function (plugins) {
|
||||
};
|
||||
|
||||
Parser.prototype.parse = function () {
|
||||
return new Promise((resolve) => {
|
||||
let node = this.options.program || this.startNode();
|
||||
this.nextToken();
|
||||
resolve(this.parseTopLevel(node));
|
||||
});
|
||||
let node = this.options.program || this.startNode();
|
||||
this.nextToken();
|
||||
return this.parseTopLevel(node);
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
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;
|
||||
|
||||
@ -734,7 +734,7 @@ pp.parseExportSpecifiers = function () {
|
||||
|
||||
let node = this.startNode();
|
||||
node.local = this.parseIdent(this.type === tt._default);
|
||||
node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local;
|
||||
node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local.__clone();
|
||||
nodes.push(this.finishNode(node, "ExportSpecifier"));
|
||||
}
|
||||
|
||||
@ -792,7 +792,7 @@ pp.parseImportSpecifiers = function (node) {
|
||||
|
||||
let specifier = this.startNode();
|
||||
specifier.imported = this.parseIdent(true);
|
||||
specifier.local = this.eatContextual("as") ? this.parseIdent() : specifier.imported;
|
||||
specifier.local = this.eatContextual("as") ? this.parseIdent() : specifier.imported.__clone();
|
||||
this.checkLVal(specifier.local, true);
|
||||
node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
|
||||
}
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
// 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) {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import {isIdentifierStart, isIdentifierChar} from "./identifier";
|
||||
import {types as tt, keywords as keywordTypes} from "./tokentype";
|
||||
import {Parser} from "./state";
|
||||
import {SourceLocation} from "./location";
|
||||
import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace";
|
||||
import { isIdentifierStart, isIdentifierChar } from "./identifier";
|
||||
import { types as tt, keywords as keywordTypes } from "./tokentype";
|
||||
import { Parser } from "./state";
|
||||
import { SourceLocation } from "./location";
|
||||
import { lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace } from "./whitespace";
|
||||
|
||||
// Object type used to represent tokens. Note that normally, tokens
|
||||
// simply exist as properties on the parser object. This is only
|
||||
@ -123,14 +123,15 @@ pp.skipBlockComment = function () {
|
||||
pp.skipLineComment = function (startSkip) {
|
||||
let start = this.pos;
|
||||
let startLoc = this.options.onComment && this.curPosition();
|
||||
let ch = this.input.charCodeAt(this.pos+=startSkip);
|
||||
let ch = this.input.charCodeAt(this.pos += startSkip);
|
||||
while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
|
||||
++this.pos;
|
||||
ch = this.input.charCodeAt(this.pos);
|
||||
}
|
||||
if (this.options.onComment)
|
||||
if (this.options.onComment) {
|
||||
this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,
|
||||
startLoc, this.curPosition());
|
||||
}
|
||||
};
|
||||
|
||||
// Called at the start of the parse and after every token. Skips
|
||||
@ -143,10 +144,12 @@ pp.skipSpace = function() {
|
||||
case 32: case 160: // ' '
|
||||
++this.pos;
|
||||
break;
|
||||
|
||||
case 13:
|
||||
if (this.input.charCodeAt(this.pos + 1) === 10) {
|
||||
++this.pos;
|
||||
}
|
||||
|
||||
case 10: case 8232: case 8233:
|
||||
++this.pos;
|
||||
if (this.options.locations) {
|
||||
@ -154,18 +157,22 @@ pp.skipSpace = function() {
|
||||
this.lineStart = this.pos;
|
||||
}
|
||||
break;
|
||||
|
||||
case 47: // '/'
|
||||
switch (this.input.charCodeAt(this.pos + 1)) {
|
||||
case 42: // '*'
|
||||
this.skipBlockComment();
|
||||
break;
|
||||
|
||||
case 47:
|
||||
this.skipLineComment(2);
|
||||
break;
|
||||
|
||||
default:
|
||||
break loop;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
|
||||
++this.pos;
|
||||
@ -402,28 +409,35 @@ pp.finishOp = function (type, size) {
|
||||
return this.finishToken(type, str);
|
||||
};
|
||||
|
||||
var regexpUnicodeSupport = false;
|
||||
try {
|
||||
new RegExp("\uffff", "u");
|
||||
regexpUnicodeSupport = true;
|
||||
} catch(e) {}
|
||||
|
||||
// Parse a regular expression. Some context-awareness is necessary,
|
||||
// since a '/' inside a '[]' set does not end the expression.
|
||||
|
||||
pp.readRegexp = function () {
|
||||
function tryCreateRegexp(src, flags, throwErrorStart) {
|
||||
try {
|
||||
return new RegExp(src, flags);
|
||||
} catch (e) {
|
||||
if (throwErrorStart !== undefined) {
|
||||
if (e instanceof SyntaxError) this.raise(throwErrorStart, "Error parsing regular expression: " + e.message);
|
||||
this.raise(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var regexpUnicodeSupport = !!tryCreateRegexp("\uffff", "u");
|
||||
|
||||
pp.readRegexp = function() {
|
||||
let escaped, inClass, start = this.pos;
|
||||
for (;;) {
|
||||
if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression");
|
||||
let ch = this.input.charAt(this.pos);
|
||||
if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression");
|
||||
if (!escaped) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else {
|
||||
if (ch === "[") inClass = true;
|
||||
else if (ch === "]" && inClass) inClass = false;
|
||||
else if (ch === "/" && !inClass) break;
|
||||
escaped = ch === "\\";
|
||||
} else {
|
||||
escaped = false;
|
||||
}
|
||||
++this.pos;
|
||||
}
|
||||
@ -456,23 +470,14 @@ pp.readRegexp = function () {
|
||||
}
|
||||
// Detect invalid regular expressions.
|
||||
let value = null;
|
||||
|
||||
// Rhino's regular expression parser is flaky and throws uncatchable exceptions,
|
||||
// so don't do detection if we are running under Rhino
|
||||
if (!isRhino) {
|
||||
try {
|
||||
new RegExp(tmp);
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) this.raise(start, `Error parsing regular expression: ${e.message}`);
|
||||
this.raise(e);
|
||||
}
|
||||
tryCreateRegexp(tmp, undefined, start);
|
||||
// Get a regular expression object for this pattern-flag pair, or `null` in
|
||||
// case the current environment doesn't support the flags it uses.
|
||||
try {
|
||||
value = new RegExp(content, mods);
|
||||
} catch (err) {}
|
||||
value = tryCreateRegexp(content, mods);
|
||||
}
|
||||
|
||||
return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value});
|
||||
};
|
||||
|
||||
|
||||
@ -27,24 +27,9 @@ function runTest(test) {
|
||||
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) {
|
||||
try {
|
||||
var ast = parse(test.code, testOpts);
|
||||
} catch (err) {
|
||||
if (test.error) {
|
||||
if (err.message === test.error) {
|
||||
return;
|
||||
@ -54,7 +39,24 @@ function runTest(test) {
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
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 ppJSON(v) { return v instanceof RegExp ? v.toString() : JSON.stringify(v, null, 2); }
|
||||
|
||||
@ -3636,7 +3636,9 @@ if (typeof exports !== "undefined") {
|
||||
var testFail = require("./driver.js").testFail;
|
||||
}
|
||||
|
||||
testFail("var x = <div>one</div><div>two</div>;", "Adjacent JSX elements must be wrapped in an enclosing tag (1:22)");
|
||||
testFail("var x = <div>one</div><div>two</div>;", "Adjacent JSX elements must be wrapped in an enclosing tag (1:22)", {
|
||||
plugins: { jsx: true }
|
||||
});
|
||||
|
||||
for (var ns in fbTestFixture) {
|
||||
ns = fbTestFixture[ns];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user