diff --git a/.gitignore b/.gitignore index 3900d8c2ff..73d4121032 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ dist packages/babel-runtime/*.js packages/babel-runtime/helpers/*.js packages/babel-runtime/regenerator/*.js -lib +lib/babel diff --git a/lib/acorn/AUTHORS b/lib/acorn/AUTHORS new file mode 100644 index 0000000000..799a063354 --- /dev/null +++ b/lib/acorn/AUTHORS @@ -0,0 +1,32 @@ +List of Acorn contributors. Updated before every release. + +Alistair Braidwood +Aparajita Fishman +Arian Stolwijk +Artem Govorov +Brandon Mills +Charles Hughes +Conrad Irwin +David Bonnet +impinball +Ingvar Stepanyan +Jiaxing Wang +Johannes Herr +Jürg Lehni +keeyipchan +krator +Marijn Haverbeke +Martin Carlberg +Mathias Bynens +Mathieu 'p01' Henri +Max Schaefer +Mihai Bazon +Mike Rennie +Oskar Schöldström +Paul Harper +Peter Rust +PlNG +r-e-d +Rich Harris +Sebastian McKenzie +zsjforcn diff --git a/lib/acorn/LICENSE b/lib/acorn/LICENSE new file mode 100644 index 0000000000..d4c7fc5838 --- /dev/null +++ b/lib/acorn/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012-2014 by various contributors (see AUTHORS) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/acorn/README.md b/lib/acorn/README.md new file mode 100644 index 0000000000..9aacffb581 --- /dev/null +++ b/lib/acorn/README.md @@ -0,0 +1,4 @@ +# Acorn + +Embedded version with Babel-specific modifications of the excellent +[acorn](https://github.com/marijnh/acorn) parser. diff --git a/lib/acorn/acorn.js b/lib/acorn/acorn.js new file mode 100644 index 0000000000..16e07ab099 --- /dev/null +++ b/lib/acorn/acorn.js @@ -0,0 +1,2934 @@ +// Acorn is a tiny, fast JavaScript parser written in JavaScript. +// +// Acorn was written by Marijn Haverbeke 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 + +(function(root, mod) { + if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS + if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD + mod(root.acorn || (root.acorn = {})); // Plain browser env +})(this, function(exports) { + "use strict"; + + exports.version = "0.12.1"; + + // 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 + + exports.parse = function(input, options) { + var p = new Parser(options, input); + var startPos = p.options.locations ? [p.pos, p.curPosition()] : p.pos; + p.nextToken(); + return p.parseTopLevel(p.options.program || p.startNodeAt(startPos)); + }; + + // A second optional argument can be given to further configure + // the parser process. These options are recognized: + + var defaultOptions = exports.defaultOptions = { + // `ecmaVersion` indicates the ECMAScript version to parse. Must + // be either 3, or 5, or 6. This influences support for strict + // mode, the set of reserved words, support for getters and + // setters and other features. + ecmaVersion: 5, + // `onInsertedSemicolon` can be a callback that will be called + // when a semicolon is automatically inserted. It will be passed + // th position of the comma as an offset, and if `locations` is + // enabled, it is given the location as a `{line, column}` object + // as second argument. + onInsertedSemicolon: null, + // `onTrailingComma` is similar to `onInsertedSemicolon`, but for + // trailing commas. + onTrailingComma: null, + // By default, reserved words are not enforced. Disable + // `allowReserved` to enforce them. When this option has the + // value "never", reserved words and keywords can also not be + // used as property names. + allowReserved: true, + // When enabled, a return at the top level is not considered an + // error. + allowReturnOutsideFunction: false, + // When enabled, import/export statements are not constrained to + // appearing at the top of the program. + allowImportExportEverywhere: false, + // When enabled, hashbang directive in the beginning of file + // is allowed and treated as a line comment. + allowHashBang: false, + // When `locations` is on, `loc` properties holding objects with + // `start` and `end` properties in `{line, column}` form (with + // line being 1-based and column 0-based) will be attached to the + // nodes. + locations: false, + // A function can be passed as `onToken` option, which will + // cause Acorn to call that function with object in the same + // format as tokenize() returns. Note that you are not + // allowed to call the parser from the callback—that will + // corrupt its internal state. + onToken: null, + // A function can be passed as `onComment` option, which will + // cause Acorn to call that function with `(block, text, start, + // end)` parameters whenever a comment is skipped. `block` is a + // boolean indicating whether this is a block (`/* */`) comment, + // `text` is the content of the comment, and `start` and `end` are + // character offsets that denote the start and end of the comment. + // When the `locations` option is on, two more parameters are + // passed, the full `{line, column}` locations of the start and + // end of the comments. Note that you are not allowed to call the + // parser from the callback—that will corrupt its internal state. + onComment: null, + // Nodes have their start and end characters offsets recorded in + // `start` and `end` properties (directly on the node, rather than + // the `loc` object, which holds line/column data. To also add a + // [semi-standardized][range] `range` property holding a `[start, + // end]` array with the same numbers, set the `ranges` option to + // `true`. + // + // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678 + ranges: false, + // It is possible to parse multiple files into a single AST by + // passing the tree produced by parsing the first file as + // `program` option in subsequent parses. This will add the + // toplevel forms of the parsed file to the `Program` (top) node + // of an existing parse tree. + program: null, + // When `locations` is on, you can pass this to record the source + // file in every node's `loc` object. + sourceFile: null, + // This value, if given, is stored in every node, whether + // `locations` is on or off. + directSourceFile: null, + // When enabled, parenthesized expressions are represented by + // (non-standard) ParenthesizedExpression nodes + preserveParens: false, + plugins: {}, + // Babel-specific options + transformers: {}, + strictMode: false + }; + + exports.plugins = {}; + + // This function tries to parse a single expression at a given + // offset in a string. Useful for parsing mixed-language formats + // that embed JavaScript expressions. + + exports.parseExpressionAt = function(input, pos, options) { + var p = new Parser(options, input, pos); + p.nextToken(); + return p.parseExpression(); + }; + + // The `getLineInfo` function is mostly useful when the + // `locations` option is off (for performance reasons) and you + // want to find the line/column position for a given character + // offset. `input` should be the code string that the offset refers + // into. + + var getLineInfo = exports.getLineInfo = function(input, offset) { + for (var line = 1, cur = 0;;) { + lineBreak.lastIndex = cur; + var match = lineBreak.exec(input); + if (match && match.index < offset) { + ++line; + cur = match.index + match[0].length; + } else break; + } + return new Position(line, offset - cur); + }; + + // Object type used to represent tokens. Note that normally, tokens + // simply exist as properties on the parser object. This is only + // used for the onToken callback and the external tokenizer. + + var Token = exports.Token = function(p) { + this.type = p.type; + this.value = p.value; + this.start = p.start; + this.end = p.end; + if (p.options.locations) + this.loc = new SourceLocation(p, p.startLoc, p.endLoc); + if (p.options.ranges) + this.range = [p.start, p.end]; + }; + + // Acorn is organized as a tokenizer and a recursive-descent parser. + // The `tokenize` export provides an interface to the tokenizer. + // Because the tokenizer is optimized for being efficiently used by + // the Acorn parser itself, this interface is somewhat crude and not + // very modular. + + exports.tokenizer = function(input, options) { + return new Parser(options, input); + }; + + // Interpret and default an options object + + function parseOptions(opts) { + var options = {}; + for (var opt in defaultOptions) + options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; + + if (isArray(options.onToken)) { + var tokens = options.onToken; + options.onToken = function (token) { tokens.push(token); }; + } + if (isArray(options.onComment)) + options.onComment = pushComment(options, options.onComment); + + return options; + } + + function pushComment(options, array) { + return function (block, text, start, end, startLoc, endLoc) { + var comment = { + type: block ? 'Block' : 'Line', + value: text, + start: start, + end: end + }; + if (options.locations) + comment.loc = new SourceLocation(this, startLoc, endLoc); + if (options.ranges) + comment.range = [start, end]; + array.push(comment); + }; + } + + // Reused empty array added for node fields that are always empty. + + var empty = []; + + // ## Token types + + // The assignment of fine-grained, information-carrying type objects + // allows the tokenizer to store the information it has about a + // token in a way that is very cheap for the parser to look up. + + // All token type variables start with an underscore, to make them + // easy to recognize. + + // The `beforeExpr` property is used to disambiguate between regular + // expressions and divisions. It is set on all token types that can + // be followed by an expression (thus, a slash after them would be a + // regular expression). + // + // `isLoop` marks a keyword as starting a loop, which is important + // to know when parsing a label, in order to allow or disallow + // continue jumps to that label. + + var TokenType = exports.TokenType = function(label, conf) { + if (!conf) conf = {}; + this.label = label; + this.keyword = conf.keyword; + this.beforeExpr = !!conf.beforeExpr; + this.rightAssociative = !!conf.rightAssociative; + this.isLoop = !!conf.isLoop; + this.isAssign = !!conf.isAssign; + this.prefix = !!conf.prefix; + this.postfix = !!conf.postfix; + this.binop = conf.binop || null; + this.updateContext = null; + }; + + function binop(name, prec) { + return new TokenType(name, {beforeExpr: true, binop: prec}); + } + var beforeExpr = {beforeExpr: true}; + + var tt = exports.tokTypes = { + num: new TokenType("num"), + regexp: new TokenType("regexp"), + string: new TokenType("string"), + name: new TokenType("name"), + eof: new TokenType("eof"), + + // Punctuation token types. + bracketL: new TokenType("[", beforeExpr), + bracketR: new TokenType("]"), + braceL: new TokenType("{", beforeExpr), + braceR: new TokenType("}"), + parenL: new TokenType("(", beforeExpr), + parenR: new TokenType(")"), + comma: new TokenType(",", beforeExpr), + semi: new TokenType(";", beforeExpr), + colon: new TokenType(":", beforeExpr), + dot: new TokenType("."), + question: new TokenType("?", beforeExpr), + arrow: new TokenType("=>", beforeExpr), + template: new TokenType("template"), + ellipsis: new TokenType("...", beforeExpr), + backQuote: new TokenType("`"), + dollarBraceL: new TokenType("${", beforeExpr), + + // Operators. These carry several kinds of properties to help the + // parser use them properly (the presence of these properties is + // what categorizes them as operators). + // + // `binop`, when present, specifies that this operator is a binary + // operator, and will refer to its precedence. + // + // `prefix` and `postfix` mark the operator as a prefix or postfix + // unary operator. + // + // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as + // binary operators with a very low precedence, that should result + // in AssignmentExpression nodes. + + eq: new TokenType("=", {beforeExpr: true, isAssign: true}), + assign: new TokenType("_=", {beforeExpr: true, isAssign: true}), + incDec: new TokenType("++/--", {prefix: true, postfix: true}), + prefix: new TokenType("prefix", {beforeExpr: true, prefix: true}), + logicalOR: binop("||", 1), + logicalAND: binop("&&", 2), + bitwiseOR: binop("|", 3), + bitwiseXOR: binop("^", 4), + bitwiseAND: binop("&", 5), + equality: binop("==/!=", 6), + relational: binop("", 7), + bitShift: binop("<>", 8), + plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true}), + modulo: binop("%", 10), + star: binop("*", 10), + slash: binop("/", 10), + exponent: new TokenType("**", {beforeExpr: true, binop: 11, rightAssociative: true}) + }; + + // Map keyword names to token types. + + var keywordTypes = {}; + + // Succinct definitions of keyword token types + function kw(name, options) { + if (!options) options = {}; + options.keyword = name; + keywordTypes[name] = tt["_" + name] = new TokenType(name, options); + }; + + kw("break"), + kw("case", beforeExpr), + kw("catch"), + kw("continue"), + kw("debugger"), + kw("default"), + kw("do", {isLoop: true}), + kw("else", beforeExpr), + kw("finally"), + kw("for", {isLoop: true}), + kw("function"), + kw("if"), + kw("return", beforeExpr), + kw("switch"); + kw("throw", beforeExpr), + kw("try"), + kw("var"), + kw("let"); + kw("const"); + kw("while", {isLoop: true}); + kw("with"); + kw("new", beforeExpr); + kw("this"); + kw("class"); + kw("extends", beforeExpr); + kw("export"); + kw("import"); + kw("yield", beforeExpr); + kw("null"); + kw("true"); + kw("false"); + kw("in", {beforeExpr: true, binop: 7}); + kw("instanceof", {beforeExpr: true, binop: 7}); + kw("typeof", {beforeExpr: true, prefix: true}); + kw("void", {beforeExpr: true, prefix: true}); + kw("delete", {beforeExpr: true, prefix: true}); + + // This is a trick taken from Esprima. It turns out that, on + // non-Chrome browsers, to check whether a string is in a set, a + // predicate containing a big ugly `switch` statement is faster than + // a regular expression, and on Chrome the two are about on par. + // This function uses `eval` (non-lexical) to produce such a + // predicate from a space-separated string of words. + // + // It starts by sorting the words by length. + + function makePredicate(words) { + words = words.split(" "); + var f = "", cats = []; + out: for (var i = 0; i < words.length; ++i) { + for (var j = 0; j < cats.length; ++j) + if (cats[j][0].length == words[i].length) { + cats[j].push(words[i]); + continue out; + } + cats.push([words[i]]); + } + function compareTo(arr) { + if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; + f += "switch(str){"; + for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; + f += "return true}return false;"; + } + + // When there are more than three length categories, an outer + // switch first dispatches on the lengths, to save on comparisons. + + if (cats.length > 3) { + cats.sort(function(a, b) {return b.length - a.length;}); + f += "switch(str.length){"; + for (var i = 0; i < cats.length; ++i) { + var cat = cats[i]; + f += "case " + cat[0].length + ":"; + compareTo(cat); + } + f += "}"; + + // Otherwise, simply generate a flat `switch` statement. + + } else { + compareTo(words); + } + return new Function("str", f); + } + + // The ECMAScript 3 reserved word list. + + var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"); + + // ECMAScript 5 reserved words. + + var isReservedWord5 = makePredicate("class enum extends super const export import"); + + // The additional reserved words in strict mode. + + var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield"); + + // The forbidden variable names in strict mode. + + var isStrictBadIdWord = 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 isEcma5AndLessKeyword = makePredicate(ecma5AndLessKeywords); + + var isEcma6Keyword = makePredicate(ecma5AndLessKeywords + " let const class extends export import yield"); + + // ## Character categories + + // Big ugly regular expressions that match characters in the + // whitespace, identifier, and identifier-start categories. These + // are only applied when a character is found to actually have a + // code point above 128. + // Generated by `tools/generate-identifier-regex.js`. + + var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; + var 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"; + var 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"; + + var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); + var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); + nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; + + // These are a run-length and offset encoded representation of the + // >0xffff code points that are a valid part of identifiers. The + // offset starts at 0x10000, and each pair of numbers represents an + // offset to the next range, and then a size of the range. They were + // generated by tools/generate-identifier-regex.js + var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541]; + var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239]; + + // This has a complexity linear to the value of the code. The + // assumption is that looking up astral identifier characters is + // rare. + function isInAstralSet(code, set) { + var pos = 0x10000; + for (var i = 0; i < set.length; i += 2) { + pos += set[i]; + if (pos > code) return false; + pos += set[i + 1]; + if (pos >= code) return true; + } + } + + // Whether a single character denotes a newline. + + var newline = exports.newline = /[\n\r\u2028\u2029]/; + + var isNewLine = exports.isNewLine = function(code) { + return code === 10 || code === 13 || code === 0x2028 || code == 0x2029; + }; + + // Matches a whole line break (where CRLF is considered a single + // line break). Used to count lines. + + var lineBreak = exports.lineBreak = /\r\n|[\n\r\u2028\u2029]/g; + + // Test whether a given character code starts an identifier. + + var isIdentifierStart = exports.isIdentifierStart = function(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); + }; + + // Test whether a given character is part of an identifier. + + var isIdentifierChar = exports.isIdentifierChar = function(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); + }; + + // ## Tokenizer + + // These are used when `options.locations` is on, for the + // `startLoc` and `endLoc` properties. + + function Position(line, col) { + this.line = line; + this.column = col; + } + + Position.prototype.offset = function(n) { + return new Position(this.line, this.column + n); + }; + + // ## Parser + + // A recursive descent parser operates by defining functions for all + // syntactic elements, and recursively calling those, each function + // advancing the input stream and returning an AST node. Precedence + // of constructs (for example, the fact that `!x[1]` means `!(x[1])` + // instead of `(!x)[1]` is handled by the fact that the parser + // function that parses unary prefix operators is called first, and + // in turn calls the function that parses `[]` subscripts — that + // way, it'll receive the node for `x[1]` already parsed, and wraps + // *that* in the unary operator node. + // + // Acorn uses an [operator precedence parser][opp] to handle binary + // operator precedence, because it is much more compact than using + // the technique outlined above, which uses different, nesting + // functions to specify precedence, for all of the ten binary + // precedence levels that JavaScript defines. + // + // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser + + var Parser = exports.Parser = function(options, input, startPos) { + this.options = parseOptions(options); + this.loadPlugins(this.options.plugins); + this.sourceFile = this.options.sourceFile || null; + this.isKeyword = this.options.ecmaVersion >= 6 ? isEcma6Keyword : isEcma5AndLessKeyword; + this.input = String(input); + + // Set up token state + + // The current position of the tokenizer in the input. + if (startPos) { + this.pos = startPos; + this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos)); + this.curLine = this.input.slice(0, this.lineStart).split(newline).length; + } else { + this.pos = this.lineStart = 0; + this.curLine = 1; + } + + // Properties of the current token: + // Its type + this.type = tt.eof; + // For tokens that include more information than their type, the value + this.value = null; + // Its start and end offset + this.start = this.end = this.pos; + // And, if locations are used, the {line, column} object + // corresponding to those offsets + this.startLoc = this.endLoc = null; + + // Position information for the previous token + this.lastTokEndLoc = this.lastTokStartLoc = null; + this.lastTokStart = this.lastTokEnd = this.pos; + + // The context stack is used to superficially track syntactic + // context to predict whether a regular expression is allowed in a + // given position. + this.context = [tc.b_stat]; + this.exprAllowed = true; + + // Flags to track whether we are in strict mode, a function, a + // generator. + this.strict = this.inFunction = this.inGenerator = false; + // Labels in scope. + this.labels = []; + + // If enabled, skip leading hashbang line. + if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === '#!') + this.skipLineComment(2); + }; + + // Shorthand because we are going to be adding a _lot_ of methods to + // this. + var pp = Parser.prototype; + + pp.extend = function(name, f) { + this[name] = f(this[name]); + }; + + pp.loadPlugins = function(plugins) { + for (var name in plugins) { + var plugin = exports.plugins[name]; + if (!plugin) throw new Error("Plugin '" + name + "' not found"); + plugin(this, plugins[name]); + } + }; + + // Move to the next token + + pp.next = function() { + if (this.options.onToken) + this.options.onToken(new Token(this)); + + this.lastTokEnd = this.end; + this.lastTokStart = this.start; + this.lastTokEndLoc = this.endLoc; + this.lastTokStartLoc = this.startLoc; + this.nextToken(); + }; + + pp.getToken = function() { + this.next(); + return new Token(this); + }; + + // If we're in an ES6 environment, make parsers iterable + if (typeof Symbol !== "undefined") + pp[Symbol.iterator] = function () { + var self = this; + return {next: function () { + var token = self.getToken(); + return { + done: token.type === tt.eof, + value: token + }; + }}; + }; + + // Toggle strict mode. Re-reads the next number or string to please + // pedantic tests (`"use strict"; 010;` should fail). + + pp.setStrict = function(strict) { + this.strict = strict; + if (this.type !== tt.num && this.type !== tt.string) return; + this.pos = this.start; + if (this.options.locations) { + while (this.pos < this.lineStart) { + this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1; + --this.curLine; + } + } + this.nextToken(); + }; + + pp.curContext = function() { + return this.context[this.context.length - 1]; + }; + + // Read a single token, updating the parser object's token-related + // properties. + + pp.nextToken = function() { + var curContext = this.curContext(); + if (!curContext || !curContext.preserveSpace) this.skipSpace(); + + this.start = this.pos; + if (this.options.locations) this.startLoc = this.curPosition(); + if (this.pos >= this.input.length) return this.finishToken(tt.eof); + + if (curContext === tc.q_tmpl) return this.readTmplToken(); + + this.readToken(this.fullCharCodeAtPos()); + }; + + pp.readToken = function(code) { + // Identifier or keyword. '\uXXXX' sequences are allowed in + // identifiers, so '\' also dispatches to that. + if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */) + return this.readWord(); + + return this.getTokenFromCode(code); + }; + + pp.fullCharCodeAtPos = function() { + var code = this.input.charCodeAt(this.pos); + if (code <= 0xd7ff || code >= 0xe000) return code; + var next = this.input.charCodeAt(this.pos + 1); + return (code << 10) + next - 0x35fdc00; + }; + + pp.skipBlockComment = function() { + var startLoc = this.options.onComment && this.options.locations && this.curPosition(); + var start = this.pos, end = this.input.indexOf("*/", this.pos += 2); + if (end === -1) this.raise(this.pos - 2, "Unterminated comment"); + this.pos = end + 2; + if (this.options.locations) { + lineBreak.lastIndex = start; + var match; + while ((match = lineBreak.exec(this.input)) && match.index < this.pos) { + ++this.curLine; + this.lineStart = match.index + match[0].length; + } + } + if (this.options.onComment) + this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos, + startLoc, this.options.locations && this.curPosition()); + }; + + pp.skipLineComment = function(startSkip) { + var start = this.pos; + var startLoc = this.options.onComment && this.options.locations && this.curPosition(); + var ch = this.input.charCodeAt(this.pos+=startSkip); + while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { + ++this.pos; + ch = this.input.charCodeAt(this.pos); + } + if (this.options.onComment) + this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, + startLoc, this.options.locations && this.curPosition()); + }; + + // Called at the start of the parse and after every token. Skips + // whitespace and comments, and. + + pp.skipSpace = function() { + while (this.pos < this.input.length) { + var ch = this.input.charCodeAt(this.pos); + if (ch === 32) { // ' ' + ++this.pos; + } else if (ch === 13) { + ++this.pos; + var next = this.input.charCodeAt(this.pos); + if (next === 10) { + ++this.pos; + } + if (this.options.locations) { + ++this.curLine; + this.lineStart = this.pos; + } + } else if (ch === 10 || ch === 8232 || ch === 8233) { + ++this.pos; + if (this.options.locations) { + ++this.curLine; + this.lineStart = this.pos; + } + } else if (ch > 8 && ch < 14) { + ++this.pos; + } else if (ch === 47) { // '/' + var next = this.input.charCodeAt(this.pos + 1); + if (next === 42) { // '*' + this.skipBlockComment(); + } else if (next === 47) { // '/' + this.skipLineComment(2); + } else break; + } else if (ch === 160) { // '\xa0' + ++this.pos; + } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { + ++this.pos; + } else { + break; + } + } + }; + + pp.curPosition = function() { + return new Position(this.curLine, this.pos - this.lineStart); + }; + + // The algorithm used to determine whether a regexp can appear at a + // given point in the program is loosely based on sweet.js' approach. + // See https://github.com/mozilla/sweet.js/wiki/design + + var TokContext = exports.TokContext = function(token, isExpr, preserveSpace) { + this.token = token; + this.isExpr = isExpr; + this.preserveSpace = preserveSpace; + }; + + var tc = exports.tokContexts = { + b_stat: new TokContext("{", false), + b_expr: new TokContext("{", true), + b_tmpl: new TokContext("${", true), + p_stat: new TokContext("(", false), + p_expr: new TokContext("(", true), + q_tmpl: new TokContext("`", true, true), + f_expr: new TokContext("function", true) + }; + + pp.braceIsBlock = function(prevType) { + var parent; + if (prevType === tt.colon && (parent = this.curContext()).token == "{") + return !parent.isExpr; + if (prevType === tt._return) + return newline.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() === tc.b_stat; + return !this.exprAllowed; + }; + + // Called at the end of every token. Sets `end`, `val`, and + // maintains `context` and `exprAllowed`, and skips the space after + // the token, so that the next one's `start` will point at the + // right position. + + pp.finishToken = function(type, val) { + this.end = this.pos; + if (this.options.locations) this.endLoc = this.curPosition(); + var prevType = this.type; + this.type = type; + this.value = val; + + this.updateContext(prevType); + }; + + pp.updateContext = function(prevType) { + var 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() { + var out = this.context.pop(); + if (out === tc.b_stat && this.curContext() === tc.f_expr) { + this.context.pop(); + this.exprAllowed = false; + } else if (out === tc.b_tmpl) { + this.exprAllowed = true; + } else { + this.exprAllowed = !(out && out.isExpr); + } + }; + + tt.braceL.updateContext = function(prevType) { + this.context.push(this.braceIsBlock(prevType) ? tc.b_stat : tc.b_expr); + this.exprAllowed = true; + }; + + tt.dollarBraceL.updateContext = function() { + this.context.push(tc.b_tmpl); + this.exprAllowed = true; + }; + + tt.parenL.updateContext = function(prevType) { + var statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while; + this.context.push(statementParens ? tc.p_stat : tc.p_expr); + this.exprAllowed = true; + }; + + tt.incDec.updateContext = function() { + // tokExprAllowed stays unchanged + }; + + tt._function.updateContext = function() { + if (this.curContext() !== tc.b_stat) + this.context.push(tc.f_expr); + this.exprAllowed = false; + }; + + tt.backQuote.updateContext = function() { + if (this.curContext() === tc.q_tmpl) + this.context.pop(); + else + this.context.push(tc.q_tmpl); + this.exprAllowed = false; + }; + + // ### Token reading + + // This is the function that is called to fetch the next token. It + // is somewhat obscure, because it works in character codes rather + // than characters, and because operator parsing has been inlined + // into it. + // + // All in the name of speed. + // + pp.readToken_dot = function() { + var next = this.input.charCodeAt(this.pos + 1); + if (next >= 48 && next <= 57) return this.readNumber(true); + var next2 = this.input.charCodeAt(this.pos + 2); + if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.' + this.pos += 3; + return this.finishToken(tt.ellipsis); + } else { + ++this.pos; + return this.finishToken(tt.dot); + } + }; + + pp.readToken_slash = function() { // '/' + var next = this.input.charCodeAt(this.pos + 1); + if (this.exprAllowed) {++this.pos; return this.readRegexp();} + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.slash, 1); + }; + + pp.readToken_mult_modulo = function(code) { // '%*' + var type = code === 42 ? tt.star : tt.modulo; + var width = 1; + var next = this.input.charCodeAt(this.pos + 1); + + if (this.options.transformers["es7.exponentiationOperator"] && next === 42) { // '*' + width++; + next = this.input.charCodeAt(this.pos + 2); + type = tt.exponent; + } + + if (next === 61) { + width++; + type = tt.assign; + } + + return this.finishOp(type, width); + }; + + pp.readToken_pipe_amp = function(code) { // '|&' + var next = this.input.charCodeAt(this.pos + 1); + if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2); + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1); + }; + + pp.readToken_caret = function() { // '^' + var next = this.input.charCodeAt(this.pos + 1); + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.bitwiseXOR, 1); + }; + + pp.readToken_plus_min = function(code) { // '+-' + var next = this.input.charCodeAt(this.pos + 1); + if (next === code) { + if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 && + newline.test(this.input.slice(this.lastTokEnd, this.pos))) { + // A `-->` line comment + this.skipLineComment(3); + this.skipSpace(); + return this.nextToken(); + } + return this.finishOp(tt.incDec, 2); + } + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.plusMin, 1); + }; + + pp.readToken_lt_gt = function(code) { // '<>' + var next = this.input.charCodeAt(this.pos + 1); + var size = 1; + if (next === code) { + size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; + if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); + return this.finishOp(tt.bitShift, size); + } + if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && + this.input.charCodeAt(this.pos + 3) == 45) { + // `10;\n --> nothing", { + type: "Program", + body: [ + { + type: "ExpressionStatement", + expression: { + type: "AssignmentExpression", + operator: "=", + left: { + type: "Identifier", + name: "x" + }, + right: { + type: "BinaryExpression", + left: { + type: "UpdateExpression", + operator: "--", + prefix: false, + argument: { + type: "Identifier", + name: "y" + } + }, + operator: ">", + right: { + type: "Literal", + value: 10 + } + } + } + } + ] +}); + +test("'use strict';\nobject.static();", { + type: "Program", + body: [ + { + type: "ExpressionStatement", + expression: { + type: "Literal", + value: "use strict", + raw: "'use strict'" + } + }, + { + type: "ExpressionStatement", + expression: { + type: "CallExpression", + callee: { + type: "MemberExpression", + object: { + type: "Identifier", + name: "object" + }, + property: { + type: "Identifier", + name: "static" + }, + computed: false + }, + arguments: [] + } + } + ] +}); + +// Failure tests + +testFail("{", + "Unexpected token (1:1)"); + +testFail("}", + "Unexpected token (1:0)"); + +testFail("3ea", + "Invalid number (1:0)"); + +testFail("3in []", + "Identifier directly after number (1:1)"); + +testFail("3e", + "Invalid number (1:0)"); + +testFail("3e+", + "Invalid number (1:0)"); + +testFail("3e-", + "Invalid number (1:0)"); + +testFail("3x", + "Identifier directly after number (1:1)"); + +testFail("3x0", + "Identifier directly after number (1:1)"); + +testFail("0x", + "Expected number in radix 16 (1:2)"); + +testFail("09", + "Invalid number (1:0)"); + +testFail("018", + "Invalid number (1:0)"); + +testFail("01a", + "Identifier directly after number (1:2)"); + +testFail("3in[]", + "Identifier directly after number (1:1)"); + +testFail("0x3in[]", + "Identifier directly after number (1:3)"); + +testFail("\"Hello\nWorld\"", + "Unterminated string constant (1:0)"); + +testFail("x\\", + "Expecting Unicode escape sequence \\uXXXX (1:2)"); + +testFail("x\\u005c", + "Invalid Unicode escape (1:1)"); + +testFail("x\\u002a", + "Invalid Unicode escape (1:1)"); + +testFail("/", + "Unterminated regular expression (1:1)"); + +testFail("/test", + "Unterminated regular expression (1:1)"); + +testFail("var x = /[a-z]/\\ux", + "Bad character escape sequence (1:8)"); + +testFail("3 = 4", + "Assigning to rvalue (1:0)"); + +testFail("func() = 4", + "Assigning to rvalue (1:0)"); + +testFail("(1 + 1) = 10", + "Assigning to rvalue (1:1)"); + +testFail("1++", + "Assigning to rvalue (1:0)"); + +testFail("1--", + "Assigning to rvalue (1:0)"); + +testFail("++1", + "Assigning to rvalue (1:2)"); + +testFail("--1", + "Assigning to rvalue (1:2)"); + +testFail("for((1 + 1) in list) process(x);", + "Assigning to rvalue (1:5)"); + +testFail("[", + "Unexpected token (1:1)"); + +testFail("[,", + "Unexpected token (1:2)"); + +testFail("1 + {", + "Unexpected token (1:5)"); + +testFail("1 + { t:t ", + "Unexpected token (1:10)"); + +testFail("1 + { t:t,", + "Unexpected token (1:10)"); + +testFail("var x = /\n/", + "Unterminated regular expression (1:9)"); + +testFail("var x = \"\n", + "Unterminated string constant (1:8)"); + +testFail("var if = 42", + "Unexpected token (1:4)"); + +testFail("i + 2 = 42", + "Assigning to rvalue (1:0)"); + +testFail("+i = 42", + "Assigning to rvalue (1:0)"); + +testFail("1 + (", + "Unexpected token (1:5)"); + +testFail("\n\n\n{", + "Unexpected token (4:1)"); + +testFail("\n/* Some multiline\ncomment */\n)", + "Unexpected token (4:0)"); + +testFail("{ set 1 }", + "Unexpected token (1:6)"); + +testFail("{ get 2 }", + "Unexpected token (1:6)"); + +testFail("({ set: s(if) { } })", + "Unexpected token (1:10)"); + +testFail("({ set s(.) { } })", + "Unexpected token (1:9)"); + +testFail("({ set: s() { } })", + "Unexpected token (1:12)"); + +testFail("({ set: s(a, b) { } })", + "Unexpected token (1:16)"); + +testFail("({ get: g(d) { } })", + "Unexpected token (1:13)"); + +testFail("({ get i() { }, i: 42 })", + "Redefinition of property (1:16)"); + +testFail("({ i: 42, get i() { } })", + "Redefinition of property (1:14)"); + +testFail("({ set i(x) { }, i: 42 })", + "Redefinition of property (1:17)"); + +testFail("({ i: 42, set i(x) { } })", + "Redefinition of property (1:14)"); + +testFail("({ get i() { }, get i() { } })", + "Redefinition of property (1:20)"); + +testFail("({ set i(x) { }, set i(x) { } })", + "Redefinition of property (1:21)"); + +testFail("function t(...) { }", + "Unexpected token (1:11)"); + +testFail("function t(...) { }", + "Unexpected token (1:14)", + { ecmaVersion: 6 }); + +testFail("function t(...rest, b) { }", + "Unexpected token (1:18)", + { ecmaVersion: 6 }); + +testFail("function t(if) { }", + "Unexpected token (1:11)"); + +testFail("function t(true) { }", + "Unexpected token (1:11)"); + +testFail("function t(false) { }", + "Unexpected token (1:11)"); + +testFail("function t(null) { }", + "Unexpected token (1:11)"); + +testFail("function null() { }", + "Unexpected token (1:9)"); + +testFail("function true() { }", + "Unexpected token (1:9)"); + +testFail("function false() { }", + "Unexpected token (1:9)"); + +testFail("function if() { }", + "Unexpected token (1:9)"); + +testFail("a b;", + "Unexpected token (1:2)"); + +testFail("if.a;", + "Unexpected token (1:2)"); + +testFail("a if;", + "Unexpected token (1:2)"); + +testFail("a class;", + "Unexpected token (1:2)"); + +testFail("break\n", + "Unsyntactic break (1:0)"); + +testFail("break 1;", + "Unexpected token (1:6)"); + +testFail("continue\n", + "Unsyntactic continue (1:0)"); + +testFail("continue 2;", + "Unexpected token (1:9)"); + +testFail("throw", + "Unexpected token (1:5)"); + +testFail("throw;", + "Unexpected token (1:5)"); + +testFail("for (var i, i2 in {});", + "Unexpected token (1:15)"); + +testFail("for ((i in {}));", + "Unexpected token (1:14)"); + +testFail("for (i + 1 in {});", + "Assigning to rvalue (1:5)"); + +testFail("for (+i in {});", + "Assigning to rvalue (1:5)"); + +testFail("if(false)", + "Unexpected token (1:9)"); + +testFail("if(false) doThis(); else", + "Unexpected token (1:24)"); + +testFail("do", + "Unexpected token (1:2)"); + +testFail("while(false)", + "Unexpected token (1:12)"); + +testFail("for(;;)", + "Unexpected token (1:7)"); + +testFail("with(x)", + "Unexpected token (1:7)"); + +testFail("try { }", + "Missing catch or finally clause (1:0)"); + +testFail("‿ = 10", + "Unexpected character '‿' (1:0)"); + +testFail("if(true) let a = 1;", + "Unexpected token (1:13)"); + +testFail("switch (c) { default: default: }", + "Multiple default clauses (1:22)"); + +testFail("new X().\"s\"", + "Unexpected token (1:8)"); + +testFail("/*", + "Unterminated comment (1:0)"); + +testFail("/*\n\n\n", + "Unterminated comment (1:0)"); + +testFail("/**", + "Unterminated comment (1:0)"); + +testFail("/*\n\n*", + "Unterminated comment (1:0)"); + +testFail("/*hello", + "Unterminated comment (1:0)"); + +testFail("/*hello *", + "Unterminated comment (1:0)"); + +testFail("\n]", + "Unexpected token (2:0)"); + +testFail("\r]", + "Unexpected token (2:0)"); + +testFail("\r\n]", + "Unexpected token (2:0)"); + +testFail("\n\r]", + "Unexpected token (3:0)"); + +testFail("//\r\n]", + "Unexpected token (2:0)"); + +testFail("//\n\r]", + "Unexpected token (3:0)"); + +testFail("/a\\\n/", + "Unterminated regular expression (1:1)"); + +testFail("//\r \n]", + "Unexpected token (3:0)"); + +testFail("/*\r\n*/]", + "Unexpected token (2:2)"); + +testFail("/*\n\r*/]", + "Unexpected token (3:2)"); + +testFail("/*\r \n*/]", + "Unexpected token (3:2)"); + +testFail("\\\\", + "Expecting Unicode escape sequence \\uXXXX (1:1)"); + +testFail("\\u005c", + "Invalid Unicode escape (1:0)"); + +testFail("\\x", + "Expecting Unicode escape sequence \\uXXXX (1:1)"); + +testFail("\\u0000", + "Invalid Unicode escape (1:0)"); + +testFail("‌ = []", + "Unexpected character '‌' (1:0)"); + +testFail("‍ = []", + "Unexpected character '‍' (1:0)"); + +testFail("\"\\", + "Unterminated string constant (1:0)"); + +testFail("\"\\u", + "Bad character escape sequence (1:0)"); + +testFail("return", + "'return' outside of function (1:0)"); + +testFail("break", + "Unsyntactic break (1:0)"); + +testFail("continue", + "Unsyntactic continue (1:0)"); + +testFail("switch (x) { default: continue; }", + "Unsyntactic continue (1:22)"); + +testFail("do { x } *", + "Unexpected token (1:9)"); + +testFail("while (true) { break x; }", + "Unsyntactic break (1:15)"); + +testFail("while (true) { continue x; }", + "Unsyntactic continue (1:15)"); + +testFail("x: while (true) { (function () { break x; }); }", + "Unsyntactic break (1:33)"); + +testFail("x: while (true) { (function () { continue x; }); }", + "Unsyntactic continue (1:33)"); + +testFail("x: while (true) { (function () { break; }); }", + "Unsyntactic break (1:33)"); + +testFail("x: while (true) { (function () { continue; }); }", + "Unsyntactic continue (1:33)"); + +testFail("x: while (true) { x: while (true) { } }", + "Label 'x' is already declared (1:18)"); + +testFail("(function () { 'use strict'; delete i; }())", + "Deleting local variable in strict mode (1:29)"); + +testFail("(function () { 'use strict'; with (i); }())", + "'with' in strict mode (1:29)"); + +testFail("function hello() {'use strict'; ({ i: 42, i: 42 }) }", + "Redefinition of property (1:42)"); + +testFail("function hello() {'use strict'; ({ hasOwnProperty: 42, hasOwnProperty: 42 }) }", + "Redefinition of property (1:55)"); + +testFail("function hello() {'use strict'; var eval = 10; }", + "Binding eval in strict mode (1:36)"); + +testFail("function hello() {'use strict'; var arguments = 10; }", + "Binding arguments in strict mode (1:36)"); + +testFail("function hello() {'use strict'; try { } catch (eval) { } }", + "Binding eval in strict mode (1:47)"); + +testFail("function hello() {'use strict'; try { } catch (arguments) { } }", + "Binding arguments in strict mode (1:47)"); + +testFail("function hello() {'use strict'; eval = 10; }", + "Assigning to eval in strict mode (1:32)"); + +testFail("function hello() {'use strict'; arguments = 10; }", + "Assigning to arguments in strict mode (1:32)"); + +testFail("function hello() {'use strict'; ++eval; }", + "Assigning to eval in strict mode (1:34)"); + +testFail("function hello() {'use strict'; --eval; }", + "Assigning to eval in strict mode (1:34)"); + +testFail("function hello() {'use strict'; ++arguments; }", + "Assigning to arguments in strict mode (1:34)"); + +testFail("function hello() {'use strict'; --arguments; }", + "Assigning to arguments in strict mode (1:34)"); + +testFail("function hello() {'use strict'; eval++; }", + "Assigning to eval in strict mode (1:32)"); + +testFail("function hello() {'use strict'; eval--; }", + "Assigning to eval in strict mode (1:32)"); + +testFail("function hello() {'use strict'; arguments++; }", + "Assigning to arguments in strict mode (1:32)"); + +testFail("function hello() {'use strict'; arguments--; }", + "Assigning to arguments in strict mode (1:32)"); + +testFail("function hello() {'use strict'; function eval() { } }", + "Defining 'eval' in strict mode (1:41)"); + +testFail("function hello() {'use strict'; function arguments() { } }", + "Defining 'arguments' in strict mode (1:41)"); + +testFail("function eval() {'use strict'; }", + "Defining 'eval' in strict mode (1:9)"); + +testFail("function arguments() {'use strict'; }", + "Defining 'arguments' in strict mode (1:9)"); + +testFail("function hello() {'use strict'; (function eval() { }()) }", + "Defining 'eval' in strict mode (1:42)"); + +testFail("function hello() {'use strict'; (function arguments() { }()) }", + "Defining 'arguments' in strict mode (1:42)"); + +testFail("(function eval() {'use strict'; })()", + "Defining 'eval' in strict mode (1:10)"); + +testFail("(function arguments() {'use strict'; })()", + "Defining 'arguments' in strict mode (1:10)"); + +testFail("function hello() {'use strict'; ({ s: function eval() { } }); }", + "Defining 'eval' in strict mode (1:47)"); + +testFail("(function package() {'use strict'; })()", + "Defining 'package' in strict mode (1:10)"); + +testFail("function hello() {'use strict'; ({ i: 10, set s(eval) { } }); }", + "Defining 'eval' in strict mode (1:48)"); + +testFail("function hello() {'use strict'; ({ set s(eval) { } }); }", + "Defining 'eval' in strict mode (1:41)"); + +testFail("function hello() {'use strict'; ({ s: function s(eval) { } }); }", + "Defining 'eval' in strict mode (1:49)"); + +testFail("function hello(eval) {'use strict';}", + "Defining 'eval' in strict mode (1:15)"); + +testFail("function hello(arguments) {'use strict';}", + "Defining 'arguments' in strict mode (1:15)"); + +testFail("function hello() { 'use strict'; function inner(eval) {} }", + "Defining 'eval' in strict mode (1:48)"); + +testFail("function hello() { 'use strict'; function inner(arguments) {} }", + "Defining 'arguments' in strict mode (1:48)"); + +testFail("function hello() { 'use strict'; \"\\1\"; }", + "Octal literal in strict mode (1:34)"); + +testFail("function hello() { 'use strict'; 021; }", + "Invalid number (1:33)"); + +testFail("function hello() { 'use strict'; ({ \"\\1\": 42 }); }", + "Octal literal in strict mode (1:37)"); + +testFail("function hello() { 'use strict'; ({ 021: 42 }); }", + "Invalid number (1:36)"); + +testFail("function hello() { \"use strict\"; function inner() { \"octal directive\\1\"; } }", + "Octal literal in strict mode (1:68)"); + +testFail("function hello() { \"use strict\"; var implements; }", + "The keyword 'implements' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var interface; }", + "The keyword 'interface' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var package; }", + "The keyword 'package' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var private; }", + "The keyword 'private' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var protected; }", + "The keyword 'protected' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var public; }", + "The keyword 'public' is reserved (1:37)"); + +testFail("function hello() { \"use strict\"; var static; }", + "The keyword 'static' is reserved (1:37)"); + +testFail("function hello(static) { \"use strict\"; }", + "Defining 'static' in strict mode (1:15)"); + +testFail("function static() { \"use strict\"; }", + "Defining 'static' in strict mode (1:9)"); + +testFail("\"use strict\"; function static() { }", + "The keyword 'static' is reserved (1:23)"); + +testFail("function a(t, t) { \"use strict\"; }", + "Argument name clash in strict mode (1:14)"); + +testFail("function a(eval) { \"use strict\"; }", + "Defining 'eval' in strict mode (1:11)"); + +testFail("function a(package) { \"use strict\"; }", + "Defining 'package' in strict mode (1:11)"); + +testFail("function a() { \"use strict\"; function b(t, t) { }; }", + "Argument name clash in strict mode (1:43)"); + +testFail("(function a(t, t) { \"use strict\"; })", + "Argument name clash in strict mode (1:15)"); + +testFail("function a() { \"use strict\"; (function b(t, t) { }); }", + "Argument name clash in strict mode (1:44)"); + +testFail("(function a(eval) { \"use strict\"; })", + "Defining 'eval' in strict mode (1:12)"); + +testFail("(function a(package) { \"use strict\"; })", + "Defining 'package' in strict mode (1:12)"); + +testFail("\"use strict\";function foo(){\"use strict\";}function bar(){var v = 015}", + "Invalid number (1:65)"); + +testFail("var this = 10;", "Unexpected token (1:4)"); + +testFail("throw\n10;", "Illegal newline after throw (1:5)"); + + +// ECMA < 6 mode should work as before + +testFail("const a;", "Unexpected token (1:6)"); + +testFail("let x;", "Unexpected token (1:4)"); + +testFail("const a = 1;", "Unexpected token (1:6)"); + +testFail("let a = 1;", "Unexpected token (1:4)"); + +testFail("for(const x = 0;;);", "Unexpected token (1:10)"); + +testFail("for(let x = 0;;);", "Unexpected token (1:8)"); + +test("let++", { + type: "Program", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + }, + body: [ + { + type: "ExpressionStatement", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + }, + expression: { + type: "UpdateExpression", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + }, + operator: "++", + prefix: false, + argument: { + type: "Identifier", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 3 + } + }, + name: "let" + } + } + } + ] +}); + +// ECMA 6 support + +test("let x", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + init: null, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("let x, y;", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + init: null, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "y", + loc: { + start: { + line: 1, + column: 7 + }, + end: { + line: 1, + column: 8 + } + } + }, + init: null, + loc: { + start: { + line: 1, + column: 7 + }, + end: { + line: 1, + column: 8 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 9 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 9 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("let x = 42", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 10 + } + } + }, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 10 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 10 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 10 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("let eval = 42, arguments = 42", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "eval", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 8 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 11 + }, + end: { + line: 1, + column: 13 + } + } + }, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 13 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "arguments", + loc: { + start: { + line: 1, + column: 15 + }, + end: { + line: 1, + column: 24 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 27 + }, + end: { + line: 1, + column: 29 + } + } + }, + loc: { + start: { + line: 1, + column: 15 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 29 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("let x = 14, y = 3, z = 1977", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 5 + } + } + }, + init: { + type: "Literal", + value: 14, + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 10 + } + } + }, + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 10 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "y", + loc: { + start: { + line: 1, + column: 12 + }, + end: { + line: 1, + column: 13 + } + } + }, + init: { + type: "Literal", + value: 3, + loc: { + start: { + line: 1, + column: 16 + }, + end: { + line: 1, + column: 17 + } + } + }, + loc: { + start: { + line: 1, + column: 12 + }, + end: { + line: 1, + column: 17 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "z", + loc: { + start: { + line: 1, + column: 19 + }, + end: { + line: 1, + column: 20 + } + } + }, + init: { + type: "Literal", + value: 1977, + loc: { + start: { + line: 1, + column: 23 + }, + end: { + line: 1, + column: 27 + } + } + }, + loc: { + start: { + line: 1, + column: 19 + }, + end: { + line: 1, + column: 27 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 27 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 27 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("for(let x = 0;;);", { + type: "Program", + body: [ + { + type: "ForStatement", + init: { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 9 + } + } + }, + init: { + type: "Literal", + value: 0, + loc: { + start: { + line: 1, + column: 12 + }, + end: { + line: 1, + column: 13 + } + } + }, + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 13 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 13 + } + } + }, + test: null, + update: null, + body: { + type: "EmptyStatement", + loc: { + start: { + line: 1, + column: 16 + }, + end: { + line: 1, + column: 17 + } + } + }, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 17 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 17 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("for(let x = 0, y = 1;;);", { + type: "Program", + body: [ + { + type: "ForStatement", + init: { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 9 + } + } + }, + init: { + type: "Literal", + value: 0, + loc: { + start: { + line: 1, + column: 12 + }, + end: { + line: 1, + column: 13 + } + } + }, + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 13 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "y", + loc: { + start: { + line: 1, + column: 15 + }, + end: { + line: 1, + column: 16 + } + } + }, + init: { + type: "Literal", + value: 1, + loc: { + start: { + line: 1, + column: 19 + }, + end: { + line: 1, + column: 20 + } + } + }, + loc: { + start: { + line: 1, + column: 15 + }, + end: { + line: 1, + column: 20 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 4 + }, + end: { + line: 1, + column: 20 + } + } + }, + test: null, + update: null, + body: { + type: "EmptyStatement", + loc: { + start: { + line: 1, + column: 23 + }, + end: { + line: 1, + column: 24 + } + } + }, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 24 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 24 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("for (let x in list) process(x);", { + type: "Program", + body: [ + { + type: "ForInStatement", + left: { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 9 + }, + end: { + line: 1, + column: 10 + } + } + }, + init: null, + loc: { + start: { + line: 1, + column: 9 + }, + end: { + line: 1, + column: 10 + } + } + } + ], + kind: "let", + loc: { + start: { + line: 1, + column: 5 + }, + end: { + line: 1, + column: 10 + } + } + }, + right: { + type: "Identifier", + name: "list", + loc: { + start: { + line: 1, + column: 14 + }, + end: { + line: 1, + column: 18 + } + } + }, + body: { + type: "ExpressionStatement", + expression: { + type: "CallExpression", + callee: { + type: "Identifier", + name: "process", + loc: { + start: { + line: 1, + column: 20 + }, + end: { + line: 1, + column: 27 + } + } + }, + arguments: [ + { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 28 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + loc: { + start: { + line: 1, + column: 20 + }, + end: { + line: 1, + column: 30 + } + } + }, + loc: { + start: { + line: 1, + column: 20 + }, + end: { + line: 1, + column: 31 + } + } + }, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 31 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 31 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("const x = 42", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 7 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 10 + }, + end: { + line: 1, + column: 12 + } + } + }, + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 12 + } + } + } + ], + kind: "const", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 12 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 12 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("const eval = 42, arguments = 42", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "eval", + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 10 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 13 + }, + end: { + line: 1, + column: 15 + } + } + }, + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 15 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "arguments", + loc: { + start: { + line: 1, + column: 17 + }, + end: { + line: 1, + column: 26 + } + } + }, + init: { + type: "Literal", + value: 42, + loc: { + start: { + line: 1, + column: 29 + }, + end: { + line: 1, + column: 31 + } + } + }, + loc: { + start: { + line: 1, + column: 17 + }, + end: { + line: 1, + column: 31 + } + } + } + ], + kind: "const", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 31 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 31 + } + } +}, {ecmaVersion: 6, locations: true}); + +test("const x = 14, y = 3, z = 1977", { + type: "Program", + body: [ + { + type: "VariableDeclaration", + declarations: [ + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 7 + } + } + }, + init: { + type: "Literal", + value: 14, + loc: { + start: { + line: 1, + column: 10 + }, + end: { + line: 1, + column: 12 + } + } + }, + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 12 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "y", + loc: { + start: { + line: 1, + column: 14 + }, + end: { + line: 1, + column: 15 + } + } + }, + init: { + type: "Literal", + value: 3, + loc: { + start: { + line: 1, + column: 18 + }, + end: { + line: 1, + column: 19 + } + } + }, + loc: { + start: { + line: 1, + column: 14 + }, + end: { + line: 1, + column: 19 + } + } + }, + { + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "z", + loc: { + start: { + line: 1, + column: 21 + }, + end: { + line: 1, + column: 22 + } + } + }, + init: { + type: "Literal", + value: 1977, + loc: { + start: { + line: 1, + column: 25 + }, + end: { + line: 1, + column: 29 + } + } + }, + loc: { + start: { + line: 1, + column: 21 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + kind: "const", + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 29 + } + } + } + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 29 + } + } +}, {ecmaVersion: 6, locations: true}); + +testFail("const a;", "Unexpected token (1:7)", {ecmaVersion: 6}); + +test("for(const x = 0;;);", { + type: "Program", + body: [{ + type: "ForStatement", + init: { + type: "VariableDeclaration", + declarations: [{ + type: "VariableDeclarator", + id: { + type: "Identifier", + name: "x", + range: [10, 11] + }, + init: { + type: "Literal", + value: 0, + range: [14, 15] + }, + range: [10, 15] + }], + kind: "const", + range: [4, 15] + }, + test: null, + update: null, + body: { + type: "EmptyStatement", + range: [18, 19] + }, + range: [0, 19] + }], + range: [0, 19] +}, {ecmaVersion: 6, ranges: true}); + +testFail("for(x of a);", "Unexpected token (1:6)"); + +testFail("for(var x of a);", "Unexpected token (1:10)"); + +// Assertion Tests +test(function TestComments() { + // Bear class + function Bear(x,y,z) { + this.position = [x||0,y||0,z||0] + } + + Bear.prototype.roar = function(message) { + return 'RAWWW: ' + message; // Whatever + }; + + function Cat() { + /* 1 + 2 + 3*/ + } + + Cat.prototype.roar = function(message) { + return 'MEOOWW: ' + /*stuff*/ message; + }; +}.toString().replace(/\r\n/g, '\n'), {}, { + onComment: [ + {type: "Line", value: " Bear class"}, + {type: "Line", value: " Whatever"}, + {type: "Block", value: [ + " 1", + " 2", + " 3" + ].join('\n')}, + {type: "Block", value: "stuff"} + ] +}); + +test(" HTML comment", {}, { + locations: true, + onComment: [{ + type: "Line", + value: " HTML comment", + loc: { + start: { line: 2, column: 0 }, + end: { line: 2, column: 16 } + } + }] +}); + +var tokTypes = acorn.tokTypes; + +test('var x = (1 + 2)', {}, { + locations: true, + loose: false, + onToken: [ + { + type: tokTypes._var, + value: "var", + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 3} + } + }, + { + type: tokTypes.name, + value: "x", + loc: { + start: {line: 1, column: 4}, + end: {line: 1, column: 5} + } + }, + { + type: tokTypes.eq, + value: "=", + loc: { + start: {line: 1, column: 6}, + end: {line: 1, column: 7} + } + }, + { + type: tokTypes.parenL, + value: undefined, + loc: { + start: {line: 1, column: 8}, + end: {line: 1, column: 9} + } + }, + { + type: tokTypes.num, + value: 1, + loc: { + start: {line: 1, column: 9}, + end: {line: 1, column: 10} + } + }, + { + type: {binop: 9, prefix: true, beforeExpr: true}, + value: "+", + loc: { + start: {line: 1, column: 11}, + end: {line: 1, column: 12} + } + }, + { + type: tokTypes.num, + value: 2, + loc: { + start: {line: 1, column: 13}, + end: {line: 1, column: 14} + } + }, + { + type: tokTypes.parenR, + value: undefined, + loc: { + start: {line: 1, column: 14}, + end: {line: 1, column: 15} + } + }, + { + type: tokTypes.eof, + value: undefined, + loc: { + start: {line: 1, column: 15}, + end: {line: 1, column: 15} + } + } + ] +}); + +test("function f(f) { 'use strict'; }", {}); + +// https://github.com/marijnh/acorn/issues/180 +test("#!/usr/bin/node\n;", {}, { + allowHashBang: true, + onComment: [{ + type: "Line", + value: "/usr/bin/node", + start: 0, + end: 15 + }] +}); + +// https://github.com/marijnh/acorn/issues/204 +test("(function () {} / 1)", { + type: "Program", + body: [{ + type: "ExpressionStatement", + expression: { + type: "BinaryExpression", + left: { + type: "FunctionExpression", + id: null, + params: [], + body: { + type: "BlockStatement", + body: [] + } + }, + operator: "/", + right: {type: "Literal", value: 1} + } + }] +}); + +test("function f() {} / 1 /", { + type: "Program", + body: [ + { + type: "FunctionDeclaration", + id: {type: "Identifier", name: "f"}, + params: [], + body: { + type: "BlockStatement", + body: [] + } + }, + { + type: "ExpressionStatement", + expression: { + type: "Literal", + regex: {pattern: " 1 ", flags: ""}, + value: / 1 / + } + } + ] +}); + +var semicolons = [] +testAssert("var x\nreturn\n10", function() { + var result = semicolons.join(" "); + semicolons.length = 0; + if (result != "5 12 15") + return "Unexpected result for onInsertedSemicolon: " + result; +}, {onInsertedSemicolon: function(pos) { semicolons.push(pos); }, + allowReturnOutsideFunction: true, + loose: false}) + +var trailingCommas = [] +testAssert("[1,2,] + {foo: 1,}", function() { + var result = trailingCommas.join(" "); + trailingCommas.length = 0; + if (result != "4 16") + return "Unexpected result for onTrailingComma: " + result; +}, {onTrailingComma: function(pos) { trailingCommas.push(pos); }, + loose: false}) diff --git a/lib/acorn/tools/generate-identifier-regex.js b/lib/acorn/tools/generate-identifier-regex.js new file mode 100644 index 0000000000..0d7c50fc38 --- /dev/null +++ b/lib/acorn/tools/generate-identifier-regex.js @@ -0,0 +1,47 @@ +// Note: run `npm install unicode-7.0.0` first. + +// Which Unicode version should be used? +var version = '7.0.0'; + +var start = require('unicode-' + version + '/properties/ID_Start/code-points') + .filter(function(ch) { return ch > 127; }); +var cont = [0x200c, 0x200d].concat(require('unicode-' + version + '/properties/ID_Continue/code-points') + .filter(function(ch) { return ch > 127 && start.indexOf(ch) == -1; })); + +function pad(str, width) { + while (str.length < width) str = "0" + str; + return str; +} + +function esc(code) { + var hex = code.toString(16); + if (hex.length <= 2) return "\\x" + pad(hex, 2); + else return "\\u" + pad(hex, 4); +} + +function generate(chars) { + var astral = [], re = ""; + for (var i = 0, at = 0x10000; i < chars.length; i++) { + var from = chars[i], to = from; + while (i < chars.length - 1 && chars[i + 1] == to + 1) { + i++; + to++; + } + if (to <= 0xffff) { + if (from == to) re += esc(from); + else if (from + 1 == to) re += esc(from) + esc(to); + else re += esc(from) + "-" + esc(to); + } else { + astral.push(from - at, to - from); + at = to; + } + } + return {nonASCII: re, astral: astral}; +} + +var startData = generate(start), contData = generate(cont); + +console.log(" var nonASCIIidentifierStartChars = \"" + startData.nonASCII + "\";"); +console.log(" var nonASCIIidentifierChars = \"" + contData.nonASCII + "\";"); +console.log(" var astralIdentifierStartCodes = " + JSON.stringify(startData.astral) + ";"); +console.log(" var astralIdentifierCodes = " + JSON.stringify(contData.astral) + ";"); diff --git a/package.json b/package.json index a21f8e800b..592e852363 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "test": "make test" }, "dependencies": { - "acorn-babel": "0.11.1-38", "ast-types": "~0.7.0", "chalk": "^1.0.0", "chokidar": "^0.12.6", diff --git a/src/babel/api/node.js b/src/babel/api/node.js index fa2a7a7818..382fd38f2c 100644 --- a/src/babel/api/node.js +++ b/src/babel/api/node.js @@ -6,7 +6,7 @@ import fs from "fs"; export { util }; export { canCompile } from "../util"; -export { default as acorn } from "acorn-babel"; +export { default as acorn } from "../../acorn"; export { default as Transformer } from "../transformation/transformer"; export { default as transform } from "../transformation"; export { default as traverse } from "../traversal"; diff --git a/src/babel/generation/generators/modules.js b/src/babel/generation/generators/modules.js index 2218e90486..551a3ea7ba 100644 --- a/src/babel/generation/generators/modules.js +++ b/src/babel/generation/generators/modules.js @@ -10,26 +10,32 @@ export function ImportSpecifier(node, print) { } export function ExportSpecifier(node, print) { - print(node.id); - if (node.name) { + print(node.local); + if (node.exported && node.local !== node.exported) { this.push(" as "); - print(node.name); + print(node.exported); } } -export function ExportBatchSpecifier() { - this.push("*"); +export function ExportAllDeclaration(node, source) { + this.push("export * from"); + print(node.source); + this.semicolon(); } -export function ExportDeclaration(node, print) { +export function ExportNamedDeclaration(node, print) { this.push("export "); + ExportDeclaration.call(this, node, print); +} +export function ExportDefaultDeclaration(node, print) { + this.push("export default "); + ExportDeclaration.call(this, node, print); +} + +function ExportDeclaration(node, print) { var specifiers = node.specifiers; - if (node.default) { - this.push("default "); - } - if (node.declaration) { print(node.declaration); if (t.isStatement(node.declaration)) return; diff --git a/src/babel/helpers/parse.js b/src/babel/helpers/parse.js index d538630011..0927a6b035 100644 --- a/src/babel/helpers/parse.js +++ b/src/babel/helpers/parse.js @@ -1,7 +1,7 @@ import normalizeAst from "./normalize-ast"; import estraverse from "estraverse"; import codeFrame from "./code-frame"; -import acorn from "acorn-babel"; +import acorn from "../../acorn"; export default function (opts, code, callback) { try { @@ -11,11 +11,12 @@ export default function (opts, code, callback) { var ast = acorn.parse(code, { allowImportExportEverywhere: opts.looseModules, allowReturnOutsideFunction: opts.looseModules, - transformers: opts.transformers, + transformers: opts.transformers || {}, ecmaVersion: 6, strictMode: opts.strictMode, onComment: comments, locations: true, + plugins: opts.plugins || [], onToken: tokens, ranges: true }); diff --git a/src/babel/parser/flow.js b/src/babel/parser/flow.js new file mode 100644 index 0000000000..052a734158 --- /dev/null +++ b/src/babel/parser/flow.js @@ -0,0 +1,728 @@ +import acorn from "../../acorn"; + +var pp = acorn.Parser.prototype; +var tt = acorn.tokTypes; + +pp.isRelational = function (op) { + return this.type === tt.relational && this.value === op; +}; + +pp.expectRelational = function (op) { + if (this.isRelational(op)) { + this.next(); + } else { + this.unexpected(); + } +}; + +pp.flow_parseDeclareClass = function (node) { + this.next(); + this.flow_parseInterfaceish(node, true); + return this.finishNode(node, "DeclareClass"); +}; + +pp.flow_parseDeclareFunction = function (node) { + this.next(); + + var id = node.id = this.parseIdent(); + + var typeNode = this.startNode(); + var typeContainer = this.startNode(); + + if (this.isRelational("<")) { + typeNode.typeParameters = this.flow_parseTypeParameterDeclaration(); + } else { + typeNode.typeParameters = null; + } + + this.expect(tt.parenL); + var tmp = this.flow_parseFunctionTypeParams(); + typeNode.params = tmp.params; + typeNode.rest = tmp.rest; + this.expect(tt.parenR); + + this.expect(tt.colon); + typeNode.returnType = this.flow_parseType(); + + typeContainer.typeAnnotation = this.finishNode(typeNode, "FunctionTypeAnnotation"); + id.typeAnnotation = this.finishNode(typeContainer, "TypeAnnotation"); + + this.finishNode(id, id.type); + + this.semicolon(); + + return this.finishNode(node, "DeclareFunction"); +}; + +pp.flow_parseDeclare = function (node) { + if (this.type === tt._class) { + return this.flow_parseDeclareClass(node); + } else if (this.type === tt._function) { + return this.flow_parseDeclareFunction(node); + } else if (this.type === tt._var) { + return this.flow_parseDeclareVariable(node); + } else if (this.isContextual("module")) { + return this.flow_parseDeclareModule(node); + } else { + this.unexpected(); + } +}; + +pp.flow_parseDeclareVariable = function (node) { + this.next(); + node.id = this.flow_parseTypeAnnotatableIdentifier(); + this.semicolon(); + return this.finishNode(node, "DeclareVariable"); +}; + +pp.flow_parseDeclareModule = function (node) { + this.next(); + + if (this.type === tt.string) { + node.id = this.parseExprAtom(); + } else { + node.id = this.parseIdent(); + } + + var bodyNode = node.body = this.startNode(); + var body = bodyNode.body = []; + this.expect(tt.braceL); + while (this.type !== tt.braceR) { + var node2 = this.startNode(); + + // todo: declare check + this.next(); + + body.push(this.flow_parseDeclare(node2)); + } + this.expect(tt.braceR); + + this.finishNode(bodyNode, "BlockStatement"); + return this.finishNode(node, "DeclareModule"); +}; + + +// Interfaces + +pp.flow_parseInterfaceish = function (node, allowStatic) { + node.id = this.parseIdent(); + + if (this.isRelational("<")) { + node.typeParameters = this.flow_parseTypeParameterDeclaration(); + } else { + node.typeParameters = null; + } + + node.extends = []; + + if (this.eat(tt._extends)) { + do { + node.extends.push(this.flow_parseInterfaceExtends()); + } while(this.eat(tt.comma)); + } + + node.body = this.flow_parseObjectType(allowStatic); +}; + +pp.flow_parseInterfaceExtends = function () { + var node = this.startNode(); + + node.id = this.parseIdent(); + if (this.isRelational("<")) { + node.typeParameters = this.flow_parseTypeParameterInstantiation(); + } else { + node.typeParameters = null; + } + + return this.finishNode(node, "InterfaceExtends"); +}; + +pp.flow_parseInterface = function (node) { + this.flow_parseInterfaceish(node, false); + return this.finishNode(node, "InterfaceDeclaration"); +}; + +// Type aliases + +pp.flow_parseTypeAlias = function (node) { + node.id = this.parseIdent(); + + if (this.isRelational("<")) { + node.typeParameters = this.flow_parseTypeParameterDeclaration(); + } else { + node.typeParameters = null; + } + + this.expect(tt.eq); + + node.right = this.flow_parseType(); + + this.semicolon(); + + return this.finishNode(node, "TypeAlias"); +}; + +// Type annotations + +pp.flow_parseTypeParameterDeclaration = function () { + var node = this.startNode(); + node.params = []; + + this.expectRelational("<"); + while (!this.isRelational(">")) { + node.params.push(this.flow_parseTypeAnnotatableIdentifier()); + if (!this.isRelational(">")) { + this.expect(tt.comma); + } + } + this.expectRelational(">"); + + return this.finishNode(node, "TypeParameterDeclaration"); +}; + +pp.flow_parseTypeParameterInstantiation = function () { + var node = this.startNode(), oldInType = this.inType; + node.params = []; + + this.inType = true; + + this.expectRelational("<"); + while (!this.isRelational(">")) { + node.params.push(this.flow_parseType()); + if (!this.isRelational(">")) { + this.expect(tt.comma); + } + } + this.expectRelational(">"); + + this.inType = oldInType; + + return this.finishNode(node, "TypeParameterInstantiation"); +}; + +pp.flow_parseObjectPropertyKey = function () { + return (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true); +}; + +pp.flow_parseObjectTypeIndexer = function (node, isStatic) { + node.static = isStatic; + + this.expect(tt.bracketL); + node.id = this.flow_parseObjectPropertyKey(); + this.expect(tt.colon); + node.key = this.flow_parseType(); + this.expect(tt.bracketR); + this.expect(tt.colon); + node.value = this.flow_parseType(); + + return this.finishNode(node, "ObjectTypeIndexer"); +}; + +pp.flow_parseObjectTypeMethodish = function (node) { + node.params = []; + node.rest = null; + node.typeParameters = null; + + if (this.isRelational("<")) { + node.typeParameters = this.flow_parseTypeParameterDeclaration(); + } + + this.expect(tt.parenL); + while (this.type === tt.name) { + node.params.push(this.flow_parseFunctionTypeParam()); + if (this.type !== tt.parenR) { + this.expect(tt.comma); + } + } + + if (this.eat(tt.ellipsis)) { + node.rest = this.flow_parseFunctionTypeParam(); + } + this.expect(tt.parenR); + this.expect(tt.colon); + node.returnType = this.flow_parseType(); + + return this.finishNode(node, "FunctionTypeAnnotation"); +}; + +pp.flow_parseObjectTypeMethod = function (start, isStatic, key) { + var node = this.startNodeAt(start); + node.value = this.flow_parseObjectTypeMethodish(this.startNodeAt(start)); + node.static = isStatic; + node.key = key; + node.optional = false; + return this.finishNode(node, "ObjectTypeProperty"); +}; + +pp.flow_parseObjectTypeCallProperty = function (node, isStatic) { + var valueNode = this.startNode(); + node.static = isStatic; + node.value = this.flow_parseObjectTypeMethodish(valueNode); + return this.finishNode(node, "ObjectTypeCallProperty"); +}; + +pp.flow_parseObjectType = function (allowStatic) { + var nodeStart = this.startNode(); + var node; + var optional = false; + var property; + var propertyKey; + var propertyTypeAnnotation; + var token; + var isStatic; + + nodeStart.callProperties = []; + nodeStart.properties = []; + nodeStart.indexers = []; + + this.expect(tt.braceL); + + while (this.type !== tt.braceR) { + var start = this.currentPos(); + node = this.startNode(); + if (allowStatic && this.isContextual("static")) { + this.next(); + isStatic = true; + } + + if (this.type === tt.bracketL) { + nodeStart.indexers.push(this.flow_parseObjectTypeIndexer(node, isStatic)); + } else if (this.type === tt.parenL || this.isRelational("<")) { + nodeStart.callProperties.push(this.flow_parseObjectTypeCallProperty(node, allowStatic)); + } else { + if (isStatic && this.type === tt.colon) { + propertyKey = this.parseIdent(); + } else { + propertyKey = this.flow_parseObjectPropertyKey(); + } + if (this.isRelational("<") || this.type === tt.parenL) { + // This is a method property + nodeStart.properties.push(this.flow_parseObjectTypeMethod(start, isStatic, propertyKey)); + } else { + if (this.eat(tt.question)) { + optional = true; + } + this.expect(tt.colon); + node.key = propertyKey; + node.value = this.flow_parseType(); + node.optional = optional; + node.static = isStatic; + nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty")); + } + } + + if (!this.eat(tt.semi) && this.type !== tt.braceR) { + this.unexpected(); + } + } + + this.expect(tt.braceR); + + return this.finishNode(nodeStart, "ObjectTypeAnnotation") +}; + +pp.flow_parseGenericType = function (start, id) { + var node = this.startNodeAt(start); + + node.typeParameters = null; + node.id = id; + + while (this.eat(tt.dot)) { + var node2 = this.startNodeAt(start); + node2.qualification = node.id; + node2.id = this.parseIdent(); + node.id = this.finishNode(node2, "QualifiedTypeIdentifier"); + } + + if (this.isRelational("<")) { + node.typeParameters = this.flow_parseTypeParameterInstantiation(); + } + + return this.finishNode(node, "GenericTypeAnnotation"); +}; + +pp.flow_parseVoidType = function () { + var node = this.startNode(); + this.expect(tt._void); + return this.finishNode(node, "VoidTypeAnnotation"); +}; + +pp.flow_parseTypeofType = function () { + var node = this.startNode(); + this.expect(tt._typeof); + node.argument = this.flow_parsePrimaryType(); + return this.finishNode(node, "TypeofTypeAnnotation"); +}; + +pp.flow_parseTupleType = function () { + var node = this.startNode(); + node.types = []; + this.expect(tt.bracketL); + // We allow trailing commas + while (this.pos < this.input.length && this.type !== tt.bracketR) { + node.types.push(this.flow_parseType()); + if (this.type === tt.bracketR) break; + this.expect(tt.comma); + } + this.expect(tt.bracketR); + return this.finishNode(node, "TupleTypeAnnotation"); +}; + +pp.flow_parseFunctionTypeParam = function () { + var optional = false; + var node = this.startNode(); + node.name = this.parseIdent(); + if (this.eat(tt.question)) { + optional = true; + } + this.expect(tt.colon); + node.optional = optional; + node.typeAnnotation = this.flow_parseType(); + return this.finishNode(node, "FunctionTypeParam"); +}; + +pp.flow_parseFunctionTypeParams = function () { + var ret = { params: [], rest: null }; + while (this.type === tt.name) { + ret.params.push(this.flow_parseFunctionTypeParam()); + if (this.type !== tt.parenR) { + this.expect(tt.comma); + } + } + if (this.eat(tt.ellipsis)) { + ret.rest = this.flow_parseFunctionTypeParam(); + } + return ret; +}; + +pp.flow_identToTypeAnnotation = function (start, node, id) { + switch (id.name) { + case "any": + return this.finishNode(node, "AnyTypeAnnotation"); + + case "bool": + case "boolean": + return this.finishNode(node, "BooleanTypeAnnotation"); + + case "number": + return this.finishNode(node, "NumberTypeAnnotation"); + + case "string": + return this.finishNode(node, "StringTypeAnnotation"); + + default: + return this.flow_parseGenericType(start, id); + } +}; + +// The parsing of types roughly parallels the parsing of expressions, and +// primary types are kind of like primary expressions...they're the +// primitives with which other types are constructed. +pp.flow_parsePrimaryType = function () { + var typeIdentifier = null; + var params = null; + var returnType = null; + var start = this.currentPos(); + 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(); + + var tmpId; + + // Check to see if this is actually a grouped type + if (this.type !== tt.parenR && this.type !== tt.ellipsis) { + if (this.type === tt.name) { + //raise(tokStart, "Grouped types are currently the only flow feature not supported, request it?"); + //tmpId = identToTypeAnnotation(start, node, parseIdent()); + //next(); + //isGroupedType = this.type !== tt.question && this.type !== tt.colon; + } else { + isGroupedType = true; + } + } + + if (isGroupedType) { + if (tmpId && tt.parenR) { + type = tmpId; + } else { + type = this.flow_parseType(); + this.expect(tt.parenR); + } + + // If we see a => next then someone was probably confused about + // function types, so we can provide a better error message + if (this.eat(tt.arrow)) { + this.raise(node, + "Unexpected token =>. It looks like " + + "you are trying to write a function type, but you ended up " + + "writing a grouped type followed by an =>, which is a syntax " + + "error. Remember, function type parameters are named so function " + + "types look like (name1: type1, name2: type2) => returnType. You " + + "probably wrote (type1) => returnType" + ); + } + + return type; + } + + tmp = this.flow_parseFunctionTypeParams(); + node.params = tmp.params; + node.rest = tmp.rest; + + this.expect(tt.parenR); + + this.expect(tt.arrow); + + node.returnType = this.flow_parseType(); + node.typeParameters = null; + + return this.finishNode(node, "FunctionTypeAnnotation"); + + case tt.string: + node.value = this.value; + node.raw = this.input.slice(this.start, this.end); + this.next(); + return this.finishNode(node, "StringLiteralTypeAnnotation"); + + default: + if (this.type.keyword) { + switch (this.type.keyword) { + case "void": + return this.flow_parseVoidType(); + + case "typeof": + return this.flow_parseTypeofType(); + } + } + } + + this.unexpected(); +}; + +pp.flow_parsePostfixType = function () { + var node = this.startNode(); + var type = node.elementType = this.flow_parsePrimaryType(); + if (this.type === tt.bracketL) { + this.expect(tt.bracketL); + this.expect(tt.bracketR); + return this.finishNode(node, "ArrayTypeAnnotation"); + } + return type; +}; + +pp.flow_parsePrefixType = function () { + var node = this.startNode(); + if (this.eat(tt.question)) { + node.typeAnnotation = this.flow_parsePrefixType(); + return this.finishNode(node, "NullableTypeAnnotation"); + } + return this.flow_parsePostfixType(); +}; + +pp.flow_parseIntersectionType = function () { + var node = this.startNode(); + var type = this.flow_parsePrefixType(); + node.types = [type]; + while (this.eat(tt.bitwiseAND)) { + node.types.push(this.flow_parsePrefixType()); + } + return node.types.length === 1 ? type : this.finishNode(node, "IntersectionTypeAnnotation"); +}; + +pp.flow_parseUnionType = function () { + var node = this.startNode(); + var type = this.flow_parseIntersectionType(); + node.types = [type]; + while (this.eat(tt.bitwiseOR)) { + node.types.push(this.flow_parseIntersectionType()); + } + return node.types.length === 1 ? type : this.finishNode(node, "UnionTypeAnnotation"); +}; + +pp.flow_parseType = function () { + var oldInType = this.inType; + this.inType = true; + var type = this.flow_parseUnionType(); + this.inType = oldInType; + return type; +}; + +pp.flow_parseTypeAnnotation = function () { + var node = this.startNode(); + + var oldInType = this.inType; + this.inType = true; + this.expect(tt.colon); + node.typeAnnotation = this.flow_parseType(); + this.inType = oldInType; + + return this.finishNode(node, "TypeAnnotation"); +}; + +pp.flow_parseTypeAnnotatableIdentifier = function (requireTypeAnnotation, canBeOptionalParam) { + var node = this.startNode(); + var ident = this.parseIdent(); + var isOptionalParam = false; + + if (canBeOptionalParam && this.eat(tt.question)) { + this.expect(tt.question); + isOptionalParam = true; + } + + if (requireTypeAnnotation || this.type === tt.colon) { + ident.typeAnnotation = this.flow_parseTypeAnnotation(); + this.finishNode(ident, ident.type); + } + + if (isOptionalParam) { + ident.optional = true; + this.finishNode(ident, ident.type); + } + + return ident; +}; + +acorn.plugins.flow = function (instance) { + // function name(): string {} + instance.extend("parseFunctionBody", function (inner) { + return function (node, allowExpression) { + if (this.type === tt.colon) { + node.returnType = this.flow_parseTypeAnnotation(); + } + + return inner.call(this, node, allowExpression); + }; + }); + + instance.extend("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("parseParenItem", function (inner) { + return function (node, start) { + if (this.type === tt.colon) { + var typeCastNode = this.startNodeAt(start); + typeCastNode.expression = node; + typeCastNode.typeAnnotation = this.flow_parseTypeAnnotation(); + return this.finishNode(typeCastNode, "TypeCastExpression"); + } else { + return node; + } + }; + }); + + instance.extend("parseClassId", function (inner) { + return function (node, isStatement) { + inner.call(this, node, isStatement); + if (this.isRelational("<")) { + node.typeParameters = this.flow_parseTypeParameterDeclaration(); + } + }; + }); + + instance.extend("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("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; + }; + }); + + // function foo() {} + instance.extend("parseFunctionParams", function (inner) { + return function (node) { + if (this.isRelational("<")) { + node.typeParameters = this.flow_parseTypeParameterDeclaration(); + } + inner.call(this, node); + }; + }); + + // var foo: string = bar; + instance.extend("parseVarHead", function (inner) { + return function (decl) { + inner.call(this, decl); + if (this.type === tt.colon) { + decl.id.typeAnnotation = this.flow_parseTypeAnnotation(); + this.finishNode(decl.id, decl.id.type); + } + }; + }); +} diff --git a/src/babel/parser/jsx.js b/src/babel/parser/jsx.js new file mode 100644 index 0000000000..37d96b33b3 --- /dev/null +++ b/src/babel/parser/jsx.js @@ -0,0 +1,658 @@ +import acorn from "../../acorn"; + +var tt = acorn.tokTypes; +var tc = acorn.tokContexts; + +tc.j_oTag = new acorn.TokContext("...", true, true); + +tt.jsxName = new acorn.TokenType("jsxName"); +tt.jsxText = new acorn.TokenType("jsxText", {beforeExpr: true}); +tt.jsxTagStart = new acorn.TokenType("jsxTagStart"); +tt.jsxTagEnd = new acorn.TokenType("jsxTagEnd"); + +tt.jsxTagStart.updateContext = function() { + this.context.push(tc.j_expr); // treat as beginning of JSX expression + this.context.push(tc.j_oTag); // start opening tag context + this.exprAllowed = false; +}; +tt.jsxTagEnd.updateContext = function(prevType) { + var out = this.context.pop(); + if (out === tc.j_oTag && prevType === tt.slash || out === tc.j_cTag) { + this.context.pop(); + this.exprAllowed = this.curContext() === tc.j_expr; + } else { + this.exprAllowed = true; + } +}; + +var pp = acorn.Parser.prototype; + +// Reads inline JSX contents token. + +pp.jsx_readToken = function() { + var out = "", chunkStart = this.pos; + for (;;) { + if (this.pos >= this.input.length) + this.raise(this.start, "Unterminated JSX contents"); + var ch = this.input.charCodeAt(this.pos); + + switch (ch) { + case 60: // '<' + case 123: // '{' + if (this.pos === this.start) { + if (ch === 60 && this.exprAllowed) { + ++this.pos; + return this.finishToken(tt.jsxTagStart); + } + return this.getTokenFromCode(ch); + } + out += this.input.slice(chunkStart, this.pos); + return this.finishToken(tt.jsxText, out); + + case 38: // '&' + out += this.input.slice(chunkStart, this.pos); + out += this.jsx_readEntity(); + chunkStart = this.pos; + break; + + default: + if (acorn.isNewLine(ch)) { + out += this.input.slice(chunkStart, this.pos); + ++this.pos; + if (ch === 13 && this.input.charCodeAt(this.pos) === 10) { + ++this.pos; + out += "\n"; + } else { + out += String.fromCharCode(ch); + } + if (this.options.locations) { + ++this.curLine; + this.lineStart = this.pos; + } + chunkStart = this.pos; + } else { + ++this.pos; + } + } + } +}; + +pp.jsx_readString = function(quote) { + var out = "", chunkStart = ++this.pos; + for (;;) { + if (this.pos >= this.input.length) + this.raise(this.start, "Unterminated string constant"); + var ch = this.input.charCodeAt(this.pos); + if (ch === quote) break; + if (ch === 38) { // '&' + out += this.input.slice(chunkStart, this.pos); + out += this.jsx_readEntity(); + chunkStart = this.pos; + } else { + ++this.pos; + } + } + out += this.input.slice(chunkStart, this.pos++); + return this.finishToken(tt.string, out); +}; + +var XHTMLEntities = { + quot: '\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' +}; + +var hexNumber = /^[\da-fA-F]+$/; +var decimalNumber = /^\d+$/; + +pp.jsx_readEntity = function() { + var str = "", count = 0, entity; + var ch = this.input[this.pos]; + if (ch !== "&") + this.raise(this.pos, "Entity must start with an ampersand"); + var startPos = ++this.pos; + while (this.pos < this.input.length && count++ < 10) { + ch = this.input[this.pos++]; + if (ch === ";") { + if (str[0] === "#") { + if (str[1] === "x") { + str = str.substr(2); + if (hexNumber.test(str)) + entity = String.fromCharCode(parseInt(str, 16)); + } else { + str = str.substr(1); + if (decimalNumber.test(str)) + entity = String.fromCharCode(parseInt(str, 10)); + } + } else { + entity = XHTMLEntities[str]; + } + break; + } + str += ch; + } + if (!entity) { + this.pos = startPos; + return "&"; + } + return entity; +}; + + +// Read a JSX identifier (valid tag or attribute name). +// +// Optimized version since JSX identifiers can't contain +// escape characters and so can be read as single slice. +// Also assumes that first character was already checked +// by isIdentifierStart in readToken. + +pp.jsx_readWord = function() { + var ch, start = this.pos; + do { + ch = this.input.charCodeAt(++this.pos); + } while (acorn.isIdentifierChar(ch) || ch === 45); // '-' + return this.finishToken(tt.jsxName, this.input.slice(start, this.pos)); +}; + +// Transforms JSX element name to string. + +function getQualifiedJSXName(object) { + if (object.type === "JSXIdentifier") + return object.name; + + if (object.type === "JSXNamespacedName") + return object.namespace.name + ':' + object.name.name; + + if (object.type === "JSXMemberExpression") + return getQualifiedJSXName(object.object) + '.' + + getQualifiedJSXName(object.property); +} + +// Parse next token as JSX identifier + +pp.jsx_parseIdentifier = function() { + var node = this.startNode(); + if (this.type === tt.jsxName) + node.name = this.value; + else if (this.type.keyword) + node.name = this.type.keyword; + else + this.unexpected(); + this.next(); + return this.finishNode(node, "JSXIdentifier"); +}; + +// Parse namespaced identifier. + +pp.jsx_parseNamespacedName = function() { + var start = this.currentPos(); + var name = this.jsx_parseIdentifier(); + if (!this.eat(tt.colon)) return name; + var node = this.startNodeAt(start); + node.namespace = name; + node.name = this.jsx_parseIdentifier(); + return this.finishNode(node, "JSXNamespacedName"); +}; + +// Parses element name in any form - namespaced, member +// or single identifier. + +pp.jsx_parseElementName = function() { + var start = this.currentPos(); + var node = this.jsx_parseNamespacedName(); + while (this.eat(tt.dot)) { + var newNode = this.startNodeAt(start); + newNode.object = node; + newNode.property = this.jsx_parseIdentifier(); + node = this.finishNode(newNode, "JSXMemberExpression"); + } + return node; +}; + +// Parses any type of JSX attribute value. + +pp.jsx_parseAttributeValue = function() { + switch (this.type) { + case tt.braceL: + var node = this.jsx_parseExpressionContainer(); + if (node.expression.type === "JSXEmptyExpression") + this.raise(node.start, "JSX attributes must only be assigned a non-empty expression"); + return node; + + case tt.jsxTagStart: + case tt.string: + return this.parseExprAtom(); + + default: + this.raise(this.start, "JSX value should be either an expression or a quoted JSX text"); + } +}; + +// JSXEmptyExpression is unique type since it doesn't actually parse anything, +// and so it should start at the end of last read token (left brace) and finish +// at the beginning of the next one (right brace). + +pp.jsx_parseEmptyExpression = function() { + var tmp = this.start; + this.start = this.lastTokEnd; + this.lastTokEnd = tmp; + + tmp = this.startLoc; + this.startLoc = this.lastTokEndLoc; + this.lastTokEndLoc = tmp; + + return this.finishNode(this.startNode(), "JSXEmptyExpression"); +}; + +// Parses JSX expression enclosed into curly brackets. + + +pp.jsx_parseExpressionContainer = function() { + var node = this.startNode(); + this.next(); + node.expression = this.type === tt.braceR + ? this.jsx_parseEmptyExpression() + : this.parseExpression(); + this.expect(tt.braceR); + return this.finishNode(node, "JSXExpressionContainer"); +}; + +// Parses following JSX attribute name-value pair. + +pp.jsx_parseAttribute = function() { + var node = this.startNode(); + if (this.eat(tt.braceL)) { + this.expect(tt.ellipsis); + node.argument = this.parseMaybeAssign(); + this.expect(tt.braceR); + return this.finishNode(node, "JSXSpreadAttribute"); + } + node.name = this.jsx_parseNamespacedName(); + node.value = this.eat(tt.eq) ? this.jsx_parseAttributeValue() : null; + return this.finishNode(node, "JSXAttribute"); +}; + +// Parses JSX opening tag starting after '<'. + +pp.jsx_parseOpeningElementAt = function(start) { + var node = this.startNodeAt(start); + node.attributes = []; + node.name = this.jsx_parseElementName(); + while (this.type !== tt.slash && this.type !== tt.jsxTagEnd) + node.attributes.push(this.jsx_parseAttribute()); + node.selfClosing = this.eat(tt.slash); + this.expect(tt.jsxTagEnd); + return this.finishNode(node, "JSXOpeningElement"); +}; + +// Parses JSX closing tag starting after '"); + } + + node.openingElement = openingElement; + node.closingElement = closingElement; + node.children = children; + return this.finishNode(node, "JSXElement"); +}; + +// Parses entire JSX element from current position. + +pp.jsx_parseElement = function() { + var start = this.currentPos(); + this.next(); + return this.jsx_parseElementAt(start); +}; + +acorn.plugins.jsx = function(instance) { + instance.extend("parseExprAtom", function(inner) { + return function(refShortHandDefaultPos) { + if (this.type === tt.jsxText) + return this.parseLiteral(this.value); + else if (this.type === tt.jsxTagStart) + return this.jsx_parseElement(); + else + return inner.call(this, refShortHandDefaultPos); + }; + }); + + instance.extend("readToken", function(inner) { + return function(code) { + var context = this.curContext(); + + if (context === tc.j_expr) return this.jsx_readToken(); + + if (context === tc.j_oTag || context === tc.j_cTag) { + if (acorn.isIdentifierStart(code)) return this.jsx_readWord(); + + if (code == 62) { + ++this.pos; + return this.finishToken(tt.jsxTagEnd); + } + + if ((code === 34 || code === 39) && context == tc.j_oTag) + return this.jsx_readString(code); + } + + if (code === 60 && this.exprAllowed) { + ++this.pos; + return this.finishToken(tt.jsxTagStart); + } + return inner.call(this, code); + }; + }); + + instance.extend("updateContext", function(inner) { + return function(prevType) { + if (this.type == tt.braceL) { + var curContext = this.curContext(); + if (curContext == tc.j_oTag) this.context.push(tc.b_expr); + else if (curContext == tc.j_expr) this.context.push(tc.b_tmpl); + else inner.call(this, prevType); + this.exprAllowed = true; + } else if (this.type === tt.slash && prevType === tt.jsxTagStart) { + this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore + this.context.push(tc.j_cTag); // reconsider as closing tag context + this.exprAllowed = false; + } else { + return inner.call(this, prevType); + } + }; + }); +} diff --git a/src/babel/transformation/file/index.js b/src/babel/transformation/file/index.js index 453d2abb9c..a4db9cad11 100644 --- a/src/babel/transformation/file/index.js +++ b/src/babel/transformation/file/index.js @@ -21,6 +21,9 @@ import path from "path"; import each from "lodash/collection/each"; import * as t from "../../types"; +import "../../parser/jsx"; +import "../../parser/flow"; + var checkTransformerVisitor = { enter(node, parent, scope, state) { checkNode(state.stack, node, scope); @@ -35,7 +38,7 @@ function checkNode(stack, node, scope) { } export default class File { - constructor(opts) { + constructor(opts = {}) { this.dynamicImportedNoDefault = []; this.dynamicImportIds = {}; this.dynamicImported = []; @@ -46,8 +49,8 @@ export default class File { this.data = {}; this.lastStatements = []; + this.log = new Logger(this, opts.filename || "unknown"); this.opts = this.normalizeOptions(opts); - this.log = new Logger(this); this.ast = {}; this.buildTransformers(); @@ -98,7 +101,7 @@ export default class File { if (key[0] === "_") continue; let option = File.options[key]; - if (!option) throw new ReferenceError(`Unknown option: ${key}`); + if (!option) this.log.error(`Unknown option: ${key}`, ReferenceError); } for (let key in File.options) { @@ -181,7 +184,7 @@ export default class File { each(transform.transformers, function (transformer, key) { var pass = transformers[key] = transformer.buildPass(file); - if (pass.canRun) { + if (pass.canTransform) { stack.push(pass); if (transformer.metadata.secondPass) { @@ -274,7 +277,7 @@ export default class File { this.dynamicImported.push(declar); if (noDefault) this.dynamicImportedNoDefault.push(declar); - if (this.transformers["es6.modules"].canRun) { + if (this.transformers["es6.modules"].canTransform) { this.moduleFormatter.importSpecifier(specifiers[0], declar, this.dynamicImports); } else { this.dynamicImports.push(declar); @@ -382,19 +385,27 @@ export default class File { // - var parseOpts = {}; + var parseOpts = { + filename: opts.filename, + plugins: {} + }; var transformers = parseOpts.transformers = {}; for (var key in this.transformers) { - transformers[key] = this.transformers[key].canRun; + transformers[key] = this.transformers[key].canParse; } parseOpts.looseModules = this.isLoose("es6.modules"); - parseOpts.strictMode = this.transformers.strict.canRun; + parseOpts.strictMode = transformers.strict; + + if (!opts.standardOnly) { + parseOpts.plugins.jsx = true; + parseOpts.plugins.flow = true; + } // - return parse(opts, code, (tree) => { + return parse(parseOpts, code, (tree) => { this.transform(tree); return this.generate(); }); @@ -424,7 +435,7 @@ export default class File { this.lastStatements = t.getLastStatements(ast.program); var modFormatter = this.moduleFormatter = this.getModuleFormatter(this.opts.modules); - if (modFormatter.init && this.transformers["es6.modules"].canRun) { + if (modFormatter.init && this.transformers["es6.modules"].canTransform) { modFormatter.init(); } diff --git a/src/babel/transformation/file/logger.js b/src/babel/transformation/file/logger.js index deb4be5899..1a717963ab 100644 --- a/src/babel/transformation/file/logger.js +++ b/src/babel/transformation/file/logger.js @@ -1,8 +1,8 @@ import * as util from "../../util"; export default class Logger { - constructor(file: File) { - this.filename = file.opts.filename; + constructor(file: File, filename: string) { + this.filename = filename; this.file = file; } @@ -12,6 +12,10 @@ export default class Logger { return parts; } + error(msg: string, Constructor = Error) { + throw new Constructor(this._buildMessage(msg)); + } + deprecate(msg) { if (!this.file.opts.suppressDeprecationMessages) { console.error(msg); diff --git a/src/babel/transformation/modules/_default.js b/src/babel/transformation/modules/_default.js index 2158b8525f..955d0a6dc3 100644 --- a/src/babel/transformation/modules/_default.js +++ b/src/babel/transformation/modules/_default.js @@ -228,14 +228,14 @@ export default class DefaultFormatter { // export { foo } from "test"; nodes.push(this.buildExportsAssignment( - t.getSpecifierName(specifier), + node.local, ref, node )); } } else { // export { foo }; - nodes.push(this.buildExportsAssignment(t.getSpecifierName(specifier), specifier.id, node)); + nodes.push(this.buildExportsAssignment(specifier.local, specifier.exported, node)); } } diff --git a/src/babel/transformation/modules/amd.js b/src/babel/transformation/modules/amd.js index ce347bdb05..12a78cdcf2 100644 --- a/src/babel/transformation/modules/amd.js +++ b/src/babel/transformation/modules/amd.js @@ -69,7 +69,6 @@ export default class AMDFormatter extends DefaultFormatter { } importSpecifier(specifier, node, nodes) { - var key = t.getSpecifierName(specifier); var ref = this.getExternalReference(node); if (includes(this.file.dynamicImportedNoDefault, node)) { @@ -86,7 +85,7 @@ export default class AMDFormatter extends DefaultFormatter { } nodes.push(t.variableDeclaration("var", [ - t.variableDeclarator(key, ref) + t.variableDeclarator(node.local, ref) ])); } diff --git a/src/babel/types/alias-keys.json b/src/babel/types/alias-keys.json index f0b12a85b5..2c13e7767b 100644 --- a/src/babel/types/alias-keys.json +++ b/src/babel/types/alias-keys.json @@ -14,7 +14,9 @@ "EmptyStatement": ["Statement"], "LabeledStatement": ["Statement"], "VariableDeclaration": ["Statement", "Declaration"], - "ExportDeclaration": ["Statement", "Declaration", "ModuleDeclaration"], + "ExportAllDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + "ExportDefaultDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + "ExportNamedDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], "ImportDeclaration": ["Statement", "Declaration", "ModuleDeclaration"], "PrivateDeclaration": ["Statement", "Declaration"], diff --git a/src/babel/types/retrievers.js b/src/babel/types/retrievers.js index ca0cfb3490..3a44a3b7fd 100644 --- a/src/babel/types/retrievers.js +++ b/src/babel/types/retrievers.js @@ -18,8 +18,6 @@ export function getBindingIdentifiers(node: Object): Object { if (t.isIdentifier(id)) { ids[id.name] = id; - } else if (t.isImportSpecifier(id)) { - search.push(id.name || id.id); } else if (t.isExportDeclaration(id)) { if (t.isDeclaration(node.declaration)) { search.push(node.declaration); @@ -38,7 +36,8 @@ export function getBindingIdentifiers(node: Object): Object { getBindingIdentifiers.keys = { UnaryExpression: ["argument"], AssignmentExpression: ["left"], - ImportBatchSpecifier: ["name"], + ImportSpecifier: ["local"], + ImportNamespaceSpecifier: ["local"], VariableDeclarator: ["id"], FunctionDeclaration: ["id"], FunctionExpression: ["id"], @@ -83,23 +82,3 @@ export function getLastStatements(node: Object): Array { return nodes; } - -/** - * Description - */ - -export function getSpecifierName(specifier: Object): Object { - return specifier.name || specifier.id; -} - -/** - * Description - */ - -export function getSpecifierId(specifier: Object): Object { - if (specifier.default) { - return t.identifier("default"); - } else { - return specifier.id; - } -} diff --git a/src/babel/types/visitor-keys.json b/src/babel/types/visitor-keys.json index b30d9f0106..ce4a0d7db7 100644 --- a/src/babel/types/visitor-keys.json +++ b/src/babel/types/visitor-keys.json @@ -23,8 +23,10 @@ "DoWhileStatement": ["body", "test"], "EmptyStatement": [], "ExportBatchSpecifier": [], - "ExportDeclaration": ["declaration", "specifiers", "source"], - "ExportSpecifier": ["id", "name"], + "ExportAllDeclaration": ["source"], + "ExportDefaultDeclaration": ["declaration"], + "ExportNamedDeclaration": ["declaration", "specifiers", "source"], + "ExportSpecifier": ["local", "exported"], "ExpressionStatement": ["expression"], "File": ["program"], "ForInStatement": ["left", "right", "body"], @@ -34,9 +36,9 @@ "FunctionExpression": ["id", "params", "defaults", "rest", "body", "returnType", "typeParameters"], "Identifier": ["typeAnnotation"], "IfStatement": ["test", "consequent", "alternate"], - "ImportBatchSpecifier": ["id"], + "ImportNamespaceSpecifier": ["local"], "ImportDeclaration": ["specifiers", "source"], - "ImportSpecifier": ["id", "name"], + "ImportSpecifier": ["imported", "local"], "LabeledStatement": ["label", "body"], "Literal": [], "LogicalExpression": ["left", "right"],