diff --git a/.eslintrc b/.eslintrc
index 2782382c7d..ea7b1eed61 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -19,7 +19,8 @@
"no-loop-func": 0,
"no-unreachable": 0,
"no-labels": 0,
- "no-process-exit": 0
+ "no-process-exit": 0,
+ "camelcase": 0
},
"env": {
"node": true
diff --git a/.gitignore b/.gitignore
index 7256bc9d3c..f636502c0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
.DS_Store
node_modules
-test/core/tmp
+test/tmp
*.log
*.cache
/templates.json
diff --git a/.travis.yml b/.travis.yml
index 85b05c77da..d071282eb3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,6 @@ cache:
node_js:
- iojs
- - "0.8"
- "0.10"
- "0.12"
diff --git a/packages/babel-cli/src/babel-external-helpers b/packages/babel-cli/src/babel-external-helpers
index 8d2d907306..8c85f0352c 100755
--- a/packages/babel-cli/src/babel-external-helpers
+++ b/packages/babel-cli/src/babel-external-helpers
@@ -10,6 +10,4 @@ commander.option("-t, --output-type [type]", "Type of output (global|umd|var)",
commander.usage("[options]");
commander.parse(process.argv);
-util.ensureTemplates().then(function () {
- console.log(runtime(commander.whitelist, commander.outputType));
-});
+console.log(runtime(commander.whitelist, commander.outputType));
diff --git a/packages/babel-cli/src/babel/util.js b/packages/babel-cli/src/babel/util.js
index e83f5faaac..8ade28d1d6 100644
--- a/packages/babel-cli/src/babel/util.js
+++ b/packages/babel-cli/src/babel/util.js
@@ -35,7 +35,7 @@ exports.transform = function (filename, code, opts) {
opts.ignore = null;
opts.only = null;
- var result = babel.__plsDontUseThis(code, opts);
+ var result = babel.transform(code, opts);
result.filename = filename;
result.actual = code;
return result;
diff --git a/packages/babel-cli/test/tmp/bar2.js b/packages/babel-cli/test/tmp/bar2.js
new file mode 100644
index 0000000000..26c2d21a78
--- /dev/null
+++ b/packages/babel-cli/test/tmp/bar2.js
@@ -0,0 +1,2 @@
+var bar = () => console.log("bar");
+bar();
\ No newline at end of file
diff --git a/packages/babel-cli/test/tmp/foo2.js b/packages/babel-cli/test/tmp/foo2.js
new file mode 100644
index 0000000000..4c4e8d7d6d
--- /dev/null
+++ b/packages/babel-cli/test/tmp/foo2.js
@@ -0,0 +1,5 @@
+import "./bar2";
+import "./not_node_modules";
+
+var foo = () => console.log("foo");
+foo();
\ No newline at end of file
diff --git a/packages/babel-cli/test/tmp/not_node_modules.jsx b/packages/babel-cli/test/tmp/not_node_modules.jsx
new file mode 100644
index 0000000000..d46284bf2d
--- /dev/null
+++ b/packages/babel-cli/test/tmp/not_node_modules.jsx
@@ -0,0 +1,10 @@
+/*
+The purpose of this file is to test that the node_modules check in the require
+hook doesn't mistakenly exclude something like "not_node_modules". To pass, this
+file merely needs to be transpiled. The transpiled code won't, and doesn't need
+to, execute without error. It won't execute because React will be undefined.
+*/
+try {
+
- Babylon is a streaming parser for Babel. + Babylon is a JavaScript parser used in Babel.
+ +---- + +## 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. diff --git a/packages/babylon/package.json b/packages/babylon/package.json index 767cde522a..b5eb00a005 100644 --- a/packages/babylon/package.json +++ b/packages/babylon/package.json @@ -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" } diff --git a/packages/babylon/src/expression.js b/packages/babylon/src/expression.js index 2126431a17..2fc04783a9 100755 --- a/packages/babylon/src/expression.js +++ b/packages/babylon/src/expression.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 { diff --git a/packages/babylon/src/index.js b/packages/babylon/src/index.js index 6b0541ea41..c9cf42ffe5 100755 --- a/packages/babylon/src/index.js +++ b/packages/babylon/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(); } diff --git a/packages/babylon/src/location.js b/packages/babylon/src/location.js index 5ab0028460..c6832b04da 100755 --- a/packages/babylon/src/location.js +++ b/packages/babylon/src/location.js @@ -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. diff --git a/packages/babylon/src/lookahead.js b/packages/babylon/src/lookahead.js index f6f456cfa3..5c1718e45e 100644 --- a/packages/babylon/src/lookahead.js +++ b/packages/babylon/src/lookahead.js @@ -1,4 +1,4 @@ -import {Parser} from "./state"; +import { Parser } from "./state"; const pp = Parser.prototype; diff --git a/packages/babylon/src/lval.js b/packages/babylon/src/lval.js index 6a6dc98b7f..7b083a0f9c 100755 --- a/packages/babylon/src/lval.js +++ b/packages/babylon/src/lval.js @@ -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; diff --git a/packages/babylon/src/node.js b/packages/babylon/src/node.js index e58a28dc55..0d40e97cc7 100755 --- a/packages/babylon/src/node.js +++ b/packages/babylon/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; } } diff --git a/packages/babylon/src/options.js b/packages/babylon/src/options.js index 88ea8fce47..bdbab22040 100755 --- a/packages/babylon/src/options.js +++ b/packages/babylon/src/options.js @@ -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: diff --git a/packages/babylon/src/parseutil.js b/packages/babylon/src/parseutil.js index 2fae5e6729..1834724bdc 100755 --- a/packages/babylon/src/parseutil.js +++ b/packages/babylon/src/parseutil.js @@ -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; diff --git a/packages/babylon/src/plugins/flow.js b/packages/babylon/src/plugins/flow.js index a219d6b37d..6efb79b961 100644 --- a/packages/babylon/src/plugins/flow.js +++ b/packages/babylon/src/plugins/flow.js @@ -820,4 +820,4 @@ export default function (instance) { } }; }); -}; +} diff --git a/packages/babylon/src/plugins/jsx/index.js b/packages/babylon/src/plugins/jsx/index.js index 19ff2e3697..3e3368f476 100644 --- a/packages/babylon/src/plugins/jsx/index.js +++ b/packages/babylon/src/plugins/jsx/index.js @@ -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) { } }; }); -}; +} diff --git a/packages/babylon/src/state.js b/packages/babylon/src/state.js index 651e485a7e..dd48c064e7 100755 --- a/packages/babylon/src/state.js +++ b/packages/babylon/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); }; diff --git a/packages/babylon/src/statement.js b/packages/babylon/src/statement.js index e62a54c2d4..bc441511d0 100755 --- a/packages/babylon/src/statement.js +++ b/packages/babylon/src/statement.js @@ -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")); } diff --git a/packages/babylon/src/tokencontext.js b/packages/babylon/src/tokencontext.js index 8395160c8d..16853534ad 100755 --- a/packages/babylon/src/tokencontext.js +++ b/packages/babylon/src/tokencontext.js @@ -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) { diff --git a/packages/babylon/src/tokenize.js b/packages/babylon/src/tokenize.js index 8cade26638..fbe25a01f2 100755 --- a/packages/babylon/src/tokenize.js +++ b/packages/babylon/src/tokenize.js @@ -1,8 +1,8 @@ -import {isIdentifierStart, isIdentifierChar} from "./identifier"; -import {types as tt, keywords as keywordTypes} from "./tokentype"; -import {Parser} from "./state"; -import {SourceLocation} from "./location"; -import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace"; +import { isIdentifierStart, isIdentifierChar } from "./identifier"; +import { types as tt, keywords as keywordTypes } from "./tokentype"; +import { Parser } from "./state"; +import { SourceLocation } from "./location"; +import { lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace } from "./whitespace"; // Object type used to represent tokens. Note that normally, tokens // simply exist as properties on the parser object. This is only @@ -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}); }; diff --git a/packages/babylon/test/driver.js b/packages/babylon/test/driver.js index 3a02fcf9e2..527fda18df 100755 --- a/packages/babylon/test/driver.js +++ b/packages/babylon/test/driver.js @@ -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); } diff --git a/packages/babylon/test/tests-jsx.js b/packages/babylon/test/tests-jsx.js index e31c41a1d4..02f86a1ab3 100644 --- a/packages/babylon/test/tests-jsx.js +++ b/packages/babylon/test/tests-jsx.js @@ -3636,7 +3636,9 @@ if (typeof exports !== "undefined") { var testFail = require("./driver.js").testFail; } -testFail("var x =