diff --git a/src/comments.js b/src/comments.js index e9ee47f546..cb31ad1383 100644 --- a/src/comments.js +++ b/src/comments.js @@ -74,7 +74,7 @@ pp.processComment = function (node) { if (lastChild) { if (lastChild.leadingComments) { - if (last(lastChild.leadingComments).range[1] <= node.range[0]) { + if (last(lastChild.leadingComments).end <= node.start) { node.leadingComments = lastChild.leadingComments; lastChild.leadingComments = null; } else { @@ -82,7 +82,7 @@ pp.processComment = function (node) { // so this takes back the leading comment. // See Also: https://github.com/eslint/espree/issues/158 for (i = lastChild.leadingComments.length - 2; i >= 0; --i) { - if (lastChild.leadingComments[i].range[1] <= node.range[0]) { + if (lastChild.leadingComments[i].end <= node.start) { node.leadingComments = lastChild.leadingComments.splice(0, i + 1); break; } diff --git a/src/expression.js b/src/expression.js index d1f926d836..8baf02aa62 100755 --- a/src/expression.js +++ b/src/expression.js @@ -16,9 +16,9 @@ // // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser -import { types as tt } from "./tokentype"; +import { types as tt } from "./tokenizer/types"; import { Parser } from "./state"; -import { reservedWords } from "./identifier"; +import { reservedWords } from "./util/identifier"; const pp = Parser.prototype; @@ -723,10 +723,12 @@ pp.parseFunctionBody = function (node, allowExpression) { if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) { let nameHash = Object.create(null), oldStrict = this.strict; this.strict = true; - if (node.id) + if (node.id) { this.checkLVal(node.id, true); - for (let i = 0; i < node.params.length; i++) - this.checkLVal(node.params[i], true, nameHash); + } + for (let param of (node.params: Array)) { + this.checkLVal(param, true, nameHash); + } this.strict = oldStrict; } }; diff --git a/src/index.js b/src/index.js index b5647e85aa..5ee90a5404 100755 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,4 @@ import { Parser, plugins } from "./state"; -import { getOptions } from "./options"; import "./parseutil"; import "./statement"; import "./lval"; @@ -7,9 +6,9 @@ import "./expression"; import "./node"; import "./location"; import "./lookahead"; -import { types as tokTypes } from "./tokentype"; -import "./tokenize"; -import "./tokencontext"; +import { types as tokTypes } from "./tokenizer/types"; +import "./tokenizer"; +import "./tokenizer/context"; import "./comments"; import flowPlugin from "./plugins/flow"; import jsxPlugin from "./plugins/jsx"; @@ -18,7 +17,7 @@ plugins.flow = flowPlugin; plugins.jsx = jsxPlugin; export function parse(input, options) { - return new Parser(getOptions(options), input).parse(); + return new Parser(options, input).parse(); } export { tokTypes }; diff --git a/src/location.js b/src/location.js index b4ec229707..8fa2cd9678 100755 --- a/src/location.js +++ b/src/location.js @@ -1,45 +1,5 @@ +import {Position, getLineInfo} from "./locutil"; import { Parser } from "./state"; -import { lineBreakG } from "./whitespace"; - -// These are used when `options.locations` is on, for the -// `startLoc` and `endLoc` properties. - -export class Position { - constructor(line, col) { - this.line = line; - this.column = col; - } - - offset(n) { - return new Position(this.line, this.column + n); - } -} - -export class SourceLocation { - constructor(start, end) { - this.start = start; - this.end = end; - } -} - -// 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. - -export function getLineInfo(input, offset) { - for (let line = 1, cur = 0; ;) { - lineBreakG.lastIndex = cur; - let match = lineBreakG.exec(input); - if (match && match.index < offset) { - ++line; - cur = match.index + match[0].length; - } else { - return new Position(line, offset - cur); - } - } -} const pp = Parser.prototype; @@ -60,7 +20,5 @@ pp.raise = function (pos, message) { }; pp.curPosition = function () { - if (this.options.locations) { - return new Position(this.curLine, this.pos - this.lineStart); - } + return new Position(this.curLine, this.pos - this.lineStart); }; diff --git a/src/locutil.js b/src/locutil.js new file mode 100644 index 0000000000..796f5d0cdb --- /dev/null +++ b/src/locutil.js @@ -0,0 +1,41 @@ +import { lineBreakG } from "./util/whitespace"; + +// These are used when `options.locations` is on, for the +// `startLoc` and `endLoc` properties. + +export class Position { + constructor(line, col) { + this.line = line; + this.column = col; + } + + offset(n) { + return new Position(this.line, this.column + n); + } +} + +export class SourceLocation { + constructor(start, end) { + this.start = start; + this.end = end; + } +} + +// 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. + +export function getLineInfo(input, offset) { + for (let line = 1, cur = 0; ;) { + lineBreakG.lastIndex = cur; + let match = lineBreakG.exec(input); + if (match && match.index < offset) { + ++line; + cur = match.index + match[0].length; + } else { + return new Position(line, offset - cur); + } + } +} diff --git a/src/lookahead.js b/src/lookahead.js index 7ff04d587d..6bd4d00a45 100644 --- a/src/lookahead.js +++ b/src/lookahead.js @@ -30,8 +30,7 @@ var STATE_KEYS = [ pp.getState = function () { var state = {}; - for (var i = 0; i < STATE_KEYS.length; i++) { - var key = STATE_KEYS[i]; + for (var key of (STATE_KEYS: Array)) { state[key] = this[key]; } state.comments = this.comments.slice(); diff --git a/src/lval.js b/src/lval.js index abd6793a79..25eb8e5dad 100755 --- a/src/lval.js +++ b/src/lval.js @@ -1,6 +1,6 @@ -import { types as tt } from "./tokentype"; +import { types as tt } from "./tokenizer/types"; import { Parser } from "./state"; -import { reservedWords } from "./identifier"; +import { reservedWords } from "./util/identifier"; const pp = Parser.prototype; @@ -10,41 +10,40 @@ const pp = Parser.prototype; pp.toAssignable = function (node, isBinding) { if (node) { switch (node.type) { - case "Identifier": - case "ObjectPattern": - case "ArrayPattern": - case "AssignmentPattern": - break; + case "Identifier": + case "ObjectPattern": + case "ArrayPattern": + case "AssignmentPattern": + break; - case "ObjectExpression": - node.type = "ObjectPattern"; - for (let i = 0; i < node.properties.length; i++) { - let prop = node.properties[i]; - if (prop.type === "SpreadProperty") continue; - if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter"); - this.toAssignable(prop.value, isBinding); - } - break; + case "ObjectExpression": + node.type = "ObjectPattern"; + for (let prop of (node.properties: Array)) { + if (prop.type === "SpreadProperty") continue; + if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter"); + this.toAssignable(prop.value, isBinding); + } + break; - case "ArrayExpression": - node.type = "ArrayPattern"; - this.toAssignableList(node.elements, isBinding); - break; + case "ArrayExpression": + node.type = "ArrayPattern"; + this.toAssignableList(node.elements, isBinding); + break; - case "AssignmentExpression": - if (node.operator === "=") { - node.type = "AssignmentPattern"; - delete node.operator; - } else { - this.raise(node.left.end, "Only '=' operator can be used for specifying default value."); - } - break; + case "AssignmentExpression": + if (node.operator === "=") { + node.type = "AssignmentPattern"; + delete node.operator; + } else { + this.raise(node.left.end, "Only '=' operator can be used for specifying default value."); + } + break; - case "MemberExpression": - if (!isBinding) break; + case "MemberExpression": + if (!isBinding) break; - default: - this.raise(node.start, "Assigning to rvalue"); + default: + this.raise(node.start, "Assigning to rvalue"); } } return node; @@ -181,16 +180,14 @@ pp.checkLVal = function (expr, isBinding, checkClashes) { break; case "ObjectPattern": - for (let i = 0; i < expr.properties.length; i++) { - var prop = expr.properties[i]; + for (let prop of (expr.properties: Array)) { if (prop.type === "Property") prop = prop.value; this.checkLVal(prop, isBinding, checkClashes); } break; case "ArrayPattern": - for (let i = 0; i < expr.elements.length; i++) { - let elem = expr.elements[i]; + for (let elem of (expr.elements: Array)) { if (elem) this.checkLVal(elem, isBinding, checkClashes); } break; diff --git a/src/node.js b/src/node.js index 2ce99dfd94..52b3da6a6c 100755 --- a/src/node.js +++ b/src/node.js @@ -1,5 +1,5 @@ import { Parser } from "./state"; -import { SourceLocation } from "./location"; +import { SourceLocation } from "./locutil"; // Start an AST node, attaching a start offset. @@ -12,13 +12,7 @@ export class Node { this.end = 0; if (parser) { - if (parser.options.locations) { - this.loc = new SourceLocation(loc); - } - - if (parser.options.ranges) { - this.range = [pos, 0]; - } + this.loc = new SourceLocation(loc); } } @@ -40,8 +34,7 @@ pp.startNodeAt = function (pos, loc) { function finishNodeAt(node, type, pos, loc) { node.type = type; node.end = pos; - if (this.options.locations) node.loc.end = loc; - if (this.options.ranges) node.range[1] = pos; + node.loc.end = loc; this.processComment(node); return node; } diff --git a/src/options.js b/src/options.js index fbcb758db4..5c5d6d50f1 100755 --- a/src/options.js +++ b/src/options.js @@ -1,5 +1,3 @@ -import { has } from "./util"; - // A second optional argument can be given to further configure // the parser process. These options are recognized: @@ -17,20 +15,6 @@ export const defaultOptions = { // When enabled, import/export statements are not constrained to // appearing at the top of the program. allowImportExportEverywhere: 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, - // 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, plugins: {}, // Babel-specific options features: {}, @@ -41,8 +25,8 @@ export const defaultOptions = { export function getOptions(opts) { let options = {}; - for (let opt in defaultOptions) { - options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; + for (let key in defaultOptions) { + options[key] = opts && key in opts ? opts[key] : defaultOptions[key]; } return options; } diff --git a/src/parseutil.js b/src/parseutil.js index bf848bf68c..1793d23614 100755 --- a/src/parseutil.js +++ b/src/parseutil.js @@ -1,6 +1,6 @@ -import { types as tt } from "./tokentype"; +import { types as tt } from "./tokenizer/types"; import { Parser } from "./state"; -import { lineBreak } from "./whitespace"; +import { lineBreak } from "./util/whitespace"; const pp = Parser.prototype; diff --git a/src/plugins/flow.js b/src/plugins/flow.js index 3db24cd886..461d0597b8 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1,4 +1,4 @@ -import { types as tt } from "../tokentype"; +import { types as tt } from "../tokenizer/types"; import { Parser } from "../state"; var pp = Parser.prototype; diff --git a/src/plugins/jsx/index.js b/src/plugins/jsx/index.js index 3e3368f476..8aeaf9b2f9 100644 --- a/src/plugins/jsx/index.js +++ b/src/plugins/jsx/index.js @@ -1,9 +1,9 @@ import XHTMLEntities from "./xhtml"; -import { TokenType, types as tt } from "../../tokentype"; -import { TokContext, types as tc } from "../../tokencontext"; +import { TokenType, types as tt } from "../../tokenizer/types"; +import { TokContext, types as tc } from "../../tokenizer/context"; import { Parser } from "../../state"; -import { isIdentifierChar, isIdentifierStart } from "../../identifier"; -import { isNewLine } from "../../whitespace"; +import { isIdentifierChar, isIdentifierStart } from "../../util/identifier"; +import { isNewLine } from "../../util/whitespace"; const HEX_NUMBER = /^[\da-fA-F]+$/; const DECIMAL_NUMBER = /^\d+$/; @@ -87,10 +87,8 @@ pp.jsxReadNewLine = function(normalizeCRLF) { } else { out = String.fromCharCode(ch); } - if (this.options.locations) { - ++this.curLine; - this.lineStart = this.pos; - } + ++this.curLine; + this.lineStart = this.pos; return out; }; diff --git a/src/state.js b/src/state.js index fc47b6b78f..c5c503afaa 100755 --- a/src/state.js +++ b/src/state.js @@ -1,98 +1,69 @@ -import { reservedWords, keywords } from "./identifier"; -import { types as tt } from "./tokentype"; -import { lineBreak } from "./whitespace"; - -export function Parser(options, input, startPos) { - this.options = options; - this.isKeyword = keywords[6]; - this.isReservedWord = reservedWords[6]; - this.input = input; - this.loadPlugins(this.options.plugins); - - // Set up token state - - // The current position of the tokenizer in the input. - if (startPos) { - this.pos = startPos; - this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos)); - this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length; - } 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 = this.curPosition(); - - // Position information for the previous token - this.lastTokEndLoc = this.lastTokStartLoc = null; - this.lastTokStart = this.lastTokEnd = this.pos; - - // The context stack is used to superficially track syntactic - // context to predict whether a regular expression is allowed in a - // given position. - this.context = this.initialContext(); - this.exprAllowed = true; - - // Figure out if it's a module code. - this.inModule = this.options.sourceType === "module"; - this.strict = this.options.strictMode === false ? false : this.inModule; - - // Used to signify the start of a potential arrow function - this.potentialArrowAt = -1; - - // Flags to track whether we are in a function, a generator. - this.inFunction = this.inGenerator = false; - // Labels in scope. - this.labels = []; - - // Leading decorators. - this.decorators = []; - - // Token store. - this.tokens = []; - - // Comment store. - this.comments = []; - - // Comment attachment store - this.trailingComments = []; - this.leadingComments = []; - this.bottomRightStack = []; - - // If enabled, skip leading hashbang line. - if (this.pos === 0 && this.input[0] === "#" && this.input[1] === "!") { - this.skipLineComment(2); - } -} - -Parser.prototype.extend = function (name, f) { - this[name] = f(this[name]); -}; +import { reservedWords, isKeyword } from "./util/identifier"; +import { getOptions } from "./options"; +import Tokenizer from "./tokenizer"; // Registered plugins export const plugins = {}; -Parser.prototype.loadPlugins = function (plugins) { - for (let name in plugins) { - let plugin = exports.plugins[name]; - if (!plugin) throw new Error(`Plugin '${name}' not found`); - plugin(this, plugins[name]); - } -}; +export class Parser extends Tokenizer { + constructor(options, input) { + super(); -Parser.prototype.parse = function () { - let file = this.startNode(); - let program = this.startNode(); - this.nextToken(); - return this.parseTopLevel(file, program); -}; + this.options = getOptions(options); + this.isKeyword = isKeyword; + this.isReservedWord = reservedWords[6]; + this.input = input; + this.loadPlugins(this.options.plugins); + + // Figure out if it's a module code. + this.inModule = this.options.sourceType === "module"; + this.strict = this.options.strictMode === false ? false : this.inModule; + + // Used to signify the start of a potential arrow function + this.potentialArrowAt = -1; + + // Flags to track whether we are in a function, a generator. + this.inFunction = this.inGenerator = false; + // Labels in scope. + this.labels = []; + + // Leading decorators. + this.decorators = []; + + // Token store. + this.tokens = []; + + // Comment store. + this.comments = []; + + // Comment attachment store + this.trailingComments = []; + this.leadingComments = []; + this.bottomRightStack = []; + + // If enabled, skip leading hashbang line. + if (this.pos === 0 && this.input[0] === "#" && this.input[1] === "!") { + this.skipLineComment(2); + } + } + + extend(name, f) { + this[name] = f(this[name]); + } + + loadPlugins(plugins) { + for (let name in plugins) { + let plugin = exports.plugins[name]; + if (!plugin) throw new Error(`Plugin '${name}' not found`); + plugin(this, plugins[name]); + } + } + + parse() { + let file = this.startNode(); + let program = this.startNode(); + this.nextToken(); + return this.parseTopLevel(file, program); + } +} diff --git a/src/statement.js b/src/statement.js index 4856eb2a85..3e0795830e 100755 --- a/src/statement.js +++ b/src/statement.js @@ -1,6 +1,6 @@ -import { types as tt } from "./tokentype"; +import { types as tt } from "./tokenizer/types"; import { Parser } from "./state"; -import { lineBreak } from "./whitespace"; +import { lineBreak } from "./util/whitespace"; const pp = Parser.prototype; @@ -368,8 +368,8 @@ pp.parseEmptyStatement = function (node) { }; pp.parseLabeledStatement = function (node, maybeName, expr) { - for (let i = 0; i < this.labels.length; ++i){ - if (this.labels[i].name === maybeName) { + for (let label of (this.labels: Array)){ + if (label.name === maybeName) { this.raise(expr.start, `Label '${maybeName}' is already declared`); } } diff --git a/src/tokenize.js b/src/tokenize.js deleted file mode 100755 index cd85e99259..0000000000 --- a/src/tokenize.js +++ /dev/null @@ -1,746 +0,0 @@ -import { isIdentifierStart, isIdentifierChar } from "./identifier"; -import { types as tt, keywords as keywordTypes } from "./tokentype"; -import { Parser } from "./state"; -import { SourceLocation } from "./location"; -import { lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace } from "./whitespace"; - -// Object type used to represent tokens. Note that normally, tokens -// simply exist as properties on the parser object. This is only -// used for the onToken callback and the external tokenizer. - -export class Token { - constructor(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.startLoc, p.endLoc); - } - - if (p.options.ranges) { - this.range = [p.start, p.end]; - } - } -} - -// ## Tokenizer - -const pp = Parser.prototype; - -// Are we running under Rhino? -/* global Packages */ -const isRhino = typeof Packages === "object" && Object.prototype.toString.call(Packages) === "[object JavaPackage]"; - -// Move to the next token - -pp.next = function () { - if (!this.isLookahead) - this.tokens.push(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); -}; - -// 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 () { - let curContext = this.curContext(); - if (!curContext || !curContext.preserveSpace) this.skipSpace(); - - this.start = this.pos; - if (this.options.locations) this.startLoc = this.curPosition(); - if (this.pos >= this.input.length) return this.finishToken(tt.eof); - - if (curContext.override) { - return curContext.override(this); - } else { - return 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, true) || code === 92 /* '\' */) - return this.readWord(); - - return this.getTokenFromCode(code); -}; - -pp.fullCharCodeAtPos = function () { - let code = this.input.charCodeAt(this.pos); - if (code <= 0xd7ff || code >= 0xe000) return code; - - let next = this.input.charCodeAt(this.pos + 1); - return (code << 10) + next - 0x35fdc00; -}; - -function pushComment(block, text, start, end, startLoc, endLoc) { - var comment = { - type: block ? "CommentBlock" : "CommentLine", - value: text, - start: start, - end: end, - loc: new SourceLocation(startLoc, endLoc), - range: [start, end] - }; - - this.tokens.push(comment); - this.comments.push(comment); - this.addComment(comment); -} - -pp.skipBlockComment = function () { - let startLoc = this.curPosition(); - let start = this.pos, end = this.input.indexOf("*/", this.pos += 2); - if (end === -1) this.raise(this.pos - 2, "Unterminated comment"); - - this.pos = end + 2; - if (this.options.locations) { - lineBreakG.lastIndex = start; - let match; - while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) { - ++this.curLine; - this.lineStart = match.index + match[0].length; - } - } - - pushComment.call(this, true, this.input.slice(start + 2, end), start, this.pos, startLoc, this.curPosition()); -}; - -pp.skipLineComment = function (startSkip) { - let start = this.pos; - let startLoc = this.curPosition(); - let ch = this.input.charCodeAt(this.pos += startSkip); - while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { - ++this.pos; - ch = this.input.charCodeAt(this.pos); - } - - pushComment.call(this, false, this.input.slice(start + startSkip, this.pos), start, this.pos, startLoc, this.curPosition()); -}; - -// Called at the start of the parse and after every token. Skips -// whitespace and comments, and. - -pp.skipSpace = function() { - loop: while (this.pos < this.input.length) { - let ch = this.input.charCodeAt(this.pos); - switch (ch) { - case 32: case 160: // ' ' - ++this.pos; - break; - - case 13: - if (this.input.charCodeAt(this.pos + 1) === 10) { - ++this.pos; - } - - case 10: case 8232: case 8233: - ++this.pos; - if (this.options.locations) { - ++this.curLine; - this.lineStart = this.pos; - } - break; - - case 47: // '/' - switch (this.input.charCodeAt(this.pos + 1)) { - case 42: // '*' - this.skipBlockComment(); - break; - - case 47: - this.skipLineComment(2); - break; - - default: - break loop; - } - break; - - default: - if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { - ++this.pos; - } else { - break loop; - } - } - } -}; - -// 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(); - let prevType = this.type; - this.type = type; - this.value = val; - - this.updateContext(prevType); -}; - -// ### Token reading - -// This is the function that is called to fetch the next token. It -// is somewhat obscure, because it works in character codes rather -// than characters, and because operator parsing has been inlined -// into it. -// -// All in the name of speed. -// -pp.readToken_dot = function () { - let next = this.input.charCodeAt(this.pos + 1); - if (next >= 48 && next <= 57) return this.readNumber(true); - - let next2 = this.input.charCodeAt(this.pos + 2); - if (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 () { // '/' - let next = this.input.charCodeAt(this.pos + 1); - if (this.exprAllowed) { - ++this.pos; - return this.readRegexp(); - } - if (next === 61) return this.finishOp(tt.assign, 2); - return this.finishOp(tt.slash, 1); -}; - -pp.readToken_mult_modulo = function (code) { // '%*' - var type = code === 42 ? tt.star : tt.modulo; - var width = 1; - var next = this.input.charCodeAt(this.pos + 1); - - if (next === 42 && this.options.features["es7.exponentiationOperator"]) { // '*' - 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) { // '|&' - let 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 () { // '^' - let next = this.input.charCodeAt(this.pos + 1); - if (next === 61) { - return this.finishOp(tt.assign, 2); - } else { - return this.finishOp(tt.bitwiseXOR, 1); - } -}; - -pp.readToken_plus_min = function (code) { // '+-' - let next = this.input.charCodeAt(this.pos + 1); - - if (next === code) { - if (next === 45 && this.input.charCodeAt(this.pos + 2) === 62 && lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) { - // A `-->` line comment - this.skipLineComment(3); - this.skipSpace(); - return this.nextToken(); - } - return this.finishOp(tt.incDec, 2); - } - - if (next === 61) { - return this.finishOp(tt.assign, 2); - } else { - return this.finishOp(tt.plusMin, 1); - } -}; - -pp.readToken_lt_gt = function (code) { // '<>' - let next = this.input.charCodeAt(this.pos + 1); - let size = 1; - - if (next === code) { - size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; - if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); - return this.finishOp(tt.bitShift, size); - } - - if (next === 33 && code === 60 && this.input.charCodeAt(this.pos + 2) === 45 && this.input.charCodeAt(this.pos + 3) === 45) { - if (this.inModule) this.unexpected(); - // `` 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); + } else { + return this.finishOp(tt.plusMin, 1); + } + } + + readToken_lt_gt(code) { // '<>' + let next = this.input.charCodeAt(this.pos + 1); + let size = 1; + + if (next === code) { + size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; + if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); + return this.finishOp(tt.bitShift, size); + } + + if (next === 33 && code === 60 && this.input.charCodeAt(this.pos + 2) === 45 && this.input.charCodeAt(this.pos + 3) === 45) { + if (this.inModule) this.unexpected(); + // `