From f538706db39f20cb0d8140d334578c4a46557a65 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Mar 2015 16:59:11 +0100 Subject: [PATCH] Code style -- less semicolons, more let variables --- src/expression.js | 777 +++++++++++++++++----------------------- src/identifier.js | 90 ++--- src/location.js | 20 +- src/loose/expression.js | 701 +++++++++++++++--------------------- src/loose/parseutil.js | 134 +++---- src/loose/state.js | 16 +- src/loose/statement.js | 502 ++++++++++++++++---------- src/loose/tokenize.js | 88 ++--- src/lval.js | 176 ++++----- src/node.js | 54 +-- src/options.js | 6 +- src/parseutil.js | 50 +-- src/statement.js | 628 +++++++++++++++++++------------- src/tokencontext.js | 72 ++-- src/tokenize.js | 615 +++++++++++++++---------------- 15 files changed, 1966 insertions(+), 1963 deletions(-) diff --git a/src/expression.js b/src/expression.js index af0f229859..82eda65434 100644 --- a/src/expression.js +++ b/src/expression.js @@ -1,5 +1,3 @@ -// ## 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 @@ -31,28 +29,28 @@ const pp = Parser.prototype // strict mode, init properties are also not allowed to be repeated. pp.checkPropClash = function(prop, propHash) { - if (this.options.ecmaVersion >= 6) return; - var key = prop.key, name; + if (this.options.ecmaVersion >= 6) return + let key = prop.key, name switch (key.type) { - case "Identifier": name = key.name; break; - case "Literal": name = String(key.value); break; - default: return; + case "Identifier": name = key.name; break + case "Literal": name = String(key.value); break + default: return } - var kind = prop.kind || "init", other; + let kind = prop.kind || "init", other if (has(propHash, name)) { - other = propHash[name]; - var isGetSet = kind !== "init"; + other = propHash[name] + let isGetSet = kind !== "init" if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init)) - this.raise(key.start, "Redefinition of property"); + this.raise(key.start, "Redefinition of property") } else { other = propHash[name] = { init: false, get: false, set: false - }; + } } - other[kind] = true; -}; + other[kind] = true +} // ### Expression parsing @@ -70,72 +68,72 @@ pp.checkPropClash = function(prop, propHash) { // delayed syntax error at correct position). pp.parseExpression = function(noIn, refShorthandDefaultPos) { - var start = this.markPosition(); - var expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos); + let start = this.markPosition() + let expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos) if (this.type === tt.comma) { - var node = this.startNodeAt(start); - node.expressions = [expr]; - while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos)); - return this.finishNode(node, "SequenceExpression"); + let node = this.startNodeAt(start) + node.expressions = [expr] + while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos)) + return this.finishNode(node, "SequenceExpression") } - return expr; -}; + return expr +} // Parse an assignment expression. This includes applications of // operators like `+=`. pp.parseMaybeAssign = function(noIn, refShorthandDefaultPos) { - if (this.type == tt._yield && this.inGenerator) return this.parseYield(); + if (this.type == tt._yield && this.inGenerator) return this.parseYield() - var failOnShorthandAssign; + let failOnShorthandAssign if (!refShorthandDefaultPos) { - refShorthandDefaultPos = {start: 0}; - failOnShorthandAssign = true; + refShorthandDefaultPos = {start: 0} + failOnShorthandAssign = true } else { - failOnShorthandAssign = false; + failOnShorthandAssign = false } - var start = this.markPosition(); - var left = this.parseMaybeConditional(noIn, refShorthandDefaultPos); + let start = this.markPosition() + let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos) if (this.type.isAssign) { - var node = this.startNodeAt(start); - node.operator = this.value; - node.left = this.type === tt.eq ? this.toAssignable(left) : left; + let node = this.startNodeAt(start) + node.operator = this.value + node.left = this.type === tt.eq ? this.toAssignable(left) : left refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly - this.checkLVal(left); - this.next(); - node.right = this.parseMaybeAssign(noIn); - return this.finishNode(node, "AssignmentExpression"); + this.checkLVal(left) + this.next() + node.right = this.parseMaybeAssign(noIn) + return this.finishNode(node, "AssignmentExpression") } else if (failOnShorthandAssign && refShorthandDefaultPos.start) { - this.unexpected(refShorthandDefaultPos.start); + this.unexpected(refShorthandDefaultPos.start) } - return left; -}; + return left +} // Parse a ternary conditional (`?:`) operator. pp.parseMaybeConditional = function(noIn, refShorthandDefaultPos) { - var start = this.markPosition(); - var expr = this.parseExprOps(noIn, refShorthandDefaultPos); - if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + let start = this.markPosition() + let expr = this.parseExprOps(noIn, refShorthandDefaultPos) + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr if (this.eat(tt.question)) { - var node = this.startNodeAt(start); - node.test = expr; - node.consequent = this.parseMaybeAssign(); - this.expect(tt.colon); - node.alternate = this.parseMaybeAssign(noIn); - return this.finishNode(node, "ConditionalExpression"); + let node = this.startNodeAt(start) + node.test = expr + node.consequent = this.parseMaybeAssign() + this.expect(tt.colon) + node.alternate = this.parseMaybeAssign(noIn) + return this.finishNode(node, "ConditionalExpression") } - return expr; -}; + return expr +} // Start the precedence parser. pp.parseExprOps = function(noIn, refShorthandDefaultPos) { - var start = this.markPosition(); - var expr = this.parseMaybeUnary(refShorthandDefaultPos); - if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; - return this.parseExprOp(expr, start, -1, noIn); -}; + let start = this.markPosition() + let expr = this.parseMaybeUnary(refShorthandDefaultPos) + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr + return this.parseExprOp(expr, start, -1, noIn) +} // Parse binary operators with the operator precedence parsing // algorithm. `left` is the left-hand side of the operator. @@ -144,89 +142,89 @@ pp.parseExprOps = function(noIn, refShorthandDefaultPos) { // operator that has a lower precedence than the set it is parsing. pp.parseExprOp = function(left, leftStart, minPrec, noIn) { - var prec = this.type.binop; + let prec = this.type.binop if (prec != null && (!noIn || this.type !== tt._in)) { if (prec > minPrec) { - var node = this.startNodeAt(leftStart); - node.left = left; - node.operator = this.value; - var op = this.type; - this.next(); - var start = this.markPosition(); - node.right = this.parseExprOp(this.parseMaybeUnary(), start, prec, noIn); - this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression"); - return this.parseExprOp(node, leftStart, minPrec, noIn); + let node = this.startNodeAt(leftStart) + node.left = left + node.operator = this.value + let op = this.type + this.next() + let start = this.markPosition() + node.right = this.parseExprOp(this.parseMaybeUnary(), start, prec, noIn) + this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression") + return this.parseExprOp(node, leftStart, minPrec, noIn) } } - return left; -}; + return left +} // Parse unary operators, both prefix and postfix. pp.parseMaybeUnary = function(refShorthandDefaultPos) { if (this.type.prefix) { - var node = this.startNode(), update = this.type === tt.incDec; - node.operator = this.value; - node.prefix = true; - this.next(); - node.argument = this.parseMaybeUnary(); - if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); - if (update) this.checkLVal(node.argument); + let node = this.startNode(), update = this.type === tt.incDec + node.operator = this.value + node.prefix = true + this.next() + node.argument = this.parseMaybeUnary() + if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start) + if (update) this.checkLVal(node.argument) else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") - this.raise(node.start, "Deleting local variable in strict mode"); - return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); + this.raise(node.start, "Deleting local variable in strict mode") + return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression") } - var start = this.markPosition(); - var expr = this.parseExprSubscripts(refShorthandDefaultPos); - if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + let start = this.markPosition() + let expr = this.parseExprSubscripts(refShorthandDefaultPos) + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr while (this.type.postfix && !this.canInsertSemicolon()) { - var node = this.startNodeAt(start); - node.operator = this.value; - node.prefix = false; - node.argument = expr; - this.checkLVal(expr); - this.next(); - expr = this.finishNode(node, "UpdateExpression"); + let node = this.startNodeAt(start) + node.operator = this.value + node.prefix = false + node.argument = expr + this.checkLVal(expr) + this.next() + expr = this.finishNode(node, "UpdateExpression") } - return expr; -}; + return expr +} // Parse call, dot, and `[]`-subscript expressions. pp.parseExprSubscripts = function(refShorthandDefaultPos) { - var start = this.markPosition(); - var expr = this.parseExprAtom(refShorthandDefaultPos); - if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; - return this.parseSubscripts(expr, start); -}; + let start = this.markPosition() + let expr = this.parseExprAtom(refShorthandDefaultPos) + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr + return this.parseSubscripts(expr, start) +} pp.parseSubscripts = function(base, start, noCalls) { if (this.eat(tt.dot)) { - var node = this.startNodeAt(start); - node.object = base; - node.property = this.parseIdent(true); - node.computed = false; - return this.parseSubscripts(this.finishNode(node, "MemberExpression"), start, noCalls); + let node = this.startNodeAt(start) + node.object = base + node.property = this.parseIdent(true) + node.computed = false + return this.parseSubscripts(this.finishNode(node, "MemberExpression"), start, noCalls) } else if (this.eat(tt.bracketL)) { - var node = this.startNodeAt(start); - node.object = base; - node.property = this.parseExpression(); - node.computed = true; - this.expect(tt.bracketR); - return this.parseSubscripts(this.finishNode(node, "MemberExpression"), start, noCalls); + let node = this.startNodeAt(start) + node.object = base + node.property = this.parseExpression() + node.computed = true + this.expect(tt.bracketR) + return this.parseSubscripts(this.finishNode(node, "MemberExpression"), start, noCalls) } else if (!noCalls && this.eat(tt.parenL)) { - var node = this.startNodeAt(start); - node.callee = base; - node.arguments = this.parseExprList(tt.parenR, false); - return this.parseSubscripts(this.finishNode(node, "CallExpression"), start, noCalls); + let node = this.startNodeAt(start) + node.callee = base + node.arguments = this.parseExprList(tt.parenR, false) + return this.parseSubscripts(this.finishNode(node, "CallExpression"), start, noCalls) } else if (this.type === tt.backQuote) { - var node = this.startNodeAt(start); - node.tag = base; - node.quasi = this.parseTemplate(); - return this.parseSubscripts(this.finishNode(node, "TaggedTemplateExpression"), start, noCalls); - } return base; -}; + let node = this.startNodeAt(start) + node.tag = base + node.quasi = this.parseTemplate() + return this.parseSubscripts(this.finishNode(node, "TaggedTemplateExpression"), start, noCalls) + } return base +} // Parse an atomic expression — either a single token that is an // expression, an expression started by a keyword like `function` or @@ -234,146 +232,147 @@ pp.parseSubscripts = function(base, start, noCalls) { // or `{}`. pp.parseExprAtom = function(refShorthandDefaultPos) { + let node switch (this.type) { case tt._this: case tt._super: - var type = this.type === tt._this ? "ThisExpression" : "SuperExpression"; - var node = this.startNode(); - this.next(); - return this.finishNode(node, type); + let type = this.type === tt._this ? "ThisExpression" : "SuperExpression" + node = this.startNode() + this.next() + return this.finishNode(node, type) case tt._yield: - if (this.inGenerator) unexpected(); + if (this.inGenerator) unexpected() case tt.name: - var start = this.markPosition(); - var id = this.parseIdent(this.type !== tt.name); + let start = this.markPosition() + let id = this.parseIdent(this.type !== tt.name) if (!this.canInsertSemicolon() && this.eat(tt.arrow)) { - return this.parseArrowExpression(this.startNodeAt(start), [id]); + return this.parseArrowExpression(this.startNodeAt(start), [id]) } - return id; + return id case tt.regexp: - var value = this.value; - var node = this.parseLiteral(value.value); - node.regex = {pattern: value.pattern, flags: value.flags}; - return node; + let value = this.value + node = this.parseLiteral(value.value) + node.regex = {pattern: value.pattern, flags: value.flags} + return node case tt.num: case tt.string: - return this.parseLiteral(this.value); + return this.parseLiteral(this.value) case tt._null: case tt._true: case tt._false: - var node = this.startNode(); - node.value = this.type === tt._null ? null : this.type === tt._true; - node.raw = this.type.keyword; - this.next(); - return this.finishNode(node, "Literal"); + node = this.startNode() + node.value = this.type === tt._null ? null : this.type === tt._true + node.raw = this.type.keyword + this.next() + return this.finishNode(node, "Literal") case tt.parenL: - return this.parseParenAndDistinguishExpression(); + return this.parseParenAndDistinguishExpression() case tt.bracketL: - var node = this.startNode(); - this.next(); + node = this.startNode() + this.next() // check whether this is array comprehension or regular array if (this.options.ecmaVersion >= 7 && this.type === tt._for) { - return this.parseComprehension(node, false); + return this.parseComprehension(node, false) } - node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos); - return this.finishNode(node, "ArrayExpression"); + node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos) + return this.finishNode(node, "ArrayExpression") case tt.braceL: - return this.parseObj(false, refShorthandDefaultPos); + return this.parseObj(false, refShorthandDefaultPos) case tt._function: - var node = this.startNode(); - this.next(); - return this.parseFunction(node, false); + node = this.startNode() + this.next() + return this.parseFunction(node, false) case tt._class: - return this.parseClass(this.startNode(), false); + return this.parseClass(this.startNode(), false) case tt._new: - return this.parseNew(); + return this.parseNew() case tt.backQuote: - return this.parseTemplate(); + return this.parseTemplate() default: - this.unexpected(); + this.unexpected() } -}; +} pp.parseLiteral = function(value) { - var node = this.startNode(); - node.value = value; - node.raw = this.input.slice(this.start, this.end); - this.next(); - return this.finishNode(node, "Literal"); -}; + let node = this.startNode() + node.value = value + node.raw = this.input.slice(this.start, this.end) + this.next() + return this.finishNode(node, "Literal") +} pp.parseParenExpression = function() { - this.expect(tt.parenL); - var val = this.parseExpression(); - this.expect(tt.parenR); - return val; -}; + this.expect(tt.parenL) + let val = this.parseExpression() + this.expect(tt.parenR) + return val +} pp.parseParenAndDistinguishExpression = function() { - var start = this.markPosition(), val; + let start = this.markPosition(), val if (this.options.ecmaVersion >= 6) { - this.next(); + this.next() if (this.options.ecmaVersion >= 7 && this.type === tt._for) { - return this.parseComprehension(this.startNodeAt(start), true); + return this.parseComprehension(this.startNodeAt(start), true) } - var innerStart = this.markPosition(), exprList = [], first = true; - var refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart; + let innerStart = this.markPosition(), exprList = [], first = true + let refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart while (this.type !== tt.parenR) { - first ? first = false : this.expect(tt.comma); + first ? first = false : this.expect(tt.comma) if (this.type === tt.ellipsis) { - spreadStart = this.start; - exprList.push(this.parseRest()); - break; + spreadStart = this.start + exprList.push(this.parseRest()) + break } else { if (this.type === tt.parenL && !innerParenStart) { - innerParenStart = this.start; + innerParenStart = this.start } - exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos)); + exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos)) } } - var innerEnd = this.markPosition(); - this.expect(tt.parenR); + let innerEnd = this.markPosition() + this.expect(tt.parenR) if (!this.canInsertSemicolon() && this.eat(tt.arrow)) { - if (innerParenStart) this.unexpected(innerParenStart); - return this.parseArrowExpression(this.startNodeAt(start), exprList); + if (innerParenStart) this.unexpected(innerParenStart) + return this.parseArrowExpression(this.startNodeAt(start), exprList) } - if (!exprList.length) this.unexpected(this.lastTokStart); - if (spreadStart) this.unexpected(spreadStart); - if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); + if (!exprList.length) this.unexpected(this.lastTokStart) + if (spreadStart) this.unexpected(spreadStart) + if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start) if (exprList.length > 1) { - val = this.startNodeAt(innerStart); - val.expressions = exprList; - this.finishNodeAt(val, "SequenceExpression", innerEnd); + val = this.startNodeAt(innerStart) + val.expressions = exprList + this.finishNodeAt(val, "SequenceExpression", innerEnd) } else { - val = exprList[0]; + val = exprList[0] } } else { - val = this.parseParenExpression(); + val = this.parseParenExpression() } if (this.options.preserveParens) { - var par = this.startNodeAt(start); - par.expression = val; - return this.finishNode(par, "ParenthesizedExpression"); + let par = this.startNodeAt(start) + par.expression = val + return this.finishNode(par, "ParenthesizedExpression") } else { - return val; + return val } -}; +} // New's precedence is slightly tricky. It must allow its argument // to be a `[]` or dot subscript expression, but not a call — at @@ -382,187 +381,187 @@ pp.parseParenAndDistinguishExpression = function() { const empty = [] pp.parseNew = function() { - var node = this.startNode(); - this.next(); - var start = this.markPosition(); - node.callee = this.parseSubscripts(this.parseExprAtom(), start, true); - if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false); - else node.arguments = empty; - return this.finishNode(node, "NewExpression"); -}; + let node = this.startNode() + this.next() + let start = this.markPosition() + node.callee = this.parseSubscripts(this.parseExprAtom(), start, true) + if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false) + else node.arguments = empty + return this.finishNode(node, "NewExpression") +} // Parse template expression. pp.parseTemplateElement = function() { - var elem = this.startNode(); + let elem = this.startNode() elem.value = { raw: this.input.slice(this.start, this.end), cooked: this.value - }; - this.next(); - elem.tail = this.type === tt.backQuote; - return this.finishNode(elem, "TemplateElement"); -}; + } + this.next() + elem.tail = this.type === tt.backQuote + return this.finishNode(elem, "TemplateElement") +} pp.parseTemplate = function() { - var node = this.startNode(); - this.next(); - node.expressions = []; - var curElt = this.parseTemplateElement(); - node.quasis = [curElt]; + let node = this.startNode() + this.next() + node.expressions = [] + let curElt = this.parseTemplateElement() + node.quasis = [curElt] while (!curElt.tail) { - this.expect(tt.dollarBraceL); - node.expressions.push(this.parseExpression()); - this.expect(tt.braceR); - node.quasis.push(curElt = this.parseTemplateElement()); + this.expect(tt.dollarBraceL) + node.expressions.push(this.parseExpression()) + this.expect(tt.braceR) + node.quasis.push(curElt = this.parseTemplateElement()) } - this.next(); - return this.finishNode(node, "TemplateLiteral"); -}; + this.next() + return this.finishNode(node, "TemplateLiteral") +} // Parse an object literal or binding pattern. pp.parseObj = function(isPattern, refShorthandDefaultPos) { - var node = this.startNode(), first = true, propHash = {}; - node.properties = []; - this.next(); + let node = this.startNode(), first = true, propHash = {} + node.properties = [] + this.next() while (!this.eat(tt.braceR)) { if (!first) { - this.expect(tt.comma); - if (this.afterTrailingComma(tt.braceR)) break; - } else first = false; + this.expect(tt.comma) + if (this.afterTrailingComma(tt.braceR)) break + } else first = false - var prop = this.startNode(), isGenerator, start; + let prop = this.startNode(), isGenerator, start if (this.options.ecmaVersion >= 6) { - prop.method = false; - prop.shorthand = false; + prop.method = false + prop.shorthand = false if (isPattern || refShorthandDefaultPos) - start = this.markPosition(); + start = this.markPosition() if (!isPattern) - isGenerator = this.eat(tt.star); + isGenerator = this.eat(tt.star) } - this.parsePropertyName(prop); + this.parsePropertyName(prop) if (this.eat(tt.colon)) { - prop.value = isPattern ? this.parseMaybeDefault() : this.parseMaybeAssign(false, refShorthandDefaultPos); - prop.kind = "init"; + prop.value = isPattern ? this.parseMaybeDefault() : this.parseMaybeAssign(false, refShorthandDefaultPos) + prop.kind = "init" } else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) { - if (isPattern) this.unexpected(); - prop.kind = "init"; - prop.method = true; - prop.value = this.parseMethod(isGenerator); + if (isPattern) this.unexpected() + prop.kind = "init" + prop.method = true + prop.value = this.parseMethod(isGenerator) } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && (this.type != tt.comma && this.type != tt.braceR)) { - if (isGenerator || isPattern) this.unexpected(); - prop.kind = prop.key.name; - this.parsePropertyName(prop); - prop.value = this.parseMethod(false); + if (isGenerator || isPattern) this.unexpected() + prop.kind = prop.key.name + this.parsePropertyName(prop) + prop.value = this.parseMethod(false) } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") { - prop.kind = "init"; + prop.kind = "init" if (isPattern) { if (this.isKeyword(prop.key.name) || (this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) || (!this.options.allowReserved && this.isReservedWord(prop.key.name))) - this.raise(prop.key.start, "Binding " + prop.key.name); - prop.value = this.parseMaybeDefault(start, prop.key); + this.raise(prop.key.start, "Binding " + prop.key.name) + prop.value = this.parseMaybeDefault(start, prop.key) } else if (this.type === tt.eq && refShorthandDefaultPos) { if (!refShorthandDefaultPos.start) - refShorthandDefaultPos.start = this.start; - prop.value = this.parseMaybeDefault(start, prop.key); + refShorthandDefaultPos.start = this.start + prop.value = this.parseMaybeDefault(start, prop.key) } else { - prop.value = prop.key; + prop.value = prop.key } - prop.shorthand = true; - } else this.unexpected(); + prop.shorthand = true + } else this.unexpected() - this.checkPropClash(prop, propHash); - node.properties.push(this.finishNode(prop, "Property")); + this.checkPropClash(prop, propHash) + node.properties.push(this.finishNode(prop, "Property")) } - return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression"); -}; + return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression") +} pp.parsePropertyName = function(prop) { if (this.options.ecmaVersion >= 6) { if (this.eat(tt.bracketL)) { - prop.computed = true; - prop.key = this.parseExpression(); - this.expect(tt.bracketR); - return; + prop.computed = true + prop.key = this.parseExpression() + this.expect(tt.bracketR) + return } else { - prop.computed = false; + prop.computed = false } } - prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true); -}; + prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true) +} // Initialize empty function node. pp.initFunction = function(node) { - node.id = null; + node.id = null if (this.options.ecmaVersion >= 6) { - node.generator = false; - node.expression = false; + node.generator = false + node.expression = false } -}; +} // Parse object or class method. pp.parseMethod = function(isGenerator) { - var node = this.startNode(); - this.initFunction(node); - this.expect(tt.parenL); - node.params = this.parseBindingList(tt.parenR, false, false); - var allowExpressionBody; + let node = this.startNode() + this.initFunction(node) + this.expect(tt.parenL) + node.params = this.parseBindingList(tt.parenR, false, false) + let allowExpressionBody if (this.options.ecmaVersion >= 6) { - node.generator = isGenerator; - allowExpressionBody = true; + node.generator = isGenerator + allowExpressionBody = true } else { - allowExpressionBody = false; + allowExpressionBody = false } - this.parseFunctionBody(node, allowExpressionBody); - return this.finishNode(node, "FunctionExpression"); -}; + this.parseFunctionBody(node, allowExpressionBody) + return this.finishNode(node, "FunctionExpression") +} // Parse arrow function expression with given parameters. pp.parseArrowExpression = function(node, params) { - this.initFunction(node); - node.params = this.toAssignableList(params, true); - this.parseFunctionBody(node, true); - return this.finishNode(node, "ArrowFunctionExpression"); -}; + this.initFunction(node) + node.params = this.toAssignableList(params, true) + this.parseFunctionBody(node, true) + return this.finishNode(node, "ArrowFunctionExpression") +} // Parse function body and check parameters. pp.parseFunctionBody = function(node, allowExpression) { - var isExpression = allowExpression && this.type !== tt.braceL; + let isExpression = allowExpression && this.type !== tt.braceL if (isExpression) { - node.body = this.parseMaybeAssign(); - node.expression = true; + node.body = this.parseMaybeAssign() + node.expression = true } else { // Start a new scope with regard to labels and the `inFunction` // flag (restore them to their old value afterwards). - var oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels; - this.inFunction = true; this.inGenerator = node.generator; this.labels = []; - node.body = this.parseBlock(true); - node.expression = false; - this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels; + let oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels + this.inFunction = true; this.inGenerator = node.generator; this.labels = [] + node.body = this.parseBlock(true) + node.expression = false + this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels } // If this is a strict mode function, verify that argument names // are not repeated, and it does not try to bind the words `eval` // or `arguments`. if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) { - var nameHash = {}, oldStrict = this.strict; - this.strict = true; + let nameHash = {}, oldStrict = this.strict + this.strict = true if (node.id) - this.checkLVal(node.id, true); - for (var i = 0; i < node.params.length; i++) - this.checkLVal(node.params[i], true, nameHash); - this.strict = oldStrict; + this.checkLVal(node.id, true) + for (let i = 0; i < node.params.length; i++) + this.checkLVal(node.params[i], true, nameHash) + this.strict = oldStrict } -}; +} // Parses a comma-separated list of expressions, and returns them as // an array. `close` is the token type that ends the list, and @@ -571,198 +570,82 @@ pp.parseFunctionBody = function(node, allowExpression) { // for array literals). pp.parseExprList = function(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) { - var elts = [], first = true; + let elts = [], first = true while (!this.eat(close)) { if (!first) { - this.expect(tt.comma); - if (allowTrailingComma && this.afterTrailingComma(close)) break; - } else first = false; + this.expect(tt.comma) + if (allowTrailingComma && this.afterTrailingComma(close)) break + } else first = false if (allowEmpty && this.type === tt.comma) { - elts.push(null); + elts.push(null) } else { if (this.type === tt.ellipsis) - elts.push(this.parseSpread(refShorthandDefaultPos)); + elts.push(this.parseSpread(refShorthandDefaultPos)) else - elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos)); + elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos)) } } - return elts; -}; + return elts +} // Parse the next token as an identifier. If `liberal` is true (used // when parsing properties), it will also convert keywords into // identifiers. pp.parseIdent = function(liberal) { - var node = this.startNode(); - if (liberal && this.options.allowReserved == "never") liberal = false; + let node = this.startNode() + if (liberal && this.options.allowReserved == "never") liberal = false if (this.type === tt.name) { if (!liberal && ((!this.options.allowReserved && this.isReservedWord(this.value)) || (this.strict && reservedWords.strict(this.value)) && (this.options.ecmaVersion >= 6 || this.input.slice(this.start, this.end).indexOf("\\") == -1))) - this.raise(this.start, "The keyword '" + this.value + "' is reserved"); - node.name = this.value; + this.raise(this.start, "The keyword '" + this.value + "' is reserved") + node.name = this.value } else if (liberal && this.type.keyword) { - node.name = this.type.keyword; + node.name = this.type.keyword } else { - this.unexpected(); + this.unexpected() } - this.next(); - return this.finishNode(node, "Identifier"); -}; - -// Parses module export declaration. - -pp.parseExport = function(node) { - this.next(); - // export * from '...'; - if (this.eat(tt.star)) { - this.expectContextual("from"); - node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); - this.semicolon(); - return this.finishNode(node, "ExportAllDeclaration"); - } - if (this.eat(tt._default)) { // export default ...; - var expr = this.parseMaybeAssign(); - if (expr.id) { - switch (expr.type) { - case "FunctionExpression": expr.type = "FunctionDeclaration"; break; - case "ClassExpression": expr.type = "ClassDeclaration"; break; - } - } - node.declaration = expr; - this.semicolon(); - return this.finishNode(node, "ExportDefaultDeclaration"); - } - // export var|const|let|function|class ...; - if (this.type.keyword) { - node.declaration = this.parseStatement(true); - node.specifiers = []; - node.source = null; - } else { // export { x, y as z } [from '...']; - node.declaration = null; - node.specifiers = this.parseExportSpecifiers(); - if (this.eatContextual("from")) { - node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); - } else { - node.source = null; - } - this.semicolon(); - } - return this.finishNode(node, "ExportNamedDeclaration"); -}; - -// Parses a comma-separated list of module exports. - -pp.parseExportSpecifiers = function() { - var nodes = [], first = true; - // export { x, y as z } [from '...'] - this.expect(tt.braceL); - while (!this.eat(tt.braceR)) { - if (!first) { - this.expect(tt.comma); - if (this.afterTrailingComma(tt.braceR)) break; - } else first = false; - - var node = this.startNode(); - node.local = this.parseIdent(this.type === tt._default); - node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local; - nodes.push(this.finishNode(node, "ExportSpecifier")); - } - return nodes; -}; - -// Parses import declaration. - -pp.parseImport = function(node) { - this.next(); - // import '...'; - if (this.type === tt.string) { - node.specifiers = empty; - node.source = this.parseExprAtom(); - node.kind = ""; - } else { - node.specifiers = this.parseImportSpecifiers(); - this.expectContextual("from"); - node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); - } - this.semicolon(); - return this.finishNode(node, "ImportDeclaration"); -}; - -// Parses a comma-separated list of module imports. - -pp.parseImportSpecifiers = function() { - var nodes = [], first = true; - if (this.type === tt.name) { - // import defaultObj, { x, y as z } from '...' - var node = this.startNode(); - node.local = this.parseIdent(); - this.checkLVal(node.local, true); - nodes.push(this.finishNode(node, "ImportDefaultSpecifier")); - if (!this.eat(tt.comma)) return nodes; - } - if (this.type === tt.star) { - var node = this.startNode(); - this.next(); - this.expectContextual("as"); - node.local = this.parseIdent(); - this.checkLVal(node.local, true); - nodes.push(this.finishNode(node, "ImportNamespaceSpecifier")); - return nodes; - } - this.expect(tt.braceL); - while (!this.eat(tt.braceR)) { - if (!first) { - this.expect(tt.comma); - if (this.afterTrailingComma(tt.braceR)) break; - } else first = false; - - var node = this.startNode(); - node.imported = this.parseIdent(true); - node.local = this.eatContextual("as") ? this.parseIdent() : node.imported; - this.checkLVal(node.local, true); - nodes.push(this.finishNode(node, "ImportSpecifier")); - } - return nodes; -}; + this.next() + return this.finishNode(node, "Identifier") +} // Parses yield expression inside generator. pp.parseYield = function() { - var node = this.startNode(); - this.next(); + let node = this.startNode() + this.next() if (this.type == tt.semi || this.canInsertSemicolon() || (this.type != tt.star && !this.type.startsExpr)) { - node.delegate = false; - node.argument = null; + node.delegate = false + node.argument = null } else { - node.delegate = this.eat(tt.star); - node.argument = this.parseMaybeAssign(); + node.delegate = this.eat(tt.star) + node.argument = this.parseMaybeAssign() } - return this.finishNode(node, "YieldExpression"); -}; + return this.finishNode(node, "YieldExpression") +} // Parses array and generator comprehensions. pp.parseComprehension = function(node, isGenerator) { - node.blocks = []; + node.blocks = [] while (this.type === tt._for) { - var block = this.startNode(); - this.next(); - this.expect(tt.parenL); - block.left = this.parseBindingAtom(); - this.checkLVal(block.left, true); - this.expectContextual("of"); - block.right = this.parseExpression(); - this.expect(tt.parenR); - node.blocks.push(this.finishNode(block, "ComprehensionBlock")); + let block = this.startNode() + this.next() + this.expect(tt.parenL) + block.left = this.parseBindingAtom() + this.checkLVal(block.left, true) + this.expectContextual("of") + block.right = this.parseExpression() + this.expect(tt.parenR) + node.blocks.push(this.finishNode(block, "ComprehensionBlock")) } - node.filter = this.eat(tt._if) ? this.parseParenExpression() : null; - node.body = this.parseExpression(); - this.expect(isGenerator ? tt.parenR : tt.bracketR); - node.generator = isGenerator; - return this.finishNode(node, "ComprehensionExpression"); -}; + node.filter = this.eat(tt._if) ? this.parseParenExpression() : null + node.body = this.parseExpression() + this.expect(isGenerator ? tt.parenR : tt.bracketR) + node.generator = isGenerator + return this.finishNode(node, "ComprehensionExpression") +} diff --git a/src/identifier.js b/src/identifier.js index fd2794c27a..ad2fe03935 100644 --- a/src/identifier.js +++ b/src/identifier.js @@ -8,42 +8,42 @@ // 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) + words = words.split(" ") + let f = "", cats = [] + out: for (let i = 0; i < words.length; ++i) { + for (let j = 0; j < cats.length; ++j) if (cats[j][0].length == words[i].length) { - cats[j].push(words[i]); - continue out; + cats[j].push(words[i]) + continue out } - cats.push([words[i]]); + 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;"; + if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";" + f += "switch(str){" + for (let 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); + cats.sort((a, b) => b.length - a.length) + f += "switch(str.length){" + for (let i = 0; i < cats.length; ++i) { + let cat = cats[i] + f += "case " + cat[0].length + ":" + compareTo(cat) } - f += "}"; + f += "}" // Otherwise, simply generate a flat `switch` statement. } else { - compareTo(words); + compareTo(words) } - return new Function("str", f); + return new Function("str", f) } // Reserved word lists for various dialects of the language @@ -58,7 +58,7 @@ export const reservedWords = { // And the keywords -var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"; +var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this" export const keywords = { 5: makePredicate(ecma5AndLessKeywords), @@ -86,44 +86,44 @@ nonASCIIidentifierStartChars = nonASCIIidentifierChars = null // offset starts at 0x10000, and each pair of numbers represents an // offset to the next range, and then a size of the range. They were // generated by tools/generate-identifier-regex.js -var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541]; -var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239]; +var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541] +var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239] // This has a complexity linear to the value of the code. The // assumption is that looking up astral identifier characters is // rare. function isInAstralSet(code, set) { - 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; + let pos = 0x10000 + for (let i = 0; i < set.length; i += 2) { + pos += set[i] + if (pos > code) return false + pos += set[i + 1] + if (pos >= code) return true } } // Test whether a given character code starts an identifier. export function isIdentifierStart(code, astral) { - if (code < 65) return code === 36; - if (code < 91) return true; - if (code < 97) return code === 95; - if (code < 123) return true; - if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); - if (astral === false) return false; - return isInAstralSet(code, astralIdentifierStartCodes); + if (code < 65) return code === 36 + if (code < 91) return true + if (code < 97) return code === 95 + if (code < 123) return true + if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) + if (astral === false) return false + return isInAstralSet(code, astralIdentifierStartCodes) } // Test whether a given character is part of an identifier. export function isIdentifierChar(code, astral) { - if (code < 48) return code === 36; - if (code < 58) return true; - if (code < 65) return false; - if (code < 91) return true; - if (code < 97) return code === 95; - if (code < 123) return true; - if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); - if (astral === false) return false; - return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes); + if (code < 48) return code === 36 + if (code < 58) return true + if (code < 65) return false + if (code < 91) return true + if (code < 97) return code === 95 + if (code < 123) return true + if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) + if (astral === false) return false + return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes) } diff --git a/src/location.js b/src/location.js index 83bae82121..31d40a3501 100644 --- a/src/location.js +++ b/src/location.js @@ -51,17 +51,17 @@ const pp = Parser.prototype // message. pp.raise = function(pos, message) { - var loc = getLineInfo(this.input, pos); - message += " (" + loc.line + ":" + loc.column + ")"; - var err = new SyntaxError(message); - err.pos = pos; err.loc = loc; err.raisedAt = this.pos; - throw err; -}; + let loc = getLineInfo(this.input, pos) + message += " (" + loc.line + ":" + loc.column + ")" + let err = new SyntaxError(message) + err.pos = pos; err.loc = loc; err.raisedAt = this.pos + throw err +} pp.curPosition = function() { - return new Position(this.curLine, this.pos - this.lineStart); -}; + return new Position(this.curLine, this.pos - this.lineStart) +} pp.markPosition = function() { - return this.options.locations ? [this.start, this.startLoc] : this.start; -}; + return this.options.locations ? [this.start, this.startLoc] : this.start +} diff --git a/src/loose/expression.js b/src/loose/expression.js index ff38374b94..4269f2f234 100644 --- a/src/loose/expression.js +++ b/src/loose/expression.js @@ -5,7 +5,7 @@ import {tokTypes as tt} from ".." const lp = LooseParser.prototype lp.checkLVal = function(expr) { - if (!expr) return expr; + if (!expr) return expr switch (expr.type) { case "Identifier": case "MemberExpression": @@ -13,400 +13,401 @@ lp.checkLVal = function(expr) { case "ArrayPattern": case "RestElement": case "AssignmentPattern": - return expr; + return expr default: - return this.dummyIdent(); + return this.dummyIdent() } -}; +} lp.parseExpression = function(noIn) { - var start = this.storeCurrentPos(); - var expr = this.parseMaybeAssign(noIn); + let start = this.storeCurrentPos() + let expr = this.parseMaybeAssign(noIn) if (this.tok.type === tt.comma) { - var node = this.startNodeAt(start); - node.expressions = [expr]; - while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn)); - return this.finishNode(node, "SequenceExpression"); + let node = this.startNodeAt(start) + node.expressions = [expr] + while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn)) + return this.finishNode(node, "SequenceExpression") } - return expr; -}; + return expr +} lp.parseParenExpression = function() { - this.pushCx(); - this.expect(tt.parenL); - var val = this.parseExpression(); - this.popCx(); - this.expect(tt.parenR); - return val; -}; + this.pushCx() + this.expect(tt.parenL) + let val = this.parseExpression() + this.popCx() + this.expect(tt.parenR) + return val +} lp.parseMaybeAssign = function(noIn) { - var start = this.storeCurrentPos(); - var left = this.parseMaybeConditional(noIn); + let start = this.storeCurrentPos() + let left = this.parseMaybeConditional(noIn) if (this.tok.type.isAssign) { - var node = this.startNodeAt(start); - node.operator = this.tok.value; - node.left = this.tok.type === tt.eq ? this.toAssignable(left) : this.checkLVal(left); - this.next(); - node.right = this.parseMaybeAssign(noIn); - return this.finishNode(node, "AssignmentExpression"); + let node = this.startNodeAt(start) + node.operator = this.tok.value + node.left = this.tok.type === tt.eq ? this.toAssignable(left) : this.checkLVal(left) + this.next() + node.right = this.parseMaybeAssign(noIn) + return this.finishNode(node, "AssignmentExpression") } - return left; -}; + return left +} lp.parseMaybeConditional = function(noIn) { - var start = this.storeCurrentPos(); - var expr = this.parseExprOps(noIn); + let start = this.storeCurrentPos() + let expr = this.parseExprOps(noIn) if (this.eat(tt.question)) { - var node = this.startNodeAt(start); - node.test = expr; - node.consequent = this.parseMaybeAssign(); - node.alternate = this.expect(tt.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent(); - return this.finishNode(node, "ConditionalExpression"); + let node = this.startNodeAt(start) + node.test = expr + node.consequent = this.parseMaybeAssign() + node.alternate = this.expect(tt.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent() + return this.finishNode(node, "ConditionalExpression") } - return expr; -}; + return expr +} lp.parseExprOps = function(noIn) { - var start = this.storeCurrentPos(); - var indent = this.curIndent, line = this.curLineStart; - return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line); -}; + let start = this.storeCurrentPos() + let indent = this.curIndent, line = this.curLineStart + return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line) +} lp.parseExprOp = function(left, start, minPrec, noIn, indent, line) { - if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left; - var prec = this.tok.type.binop; + if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left + let prec = this.tok.type.binop if (prec != null && (!noIn || this.tok.type !== tt._in)) { if (prec > minPrec) { - var node = this.startNodeAt(start); - node.left = left; - node.operator = this.tok.value; - this.next(); + let node = this.startNodeAt(start) + node.left = left + node.operator = this.tok.value + this.next() if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) { - node.right = this.dummyIdent(); + node.right = this.dummyIdent() } else { - var rightStart = this.storeCurrentPos(); - node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line); + let rightStart = this.storeCurrentPos() + node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line) } - this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression"); - return this.parseExprOp(node, start, minPrec, noIn, indent, line); + this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression") + return this.parseExprOp(node, start, minPrec, noIn, indent, line) } } - return left; -}; + return left +} lp.parseMaybeUnary = function(noIn) { if (this.tok.type.prefix) { - var node = this.startNode(), update = this.tok.type === tt.incDec; - node.operator = this.tok.value; - node.prefix = true; - this.next(); - node.argument = this.parseMaybeUnary(noIn); - if (update) node.argument = this.checkLVal(node.argument); - return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); + let node = this.startNode(), update = this.tok.type === tt.incDec + node.operator = this.tok.value + node.prefix = true + this.next() + node.argument = this.parseMaybeUnary(noIn) + if (update) node.argument = this.checkLVal(node.argument) + return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression") } else if (this.tok.type === tt.ellipsis) { - var node = this.startNode(); - this.next(); - node.argument = this.parseMaybeUnary(noIn); - return this.finishNode(node, "SpreadElement"); + let node = this.startNode() + this.next() + node.argument = this.parseMaybeUnary(noIn) + return this.finishNode(node, "SpreadElement") } - var start = this.storeCurrentPos(); - var expr = this.parseExprSubscripts(); + let start = this.storeCurrentPos() + let expr = this.parseExprSubscripts() while (this.tok.type.postfix && !this.canInsertSemicolon()) { - var node = this.startNodeAt(start); - node.operator = this.tok.value; - node.prefix = false; - node.argument = this.checkLVal(expr); - this.next(); - expr = this.finishNode(node, "UpdateExpression"); + let node = this.startNodeAt(start) + node.operator = this.tok.value + node.prefix = false + node.argument = this.checkLVal(expr) + this.next() + expr = this.finishNode(node, "UpdateExpression") } - return expr; -}; + return expr +} lp.parseExprSubscripts = function() { - var start = this.storeCurrentPos(); - return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart); -}; + let start = this.storeCurrentPos() + return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart) +} lp.parseSubscripts = function(base, start, noCalls, startIndent, line) { for (;;) { if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) { if (this.tok.type == tt.dot && this.curIndent == startIndent) - --startIndent; + --startIndent else - return base; + return base } if (this.eat(tt.dot)) { - var node = this.startNodeAt(start); - node.object = base; + let node = this.startNodeAt(start) + node.object = base if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) - node.property = this.dummyIdent(); + node.property = this.dummyIdent() else - node.property = this.parsePropertyAccessor() || this.dummyIdent(); - node.computed = false; - base = this.finishNode(node, "MemberExpression"); + node.property = this.parsePropertyAccessor() || this.dummyIdent() + node.computed = false + base = this.finishNode(node, "MemberExpression") } else if (this.tok.type == tt.bracketL) { - this.pushCx(); - this.next(); - var node = this.startNodeAt(start); - node.object = base; - node.property = this.parseExpression(); - node.computed = true; - this.popCx(); - this.expect(tt.bracketR); - base = this.finishNode(node, "MemberExpression"); + this.pushCx() + this.next() + let node = this.startNodeAt(start) + node.object = base + node.property = this.parseExpression() + node.computed = true + this.popCx() + this.expect(tt.bracketR) + base = this.finishNode(node, "MemberExpression") } else if (!noCalls && this.tok.type == tt.parenL) { - this.pushCx(); - var node = this.startNodeAt(start); - node.callee = base; - node.arguments = this.parseExprList(tt.parenR); - base = this.finishNode(node, "CallExpression"); + this.pushCx() + let node = this.startNodeAt(start) + node.callee = base + node.arguments = this.parseExprList(tt.parenR) + base = this.finishNode(node, "CallExpression") } else if (this.tok.type == tt.backQuote) { - var node = this.startNodeAt(start); - node.tag = base; - node.quasi = this.parseTemplate(); - base = this.finishNode(node, "TaggedTemplateExpression"); + let node = this.startNodeAt(start) + node.tag = base + node.quasi = this.parseTemplate() + base = this.finishNode(node, "TaggedTemplateExpression") } else { - return base; + return base } } -}; +} lp.parseExprAtom = function() { + let node switch (this.tok.type) { case tt._this: case tt._super: - var type = this.tok.type === tt._this ? "ThisExpression" : "SuperExpression"; - var node = this.startNode(); - this.next(); - return this.finishNode(node, type); + let type = this.tok.type === tt._this ? "ThisExpression" : "SuperExpression" + node = this.startNode() + this.next() + return this.finishNode(node, type) case tt.name: - var start = this.storeCurrentPos(); - var id = this.parseIdent(); - return this.eat(tt.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id; + let start = this.storeCurrentPos() + let id = this.parseIdent() + return this.eat(tt.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id case tt.regexp: - var node = this.startNode(); - var val = this.tok.value; - node.regex = {pattern: val.pattern, flags: val.flags}; - node.value = val.value; - node.raw = this.input.slice(this.tok.start, this.tok.end); - this.next(); - return this.finishNode(node, "Literal"); + node = this.startNode() + let val = this.tok.value + node.regex = {pattern: val.pattern, flags: val.flags} + node.value = val.value + node.raw = this.input.slice(this.tok.start, this.tok.end) + this.next() + return this.finishNode(node, "Literal") case tt.num: case tt.string: - var node = this.startNode(); - node.value = this.tok.value; - node.raw = this.input.slice(this.tok.start, this.tok.end); - this.next(); - return this.finishNode(node, "Literal"); + node = this.startNode() + node.value = this.tok.value + node.raw = this.input.slice(this.tok.start, this.tok.end) + this.next() + return this.finishNode(node, "Literal") case tt._null: case tt._true: case tt._false: - var node = this.startNode(); - node.value = this.tok.type === tt._null ? null : this.tok.type === tt._true; - node.raw = this.tok.type.keyword; - this.next(); - return this.finishNode(node, "Literal"); + node = this.startNode() + node.value = this.tok.type === tt._null ? null : this.tok.type === tt._true + node.raw = this.tok.type.keyword + this.next() + return this.finishNode(node, "Literal") case tt.parenL: - var start = this.storeCurrentPos(); - this.next(); - var val = this.parseExpression(); - this.expect(tt.parenR); + let parenStart = this.storeCurrentPos() + this.next() + let inner = this.parseExpression() + this.expect(tt.parenR) if (this.eat(tt.arrow)) { - return this.parseArrowExpression(this.startNodeAt(start), val.expressions || (isDummy(val) ? [] : [val])); + return this.parseArrowExpression(this.startNodeAt(parenStart), inner.expressions || (isDummy(inner) ? [] : [inner])) } if (this.options.preserveParens) { - var par = this.startNodeAt(start); - par.expression = val; - val = this.finishNode(par, "ParenthesizedExpression"); + let par = this.startNodeAt(parenStart) + par.expression = inner + inner = this.finishNode(par, "ParenthesizedExpression") } - return val; + return inner case tt.bracketL: - var node = this.startNode(); - this.pushCx(); - node.elements = this.parseExprList(tt.bracketR, true); - return this.finishNode(node, "ArrayExpression"); + node = this.startNode() + this.pushCx() + node.elements = this.parseExprList(tt.bracketR, true) + return this.finishNode(node, "ArrayExpression") case tt.braceL: - return this.parseObj(); + return this.parseObj() case tt._class: - return this.parseClass(); + return this.parseClass() case tt._function: - var node = this.startNode(); - this.next(); - return this.parseFunction(node, false); + node = this.startNode() + this.next() + return this.parseFunction(node, false) case tt._new: - return this.parseNew(); + return this.parseNew() case tt._yield: - var node = this.startNode(); - this.next(); + node = this.startNode() + this.next() if (this.semicolon() || this.canInsertSemicolon() || (this.tok.type != tt.star && !this.tok.type.startsExpr)) { - node.delegate = false; - node.argument = null; + node.delegate = false + node.argument = null } else { - node.delegate = this.eat(tt.star); - node.argument = this.parseMaybeAssign(); + node.delegate = this.eat(tt.star) + node.argument = this.parseMaybeAssign() } - return this.finishNode(node, "YieldExpression"); + return this.finishNode(node, "YieldExpression") case tt.backQuote: - return this.parseTemplate(); + return this.parseTemplate() default: - return this.dummyIdent(); + return this.dummyIdent() } -}; +} lp.parseNew = function() { - var node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart; - this.next(); - var start = this.storeCurrentPos(); - node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line); + let node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart + this.next() + let start = this.storeCurrentPos() + node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line) if (this.tok.type == tt.parenL) { - this.pushCx(); - node.arguments = this.parseExprList(tt.parenR); + this.pushCx() + node.arguments = this.parseExprList(tt.parenR) } else { - node.arguments = []; + node.arguments = [] } - return this.finishNode(node, "NewExpression"); -}; + return this.finishNode(node, "NewExpression") +} lp.parseTemplateElement = function() { - var elem = this.startNode(); + let elem = this.startNode() elem.value = { raw: this.input.slice(this.tok.start, this.tok.end), cooked: this.tok.value - }; - this.next(); - elem.tail = this.tok.type === tt.backQuote; - return this.finishNode(elem, "TemplateElement"); -}; + } + this.next() + elem.tail = this.tok.type === tt.backQuote + return this.finishNode(elem, "TemplateElement") +} lp.parseTemplate = function() { - var node = this.startNode(); - this.next(); - node.expressions = []; - var curElt = this.parseTemplateElement(); - node.quasis = [curElt]; + let node = this.startNode() + this.next() + node.expressions = [] + let curElt = this.parseTemplateElement() + node.quasis = [curElt] while (!curElt.tail) { - this.next(); - node.expressions.push(this.parseExpression()); + this.next() + node.expressions.push(this.parseExpression()) if (this.expect(tt.braceR)) { - curElt = this.parseTemplateElement(); + curElt = this.parseTemplateElement() } else { - curElt = this.startNode(); - curElt.value = {cooked: '', raw: ''}; - curElt.tail = true; + curElt = this.startNode() + curElt.value = {cooked: '', raw: ''} + curElt.tail = true } - node.quasis.push(curElt); + node.quasis.push(curElt) } - this.expect(tt.backQuote); - return this.finishNode(node, "TemplateLiteral"); -}; + this.expect(tt.backQuote) + return this.finishNode(node, "TemplateLiteral") +} lp.parseObj = function() { - var node = this.startNode(); - node.properties = []; - this.pushCx(); - var indent = this.curIndent + 1, line = this.curLineStart; - this.eat(tt.braceL); - if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart; } + let node = this.startNode() + node.properties = [] + this.pushCx() + let indent = this.curIndent + 1, line = this.curLineStart + this.eat(tt.braceL) + if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart } while (!this.closes(tt.braceR, indent, line)) { - var prop = this.startNode(), isGenerator, start; + let prop = this.startNode(), isGenerator, start if (this.options.ecmaVersion >= 6) { - start = this.storeCurrentPos(); - prop.method = false; - prop.shorthand = false; - isGenerator = this.eat(tt.star); + start = this.storeCurrentPos() + prop.method = false + prop.shorthand = false + isGenerator = this.eat(tt.star) } - this.parsePropertyName(prop); - if (isDummy(prop.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue; } + this.parsePropertyName(prop) + if (isDummy(prop.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue } if (this.eat(tt.colon)) { - prop.kind = "init"; - prop.value = this.parseMaybeAssign(); + prop.kind = "init" + prop.value = this.parseMaybeAssign() } else if (this.options.ecmaVersion >= 6 && (this.tok.type === tt.parenL || this.tok.type === tt.braceL)) { - prop.kind = "init"; - prop.method = true; - prop.value = this.parseMethod(isGenerator); + prop.kind = "init" + prop.method = true + prop.value = this.parseMethod(isGenerator) } else if (this.options.ecmaVersion >= 5 && prop.key.type === "Identifier" && !prop.computed && (prop.key.name === "get" || prop.key.name === "set") && (this.tok.type != tt.comma && this.tok.type != tt.braceR)) { - prop.kind = prop.key.name; - this.parsePropertyName(prop); - prop.value = this.parseMethod(false); + prop.kind = prop.key.name + this.parsePropertyName(prop) + prop.value = this.parseMethod(false) } else { - prop.kind = "init"; + prop.kind = "init" if (this.options.ecmaVersion >= 6) { if (this.eat(tt.eq)) { - var assign = this.startNodeAt(start); - assign.operator = "="; - assign.left = prop.key; - assign.right = this.parseMaybeAssign(); - prop.value = this.finishNode(assign, "AssignmentExpression"); + let assign = this.startNodeAt(start) + assign.operator = "=" + assign.left = prop.key + assign.right = this.parseMaybeAssign() + prop.value = this.finishNode(assign, "AssignmentExpression") } else { - prop.value = prop.key; + prop.value = prop.key } } else { - prop.value = this.dummyIdent(); + prop.value = this.dummyIdent() } - prop.shorthand = true; + prop.shorthand = true } - node.properties.push(this.finishNode(prop, "Property")); - this.eat(tt.comma); + node.properties.push(this.finishNode(prop, "Property")) + this.eat(tt.comma) } - this.popCx(); + this.popCx() if (!this.eat(tt.braceR)) { // If there is no closing brace, make the node span to the start // of the next token (this is useful for Tern) - this.last.end = this.tok.start; - if (this.options.locations) this.last.loc.end = this.tok.loc.start; + this.last.end = this.tok.start + if (this.options.locations) this.last.loc.end = this.tok.loc.start } - return this.finishNode(node, "ObjectExpression"); -}; + return this.finishNode(node, "ObjectExpression") +} lp.parsePropertyName = function(prop) { if (this.options.ecmaVersion >= 6) { if (this.eat(tt.bracketL)) { - prop.computed = true; - prop.key = this.parseExpression(); - this.expect(tt.bracketR); - return; + prop.computed = true + prop.key = this.parseExpression() + this.expect(tt.bracketR) + return } else { - prop.computed = false; + prop.computed = false } } - var key = (this.tok.type === tt.num || this.tok.type === tt.string) ? this.parseExprAtom() : this.parseIdent(); - prop.key = key || this.dummyIdent(); -}; + let key = (this.tok.type === tt.num || this.tok.type === tt.string) ? this.parseExprAtom() : this.parseIdent() + prop.key = key || this.dummyIdent() +} lp.parsePropertyAccessor = function() { - if (this.tok.type === tt.name || this.tok.type.keyword) return this.parseIdent(); -}; + if (this.tok.type === tt.name || this.tok.type.keyword) return this.parseIdent() +} lp.parseIdent = function() { - var node = this.startNode(); - node.name = this.tok.type === tt.name ? this.tok.value : this.tok.type.keyword; - this.next(); - return this.finishNode(node, "Identifier"); -}; + let node = this.startNode() + node.name = this.tok.type === tt.name ? this.tok.value : this.tok.type.keyword + this.next() + return this.finishNode(node, "Identifier") +} lp.initFunction = function(node) { - node.id = null; - node.params = []; + node.id = null + node.params = [] if (this.options.ecmaVersion >= 6) { - node.generator = false; - node.expression = false; + node.generator = false + node.expression = false } -}; +} // Convert existing expression atom to assignable pattern // if possible. @@ -415,189 +416,83 @@ lp.toAssignable = function(node) { if (this.options.ecmaVersion >= 6 && node) { switch (node.type) { case "ObjectExpression": - node.type = "ObjectPattern"; - var props = node.properties; - for (var i = 0; i < props.length; i++) - this.toAssignable(props[i].value); - break; + node.type = "ObjectPattern" + let props = node.properties + for (let i = 0; i < props.length; i++) + this.toAssignable(props[i].value) + break case "ArrayExpression": - node.type = "ArrayPattern"; - this.toAssignableList(node.elements); - break; + node.type = "ArrayPattern" + this.toAssignableList(node.elements) + break case "SpreadElement": - node.type = "RestElement"; - node.argument = this.toAssignable(node.argument); - break; + node.type = "RestElement" + node.argument = this.toAssignable(node.argument) + break case "AssignmentExpression": - node.type = "AssignmentPattern"; - break; + node.type = "AssignmentPattern" + break } } - return this.checkLVal(node); -}; + return this.checkLVal(node) +} lp.toAssignableList = function(exprList) { - for (var i = 0; i < exprList.length; i++) - this.toAssignable(exprList[i]); - return exprList; -}; + for (let i = 0; i < exprList.length; i++) + this.toAssignable(exprList[i]) + return exprList +} lp.parseFunctionParams = function(params) { - this.pushCx(); - params = this.parseExprList(tt.parenR); - return this.toAssignableList(params); -}; + this.pushCx() + params = this.parseExprList(tt.parenR) + return this.toAssignableList(params) +} lp.parseMethod = function(isGenerator) { - var node = this.startNode(); - this.initFunction(node); - node.params = this.parseFunctionParams(); - node.generator = isGenerator || false; - node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== tt.braceL; - node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock(); - return this.finishNode(node, "FunctionExpression"); -}; + let node = this.startNode() + this.initFunction(node) + node.params = this.parseFunctionParams() + node.generator = isGenerator || false + node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== tt.braceL + node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock() + return this.finishNode(node, "FunctionExpression") +} lp.parseArrowExpression = function(node, params) { - this.initFunction(node); - node.params = this.toAssignableList(params); - node.expression = this.tok.type !== tt.braceL; - node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock(); - return this.finishNode(node, "ArrowFunctionExpression"); -}; - -lp.parseExport = function() { - var node = this.startNode(); - this.next(); - if (this.eat(tt.star)) { - node.source = this.eatContextual("from") ? this.parseExprAtom() : null; - return this.finishNode(node, "ExportAllDeclaration"); - } - if (this.eat(tt._default)) { - var expr = this.parseMaybeAssign(); - if (expr.id) { - switch (expr.type) { - case "FunctionExpression": expr.type = "FunctionDeclaration"; break; - case "ClassExpression": expr.type = "ClassDeclaration"; break; - } - } - node.declaration = expr; - this.semicolon(); - return this.finishNode(node, "ExportDefaultDeclaration"); - } - if (this.tok.type.keyword) { - node.declaration = this.parseStatement(); - node.specifiers = []; - node.source = null; - } else { - node.declaration = null; - node.specifiers = this.parseExportSpecifierList(); - node.source = this.eatContextual("from") ? this.parseExprAtom() : null; - this.semicolon(); - } - return this.finishNode(node, "ExportNamedDeclaration"); -}; - -lp.parseImport = function() { - var node = this.startNode(); - this.next(); - if (this.tok.type === tt.string) { - node.specifiers = []; - node.source = this.parseExprAtom(); - node.kind = ''; - } else { - if (this.tok.type === tt.name && this.tok.value !== "from") { - var elt = this.startNode(); - elt.local = this.parseIdent(); - this.finishNode(elt, "ImportDefaultSpecifier"); - this.eat(tt.comma); - } - node.specifiers = this.parseImportSpecifierList(); - node.source = this.eatContextual("from") ? this.parseExprAtom() : null; - if (elt) node.specifiers.unshift(elt); - } - this.semicolon(); - return this.finishNode(node, "ImportDeclaration"); -}; - -lp.parseImportSpecifierList = function() { - var elts = []; - if (this.tok.type === tt.star) { - var elt = this.startNode(); - this.next(); - if (this.eatContextual("as")) elt.local = this.parseIdent(); - elts.push(this.finishNode(elt, "ImportNamespaceSpecifier")); - } else { - var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart; - this.pushCx(); - this.eat(tt.braceL); - if (this.curLineStart > continuedLine) continuedLine = this.curLineStart; - while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) { - var elt = this.startNode(); - if (this.eat(tt.star)) { - if (this.eatContextual("as")) elt.local = this.parseIdent(); - this.finishNode(elt, "ImportNamespaceSpecifier"); - } else { - if (this.isContextual("from")) break; - elt.imported = this.parseIdent(); - elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported; - this.finishNode(elt, "ImportSpecifier"); - } - elts.push(elt); - this.eat(tt.comma); - } - this.eat(tt.braceR); - this.popCx(); - } - return elts; -}; - -lp.parseExportSpecifierList = function() { - var elts = []; - var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart; - this.pushCx(); - this.eat(tt.braceL); - if (this.curLineStart > continuedLine) continuedLine = this.curLineStart; - while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) { - if (this.isContextual("from")) break; - var elt = this.startNode(); - elt.local = this.parseIdent(); - elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local; - this.finishNode(elt, "ExportSpecifier"); - elts.push(elt); - this.eat(tt.comma); - } - this.eat(tt.braceR); - this.popCx(); - return elts; -}; + this.initFunction(node) + node.params = this.toAssignableList(params) + node.expression = this.tok.type !== tt.braceL + node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock() + return this.finishNode(node, "ArrowFunctionExpression") +} lp.parseExprList = function(close, allowEmpty) { - var indent = this.curIndent, line = this.curLineStart, elts = []; + let indent = this.curIndent, line = this.curLineStart, elts = [] this.next(); // Opening bracket while (!this.closes(close, indent + 1, line)) { if (this.eat(tt.comma)) { - elts.push(allowEmpty ? null : this.dummyIdent()); - continue; + elts.push(allowEmpty ? null : this.dummyIdent()) + continue } - var elt = this.parseMaybeAssign(); + let elt = this.parseMaybeAssign() if (isDummy(elt)) { - if (this.closes(close, indent, line)) break; - this.next(); + if (this.closes(close, indent, line)) break + this.next() } else { - elts.push(elt); + elts.push(elt) } - this.eat(tt.comma); + this.eat(tt.comma) } - this.popCx(); + this.popCx() if (!this.eat(close)) { // If there is no closing brace, make the node span to the start // of the next token (this is useful for Tern) - this.last.end = this.tok.start; - if (this.options.locations) this.last.loc.end = this.tok.loc.start; + this.last.end = this.tok.start + if (this.options.locations) this.last.loc.end = this.tok.loc.start } - return elts; -}; + return elts +} diff --git a/src/loose/parseutil.js b/src/loose/parseutil.js index 5f656ec282..3e2c80418f 100644 --- a/src/loose/parseutil.js +++ b/src/loose/parseutil.js @@ -4,123 +4,123 @@ import {Node, SourceLocation, lineBreak, isNewLine, tokTypes as tt} from ".." const lp = LooseParser.prototype lp.startNode = function() { - var node = new Node; - node.start = this.tok.start; + let node = new Node + node.start = this.tok.start if (this.options.locations) - node.loc = new SourceLocation(this.toks, this.tok.loc.start); + node.loc = new SourceLocation(this.toks, this.tok.loc.start) if (this.options.directSourceFile) - node.sourceFile = this.options.directSourceFile; + node.sourceFile = this.options.directSourceFile if (this.options.ranges) - node.range = [this.tok.start, 0]; - return node; -}; + node.range = [this.tok.start, 0] + return node +} lp.storeCurrentPos = function() { - return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start; -}; + return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start +} lp.startNodeAt = function(pos) { - var node = new Node; + let node = new Node if (this.options.locations) { - node.start = pos[0]; - node.loc = new SourceLocation(this.toks, pos[1]); - pos = pos[0]; + node.start = pos[0] + node.loc = new SourceLocation(this.toks, pos[1]) + pos = pos[0] } else { - node.start = pos; + node.start = pos } if (this.options.directSourceFile) - node.sourceFile = this.options.directSourceFile; + node.sourceFile = this.options.directSourceFile if (this.options.ranges) - node.range = [pos, 0]; - return node; -}; + node.range = [pos, 0] + return node +} lp.finishNode = function(node, type) { - node.type = type; - node.end = this.last.end; + node.type = type + node.end = this.last.end if (this.options.locations) - node.loc.end = this.last.loc.end; + node.loc.end = this.last.loc.end if (this.options.ranges) - node.range[1] = this.last.end; - return node; -}; + node.range[1] = this.last.end + return node +} lp.dummyIdent = function() { - var dummy = this.startNode(); - dummy.name = "✖"; - return this.finishNode(dummy, "Identifier"); -}; + let dummy = this.startNode() + dummy.name = "✖" + return this.finishNode(dummy, "Identifier") +} -export function isDummy(node) { return node.name == "✖"; } +export function isDummy(node) { return node.name == "✖" } lp.eat = function(type) { if (this.tok.type === type) { - this.next(); - return true; + this.next() + return true } else { - return false; + return false } -}; +} lp.isContextual = function(name) { - return this.tok.type === tt.name && this.tok.value === name; -}; + return this.tok.type === tt.name && this.tok.value === name +} lp.eatContextual = function(name) { - return this.tok.value === name && this.eat(tt.name); -}; + return this.tok.value === name && this.eat(tt.name) +} lp.canInsertSemicolon = function() { return this.tok.type === tt.eof || this.tok.type === tt.braceR || - lineBreak.test(this.input.slice(this.last.end, this.tok.start)); -}; + lineBreak.test(this.input.slice(this.last.end, this.tok.start)) +} lp.semicolon = function() { - return this.eat(tt.semi); -}; + return this.eat(tt.semi) +} lp.expect = function(type) { - if (this.eat(type)) return true; - for (var i = 1; i <= 2; i++) { + if (this.eat(type)) return true + for (let i = 1; i <= 2; i++) { if (this.lookAhead(i).type == type) { - for (var j = 0; j < i; j++) this.next(); - return true; + for (let j = 0; j < i; j++) this.next() + return true } } -}; +} lp.pushCx = function() { - this.context.push(this.curIndent); -}; + this.context.push(this.curIndent) +} lp.popCx = function() { - this.curIndent = this.context.pop(); -}; + this.curIndent = this.context.pop() +} lp.lineEnd = function(pos) { - while (pos < this.input.length && !isNewLine(this.input.charCodeAt(pos))) ++pos; - return pos; -}; + while (pos < this.input.length && !isNewLine(this.input.charCodeAt(pos))) ++pos + return pos +} lp.indentationAfter = function(pos) { - for (var count = 0;; ++pos) { - var ch = this.input.charCodeAt(pos); - if (ch === 32) ++count; - else if (ch === 9) count += this.options.tabSize; - else return count; + for (let count = 0;; ++pos) { + let ch = this.input.charCodeAt(pos) + if (ch === 32) ++count + else if (ch === 9) count += this.options.tabSize + else return count } -}; +} lp.closes = function(closeTok, indent, line, blockHeuristic) { - if (this.tok.type === closeTok || this.tok.type === tt.eof) return true; + if (this.tok.type === closeTok || this.tok.type === tt.eof) return true return line != this.curLineStart && this.curIndent < indent && this.tokenStartsLine() && (!blockHeuristic || this.nextLineStart >= this.input.length || - this.indentationAfter(this.nextLineStart) < indent); -}; + this.indentationAfter(this.nextLineStart) < indent) +} lp.tokenStartsLine = function() { - for (var p = this.tok.start - 1; p >= this.curLineStart; --p) { - var ch = this.input.charCodeAt(p); - if (ch !== 9 && ch !== 32) return false; + for (let p = this.tok.start - 1; p >= this.curLineStart; --p) { + let ch = this.input.charCodeAt(p) + if (ch !== 9 && ch !== 32) return false } - return true; -}; + return true +} diff --git a/src/loose/state.js b/src/loose/state.js index 3854da2261..fbf1227ab3 100644 --- a/src/loose/state.js +++ b/src/loose/state.js @@ -2,16 +2,16 @@ import {tokenizer, SourceLocation, tokTypes as tt} from ".." export function LooseParser(input, options) { this.toks = tokenizer(input, options) - this.options = this.toks.options; - this.input = this.toks.input; - this.tok = this.last = {type: tt.eof, start: 0, end: 0}; + this.options = this.toks.options + this.input = this.toks.input + this.tok = this.last = {type: tt.eof, start: 0, end: 0} if (this.options.locations) { - var here = this.toks.curPosition(); - this.tok.loc = new SourceLocation(this.toks, here, here); + let here = this.toks.curPosition() + this.tok.loc = new SourceLocation(this.toks, here, here) } this.ahead = []; // Tokens ahead this.context = []; // Indentation contexted - this.curIndent = 0; - this.curLineStart = 0; - this.nextLineStart = this.lineEnd(this.curLineStart) + 1; + this.curIndent = 0 + this.curLineStart = 0 + this.nextLineStart = this.lineEnd(this.curLineStart) + 1 } diff --git a/src/loose/statement.js b/src/loose/statement.js index 714d104b08..eddfe4f05e 100644 --- a/src/loose/statement.js +++ b/src/loose/statement.js @@ -5,307 +5,415 @@ import {getLineInfo, tokTypes as tt} from ".." const lp = LooseParser.prototype lp.parseTopLevel = function() { - var node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0); - node.body = []; - while (this.tok.type !== tt.eof) node.body.push(this.parseStatement()); - this.last = this.tok; + let node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0) + node.body = [] + while (this.tok.type !== tt.eof) node.body.push(this.parseStatement()) + this.last = this.tok if (this.options.ecmaVersion >= 6) { - node.sourceType = this.options.sourceType; + node.sourceType = this.options.sourceType } - return this.finishNode(node, "Program"); -}; + return this.finishNode(node, "Program") +} lp.parseStatement = function() { - var starttype = this.tok.type, node = this.startNode(); + let starttype = this.tok.type, node = this.startNode() switch (starttype) { case tt._break: case tt._continue: - this.next(); - var isBreak = starttype === tt._break; + this.next() + let isBreak = starttype === tt._break if (this.semicolon() || this.canInsertSemicolon()) { - node.label = null; + node.label = null } else { - node.label = this.tok.type === tt.name ? this.parseIdent() : null; - this.semicolon(); + node.label = this.tok.type === tt.name ? this.parseIdent() : null + this.semicolon() } - return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); + return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement") case tt._debugger: - this.next(); - this.semicolon(); - return this.finishNode(node, "DebuggerStatement"); + this.next() + this.semicolon() + return this.finishNode(node, "DebuggerStatement") case tt._do: - this.next(); - node.body = this.parseStatement(); - node.test = this.eat(tt._while) ? this.parseParenExpression() : this.dummyIdent(); - this.semicolon(); - return this.finishNode(node, "DoWhileStatement"); + this.next() + node.body = this.parseStatement() + node.test = this.eat(tt._while) ? this.parseParenExpression() : this.dummyIdent() + this.semicolon() + return this.finishNode(node, "DoWhileStatement") case tt._for: - this.next(); - this.pushCx(); - this.expect(tt.parenL); - if (this.tok.type === tt.semi) return this.parseFor(node, null); + this.next() + this.pushCx() + this.expect(tt.parenL) + if (this.tok.type === tt.semi) return this.parseFor(node, null) if (this.tok.type === tt._var || this.tok.type === tt._let || this.tok.type === tt._const) { - var init = this.parseVar(true); + let init = this.parseVar(true) if (init.declarations.length === 1 && (this.tok.type === tt._in || this.isContextual("of"))) { - return this.parseForIn(node, init); + return this.parseForIn(node, init) } - return this.parseFor(node, init); + return this.parseFor(node, init) } - var init = this.parseExpression(true); + let init = this.parseExpression(true) if (this.tok.type === tt._in || this.isContextual("of")) - return this.parseForIn(node, this.toAssignable(init)); - return this.parseFor(node, init); + return this.parseForIn(node, this.toAssignable(init)) + return this.parseFor(node, init) case tt._function: - this.next(); - return this.parseFunction(node, true); + this.next() + return this.parseFunction(node, true) case tt._if: - this.next(); - node.test = this.parseParenExpression(); - node.consequent = this.parseStatement(); - node.alternate = this.eat(tt._else) ? this.parseStatement() : null; - return this.finishNode(node, "IfStatement"); + this.next() + node.test = this.parseParenExpression() + node.consequent = this.parseStatement() + node.alternate = this.eat(tt._else) ? this.parseStatement() : null + return this.finishNode(node, "IfStatement") case tt._return: - this.next(); - if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null; - else { node.argument = this.parseExpression(); this.semicolon(); } - return this.finishNode(node, "ReturnStatement"); + this.next() + if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null + else { node.argument = this.parseExpression(); this.semicolon() } + return this.finishNode(node, "ReturnStatement") case tt._switch: - var blockIndent = this.curIndent, line = this.curLineStart; - this.next(); - node.discriminant = this.parseParenExpression(); - node.cases = []; - this.pushCx(); - this.expect(tt.braceL); + let blockIndent = this.curIndent, line = this.curLineStart + this.next() + node.discriminant = this.parseParenExpression() + node.cases = [] + this.pushCx() + this.expect(tt.braceL) - for (var cur; !this.closes(tt.braceR, blockIndent, line, true);) { + let cur + while (!this.closes(tt.braceR, blockIndent, line, true)) { if (this.tok.type === tt._case || this.tok.type === tt._default) { - var isCase = this.tok.type === tt._case; - if (cur) this.finishNode(cur, "SwitchCase"); - node.cases.push(cur = this.startNode()); - cur.consequent = []; - this.next(); - if (isCase) cur.test = this.parseExpression(); - else cur.test = null; - this.expect(tt.colon); + let isCase = this.tok.type === tt._case + if (cur) this.finishNode(cur, "SwitchCase") + node.cases.push(cur = this.startNode()) + cur.consequent = [] + this.next() + if (isCase) cur.test = this.parseExpression() + else cur.test = null + this.expect(tt.colon) } else { if (!cur) { - node.cases.push(cur = this.startNode()); - cur.consequent = []; - cur.test = null; + node.cases.push(cur = this.startNode()) + cur.consequent = [] + cur.test = null } - cur.consequent.push(this.parseStatement()); + cur.consequent.push(this.parseStatement()) } } - if (cur) this.finishNode(cur, "SwitchCase"); - this.popCx(); - this.eat(tt.braceR); - return this.finishNode(node, "SwitchStatement"); + if (cur) this.finishNode(cur, "SwitchCase") + this.popCx() + this.eat(tt.braceR) + return this.finishNode(node, "SwitchStatement") case tt._throw: - this.next(); - node.argument = this.parseExpression(); - this.semicolon(); - return this.finishNode(node, "ThrowStatement"); + this.next() + node.argument = this.parseExpression() + this.semicolon() + return this.finishNode(node, "ThrowStatement") case tt._try: - this.next(); - node.block = this.parseBlock(); - node.handler = null; + this.next() + node.block = this.parseBlock() + node.handler = null if (this.tok.type === tt._catch) { - var clause = this.startNode(); - this.next(); - this.expect(tt.parenL); - clause.param = this.toAssignable(this.parseExprAtom()); - this.expect(tt.parenR); - clause.guard = null; - clause.body = this.parseBlock(); - node.handler = this.finishNode(clause, "CatchClause"); + let clause = this.startNode() + this.next() + this.expect(tt.parenL) + clause.param = this.toAssignable(this.parseExprAtom()) + this.expect(tt.parenR) + clause.guard = null + clause.body = this.parseBlock() + node.handler = this.finishNode(clause, "CatchClause") } - node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null; - if (!node.handler && !node.finalizer) return node.block; - return this.finishNode(node, "TryStatement"); + node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null + if (!node.handler && !node.finalizer) return node.block + return this.finishNode(node, "TryStatement") case tt._var: case tt._let: case tt._const: - return this.parseVar(); + return this.parseVar() case tt._while: - this.next(); - node.test = this.parseParenExpression(); - node.body = this.parseStatement(); - return this.finishNode(node, "WhileStatement"); + this.next() + node.test = this.parseParenExpression() + node.body = this.parseStatement() + return this.finishNode(node, "WhileStatement") case tt._with: - this.next(); - node.object = this.parseParenExpression(); - node.body = this.parseStatement(); - return this.finishNode(node, "WithStatement"); + this.next() + node.object = this.parseParenExpression() + node.body = this.parseStatement() + return this.finishNode(node, "WithStatement") case tt.braceL: - return this.parseBlock(); + return this.parseBlock() case tt.semi: - this.next(); - return this.finishNode(node, "EmptyStatement"); + this.next() + return this.finishNode(node, "EmptyStatement") case tt._class: - return this.parseClass(true); + return this.parseClass(true) case tt._import: - return this.parseImport(); + return this.parseImport() case tt._export: - return this.parseExport(); + return this.parseExport() default: - var expr = this.parseExpression(); + let expr = this.parseExpression() if (isDummy(expr)) { - this.next(); - if (this.tok.type === tt.eof) return this.finishNode(node, "EmptyStatement"); - return this.parseStatement(); + this.next() + if (this.tok.type === tt.eof) return this.finishNode(node, "EmptyStatement") + return this.parseStatement() } else if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) { - node.body = this.parseStatement(); - node.label = expr; - return this.finishNode(node, "LabeledStatement"); + node.body = this.parseStatement() + node.label = expr + return this.finishNode(node, "LabeledStatement") } else { - node.expression = expr; - this.semicolon(); - return this.finishNode(node, "ExpressionStatement"); + node.expression = expr + this.semicolon() + return this.finishNode(node, "ExpressionStatement") } } -}; +} lp.parseBlock = function() { - var node = this.startNode(); - this.pushCx(); - this.expect(tt.braceL); - var blockIndent = this.curIndent, line = this.curLineStart; - node.body = []; + let node = this.startNode() + this.pushCx() + this.expect(tt.braceL) + let blockIndent = this.curIndent, line = this.curLineStart + node.body = [] while (!this.closes(tt.braceR, blockIndent, line, true)) - node.body.push(this.parseStatement()); - this.popCx(); - this.eat(tt.braceR); - return this.finishNode(node, "BlockStatement"); -}; + node.body.push(this.parseStatement()) + this.popCx() + this.eat(tt.braceR) + return this.finishNode(node, "BlockStatement") +} lp.parseFor = function(node, init) { - node.init = init; - node.test = node.update = null; - if (this.eat(tt.semi) && this.tok.type !== tt.semi) node.test = this.parseExpression(); - if (this.eat(tt.semi) && this.tok.type !== tt.parenR) node.update = this.parseExpression(); - this.popCx(); - this.expect(tt.parenR); - node.body = this.parseStatement(); - return this.finishNode(node, "ForStatement"); -}; + node.init = init + node.test = node.update = null + if (this.eat(tt.semi) && this.tok.type !== tt.semi) node.test = this.parseExpression() + if (this.eat(tt.semi) && this.tok.type !== tt.parenR) node.update = this.parseExpression() + this.popCx() + this.expect(tt.parenR) + node.body = this.parseStatement() + return this.finishNode(node, "ForStatement") +} lp.parseForIn = function(node, init) { - var type = this.tok.type === tt._in ? "ForInStatement" : "ForOfStatement"; - this.next(); - node.left = init; - node.right = this.parseExpression(); - this.popCx(); - this.expect(tt.parenR); - node.body = this.parseStatement(); - return this.finishNode(node, type); -}; + let type = this.tok.type === tt._in ? "ForInStatement" : "ForOfStatement" + this.next() + node.left = init + node.right = this.parseExpression() + this.popCx() + this.expect(tt.parenR) + node.body = this.parseStatement() + return this.finishNode(node, type) +} lp.parseVar = function(noIn) { - var node = this.startNode(); - node.kind = this.tok.type.keyword; - this.next(); - node.declarations = []; + let node = this.startNode() + node.kind = this.tok.type.keyword + this.next() + node.declarations = [] do { - var decl = this.startNode(); - decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom()) : this.parseIdent(); - decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : null; - node.declarations.push(this.finishNode(decl, "VariableDeclarator")); - } while (this.eat(tt.comma)); + let decl = this.startNode() + decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom()) : this.parseIdent() + decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : null + node.declarations.push(this.finishNode(decl, "VariableDeclarator")) + } while (this.eat(tt.comma)) if (!node.declarations.length) { - var decl = this.startNode(); - decl.id = this.dummyIdent(); - node.declarations.push(this.finishNode(decl, "VariableDeclarator")); + let decl = this.startNode() + decl.id = this.dummyIdent() + node.declarations.push(this.finishNode(decl, "VariableDeclarator")) } - if (!noIn) this.semicolon(); - return this.finishNode(node, "VariableDeclaration"); -}; + if (!noIn) this.semicolon() + return this.finishNode(node, "VariableDeclaration") +} lp.parseClass = function(isStatement) { - var node = this.startNode(); - this.next(); - if (this.tok.type === tt.name) node.id = this.parseIdent(); - else if (isStatement) node.id = this.dummyIdent(); - else node.id = null; - node.superClass = this.eat(tt._extends) ? this.parseExpression() : null; - node.body = this.startNode(); - node.body.body = []; - this.pushCx(); - var indent = this.curIndent + 1, line = this.curLineStart; - this.eat(tt.braceL); - if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart; } + let node = this.startNode() + this.next() + if (this.tok.type === tt.name) node.id = this.parseIdent() + else if (isStatement) node.id = this.dummyIdent() + else node.id = null + node.superClass = this.eat(tt._extends) ? this.parseExpression() : null + node.body = this.startNode() + node.body.body = [] + this.pushCx() + let indent = this.curIndent + 1, line = this.curLineStart + this.eat(tt.braceL) + if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart } while (!this.closes(tt.braceR, indent, line)) { - if (this.semicolon()) continue; - var method = this.startNode(), isGenerator, start; + if (this.semicolon()) continue + let method = this.startNode(), isGenerator, start if (this.options.ecmaVersion >= 6) { - method['static'] = false; - isGenerator = this.eat(tt.star); + method['static'] = false + isGenerator = this.eat(tt.star) } - this.parsePropertyName(method); - if (isDummy(method.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue; } + this.parsePropertyName(method) + if (isDummy(method.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue } if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" && (this.tok.type != tt.parenL && this.tok.type != tt.braceL)) { - method['static'] = true; - isGenerator = this.eat(tt.star); - this.parsePropertyName(method); + method['static'] = true + isGenerator = this.eat(tt.star) + this.parsePropertyName(method) } else { - method['static'] = false; + method['static'] = false } if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" && !method.computed && (method.key.name === "get" || method.key.name === "set") && this.tok.type !== tt.parenL && this.tok.type !== tt.braceL) { - method.kind = method.key.name; - this.parsePropertyName(method); - method.value = this.parseMethod(false); + method.kind = method.key.name + this.parsePropertyName(method) + method.value = this.parseMethod(false) } else { if (!method.computed && !method['static'] && !isGenerator && ( method.key.type === "Identifier" && method.key.name === "constructor" || method.key.type === "Literal" && method.key.value === "constructor")) { - method.kind = "constructor"; + method.kind = "constructor" } else { - method.kind = "method"; + method.kind = "method" } - method.value = this.parseMethod(isGenerator); + method.value = this.parseMethod(isGenerator) } - node.body.body.push(this.finishNode(method, "MethodDefinition")); + node.body.body.push(this.finishNode(method, "MethodDefinition")) } - this.popCx(); + this.popCx() if (!this.eat(tt.braceR)) { // If there is no closing brace, make the node span to the start // of the next token (this is useful for Tern) - this.last.end = this.tok.start; - if (this.options.locations) this.last.loc.end = this.tok.loc.start; + this.last.end = this.tok.start + if (this.options.locations) this.last.loc.end = this.tok.loc.start } - this.semicolon(); - this.finishNode(node.body, "ClassBody"); - return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); -}; + this.semicolon() + this.finishNode(node.body, "ClassBody") + return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression") +} lp.parseFunction = function(node, isStatement) { - this.initFunction(node); + this.initFunction(node) if (this.options.ecmaVersion >= 6) { - node.generator = this.eat(tt.star); + node.generator = this.eat(tt.star) } - if (this.tok.type === tt.name) node.id = this.parseIdent(); - else if (isStatement) node.id = this.dummyIdent(); - node.params = this.parseFunctionParams(); - node.body = this.parseBlock(); - return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); -}; + if (this.tok.type === tt.name) node.id = this.parseIdent() + else if (isStatement) node.id = this.dummyIdent() + node.params = this.parseFunctionParams() + node.body = this.parseBlock() + return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression") +} + +lp.parseExport = function() { + let node = this.startNode() + this.next() + if (this.eat(tt.star)) { + node.source = this.eatContextual("from") ? this.parseExprAtom() : null + return this.finishNode(node, "ExportAllDeclaration") + } + if (this.eat(tt._default)) { + let expr = this.parseMaybeAssign() + if (expr.id) { + switch (expr.type) { + case "FunctionExpression": expr.type = "FunctionDeclaration"; break + case "ClassExpression": expr.type = "ClassDeclaration"; break + } + } + node.declaration = expr + this.semicolon() + return this.finishNode(node, "ExportDefaultDeclaration") + } + if (this.tok.type.keyword) { + node.declaration = this.parseStatement() + node.specifiers = [] + node.source = null + } else { + node.declaration = null + node.specifiers = this.parseExportSpecifierList() + node.source = this.eatContextual("from") ? this.parseExprAtom() : null + this.semicolon() + } + return this.finishNode(node, "ExportNamedDeclaration") +} + +lp.parseImport = function() { + let node = this.startNode() + this.next() + if (this.tok.type === tt.string) { + node.specifiers = [] + node.source = this.parseExprAtom() + node.kind = '' + } else { + let elt + if (this.tok.type === tt.name && this.tok.value !== "from") { + elt = this.startNode() + elt.local = this.parseIdent() + this.finishNode(elt, "ImportDefaultSpecifier") + this.eat(tt.comma) + } + node.specifiers = this.parseImportSpecifierList() + node.source = this.eatContextual("from") ? this.parseExprAtom() : null + if (elt) node.specifiers.unshift(elt) + } + this.semicolon() + return this.finishNode(node, "ImportDeclaration") +} + +lp.parseImportSpecifierList = function() { + let elts = [] + if (this.tok.type === tt.star) { + let elt = this.startNode() + this.next() + if (this.eatContextual("as")) elt.local = this.parseIdent() + elts.push(this.finishNode(elt, "ImportNamespaceSpecifier")) + } else { + let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart + this.pushCx() + this.eat(tt.braceL) + if (this.curLineStart > continuedLine) continuedLine = this.curLineStart + while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) { + let elt = this.startNode() + if (this.eat(tt.star)) { + if (this.eatContextual("as")) elt.local = this.parseIdent() + this.finishNode(elt, "ImportNamespaceSpecifier") + } else { + if (this.isContextual("from")) break + elt.imported = this.parseIdent() + elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported + this.finishNode(elt, "ImportSpecifier") + } + elts.push(elt) + this.eat(tt.comma) + } + this.eat(tt.braceR) + this.popCx() + } + return elts +} + +lp.parseExportSpecifierList = function() { + let elts = [] + let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart + this.pushCx() + this.eat(tt.braceL) + if (this.curLineStart > continuedLine) continuedLine = this.curLineStart + while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) { + if (this.isContextual("from")) break + let elt = this.startNode() + elt.local = this.parseIdent() + elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local + this.finishNode(elt, "ExportSpecifier") + elts.push(elt) + this.eat(tt.comma) + } + this.eat(tt.braceR) + this.popCx() + return elts +} diff --git a/src/loose/tokenize.js b/src/loose/tokenize.js index ba3d14a8df..edecc17b5b 100644 --- a/src/loose/tokenize.js +++ b/src/loose/tokenize.js @@ -8,101 +8,101 @@ function isSpace(ch) { } lp.next = function() { - this.last = this.tok; + this.last = this.tok if (this.ahead.length) - this.tok = this.ahead.shift(); + this.tok = this.ahead.shift() else - this.tok = this.readToken(); + this.tok = this.readToken() if (this.tok.start >= this.nextLineStart) { while (this.tok.start >= this.nextLineStart) { - this.curLineStart = this.nextLineStart; - this.nextLineStart = this.lineEnd(this.curLineStart) + 1; + this.curLineStart = this.nextLineStart + this.nextLineStart = this.lineEnd(this.curLineStart) + 1 } - this.curIndent = this.indentationAfter(this.curLineStart); + this.curIndent = this.indentationAfter(this.curLineStart) } -}; +} lp.readToken = function() { for (;;) { try { - this.toks.next(); + this.toks.next() if (this.toks.type === tt.dot && this.input.substr(this.toks.end, 1) === "." && this.options.ecmaVersion >= 6) { - this.toks.end++; - this.toks.type = tt.ellipsis; + this.toks.end++ + this.toks.type = tt.ellipsis } - return new Token(this.toks); + return new Token(this.toks) } catch(e) { - if (!(e instanceof SyntaxError)) throw e; + if (!(e instanceof SyntaxError)) throw e // Try to skip some text, based on the error message, and then continue - var msg = e.message, pos = e.raisedAt, replace = true; + let msg = e.message, pos = e.raisedAt, replace = true if (/unterminated/i.test(msg)) { - pos = this.lineEnd(e.pos + 1); + pos = this.lineEnd(e.pos + 1) if (/string/.test(msg)) { - replace = {start: e.pos, end: pos, type: tt.string, value: this.input.slice(e.pos + 1, pos)}; + replace = {start: e.pos, end: pos, type: tt.string, value: this.input.slice(e.pos + 1, pos)} } else if (/regular expr/i.test(msg)) { - var re = this.input.slice(e.pos, pos); - try { re = new RegExp(re); } catch(e) {} - replace = {start: e.pos, end: pos, type: tt.regexp, value: re}; + let re = this.input.slice(e.pos, pos) + try { re = new RegExp(re) } catch(e) {} + replace = {start: e.pos, end: pos, type: tt.regexp, value: re} } else if (/template/.test(msg)) { replace = {start: e.pos, end: pos, type: tt.template, - value: this.input.slice(e.pos, pos)}; + value: this.input.slice(e.pos, pos)} } else { - replace = false; + replace = false } } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number/i.test(msg)) { - while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos; + while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos } else if (/character escape|expected hexadecimal/i.test(msg)) { while (pos < this.input.length) { - var ch = this.input.charCodeAt(pos++); - if (ch === 34 || ch === 39 || isNewLine(ch)) break; + let ch = this.input.charCodeAt(pos++) + if (ch === 34 || ch === 39 || isNewLine(ch)) break } } else if (/unexpected character/i.test(msg)) { - pos++; - replace = false; + pos++ + replace = false } else if (/regular expression/i.test(msg)) { - replace = true; + replace = true } else { - throw e; + throw e } - this.resetTo(pos); - if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"}; + this.resetTo(pos) + if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"} if (replace) { if (this.options.locations) replace.loc = new SourceLocation( this.toks, getLineInfo(this.input, replace.start), - getLineInfo(this.input, replace.end)); - return replace; + getLineInfo(this.input, replace.end)) + return replace } } } -}; +} lp.resetTo = function(pos) { - this.toks.pos = pos; - var ch = this.input.charAt(pos - 1); + this.toks.pos = pos + let ch = this.input.charAt(pos - 1) this.toks.exprAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) || /[enwfd]/.test(ch) && - /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos)); + /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos)) if (this.options.locations) { - this.toks.curLine = 1; - this.toks.lineStart = lineBreakG.lastIndex = 0; - var match; + this.toks.curLine = 1 + this.toks.lineStart = lineBreakG.lastIndex = 0 + let match while ((match = lineBreakG.exec(this.input)) && match.index < pos) { - ++this.toks.curLine; - this.toks.lineStart = match.index + match[0].length; + ++this.toks.curLine + this.toks.lineStart = match.index + match[0].length } } -}; +} lp.lookAhead = function(n) { while (n > this.ahead.length) - this.ahead.push(this.readToken()); - return this.ahead[n - 1]; -}; + this.ahead.push(this.readToken()) + return this.ahead[n - 1] +} diff --git a/src/lval.js b/src/lval.js index 75ef38147c..660f1c7c74 100644 --- a/src/lval.js +++ b/src/lval.js @@ -15,134 +15,134 @@ pp.toAssignable = function(node, isBinding) { case "ObjectPattern": case "ArrayPattern": case "AssignmentPattern": - break; + break case "ObjectExpression": - node.type = "ObjectPattern"; - for (var i = 0; i < node.properties.length; i++) { - var prop = node.properties[i]; - if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter"); - this.toAssignable(prop.value, isBinding); + node.type = "ObjectPattern" + for (let i = 0; i < node.properties.length; i++) { + let prop = node.properties[i] + if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter") + this.toAssignable(prop.value, isBinding) } - break; + break case "ArrayExpression": - node.type = "ArrayPattern"; - this.toAssignableList(node.elements, isBinding); - break; + node.type = "ArrayPattern" + this.toAssignableList(node.elements, isBinding) + break case "AssignmentExpression": if (node.operator === "=") { - node.type = "AssignmentPattern"; + node.type = "AssignmentPattern" } else { - this.raise(node.left.end, "Only '=' operator can be used for specifying default value."); + this.raise(node.left.end, "Only '=' operator can be used for specifying default value.") } - break; + break case "MemberExpression": - if (!isBinding) break; + if (!isBinding) break default: - this.raise(node.start, "Assigning to rvalue"); + this.raise(node.start, "Assigning to rvalue") } } - return node; -}; + return node +} // Convert list of expression atoms to binding list. pp.toAssignableList = function(exprList, isBinding) { - var end = exprList.length; + let end = exprList.length if (end) { - var last = exprList[end - 1]; + let last = exprList[end - 1] if (last && last.type == "RestElement") { - --end; + --end } else if (last && last.type == "SpreadElement") { - last.type = "RestElement"; - var arg = last.argument; - this.toAssignable(arg, isBinding); + last.type = "RestElement" + let arg = last.argument + this.toAssignable(arg, isBinding) if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") - this.unexpected(arg.start); - --end; + this.unexpected(arg.start) + --end } } - for (var i = 0; i < end; i++) { - var elt = exprList[i]; - if (elt) this.toAssignable(elt, isBinding); + for (let i = 0; i < end; i++) { + let elt = exprList[i] + if (elt) this.toAssignable(elt, isBinding) } - return exprList; -}; + return exprList +} // Parses spread element. pp.parseSpread = function(refShorthandDefaultPos) { - var node = this.startNode(); - this.next(); - node.argument = this.parseMaybeAssign(refShorthandDefaultPos); - return this.finishNode(node, "SpreadElement"); -}; + let node = this.startNode() + this.next() + node.argument = this.parseMaybeAssign(refShorthandDefaultPos) + return this.finishNode(node, "SpreadElement") +} pp.parseRest = function() { - var node = this.startNode(); - this.next(); - node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected(); - return this.finishNode(node, "RestElement"); -}; + let node = this.startNode() + this.next() + node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected() + return this.finishNode(node, "RestElement") +} // Parses lvalue (assignable) atom. pp.parseBindingAtom = function() { - if (this.options.ecmaVersion < 6) return this.parseIdent(); + if (this.options.ecmaVersion < 6) return this.parseIdent() switch (this.type) { case tt.name: - return this.parseIdent(); + return this.parseIdent() case tt.bracketL: - var node = this.startNode(); - this.next(); - node.elements = this.parseBindingList(tt.bracketR, true, true); - return this.finishNode(node, "ArrayPattern"); + let node = this.startNode() + this.next() + node.elements = this.parseBindingList(tt.bracketR, true, true) + return this.finishNode(node, "ArrayPattern") case tt.braceL: - return this.parseObj(true); + return this.parseObj(true) default: - this.unexpected(); + this.unexpected() } -}; +} pp.parseBindingList = function(close, allowEmpty, allowTrailingComma) { - var elts = [], first = true; + let elts = [], first = true while (!this.eat(close)) { - if (first) first = false; - else this.expect(tt.comma); + if (first) first = false + else this.expect(tt.comma) if (allowEmpty && this.type === tt.comma) { - elts.push(null); + elts.push(null) } else if (allowTrailingComma && this.afterTrailingComma(close)) { - break; + break } else if (this.type === tt.ellipsis) { - elts.push(this.parseRest()); - this.expect(close); - break; + elts.push(this.parseRest()) + this.expect(close) + break } else { - elts.push(this.parseMaybeDefault()); + elts.push(this.parseMaybeDefault()) } } - return elts; -}; + return elts +} // Parses assignment pattern around given atom if possible. pp.parseMaybeDefault = function(startPos, left) { - startPos = startPos || this.markPosition(); - left = left || this.parseBindingAtom(); - if (!this.eat(tt.eq)) return left; - var node = this.startNodeAt(startPos); - node.operator = "="; - node.left = left; - node.right = this.parseMaybeAssign(); - return this.finishNode(node, "AssignmentPattern"); -}; + startPos = startPos || this.markPosition() + left = left || this.parseBindingAtom() + if (!this.eat(tt.eq)) return left + let node = this.startNodeAt(startPos) + node.operator = "=" + node.left = left + node.right = this.parseMaybeAssign() + return this.finishNode(node, "AssignmentPattern") +} // Verify that a node is an lval — something that can be assigned // to. @@ -151,39 +151,39 @@ pp.checkLVal = function(expr, isBinding, checkClashes) { switch (expr.type) { case "Identifier": if (this.strict && (reservedWords.strictBind(expr.name) || reservedWords.strict(expr.name))) - this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode"); + this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode") if (checkClashes) { if (has(checkClashes, expr.name)) - this.raise(expr.start, "Argument name clash in strict mode"); - checkClashes[expr.name] = true; + this.raise(expr.start, "Argument name clash in strict mode") + checkClashes[expr.name] = true } - break; + break case "MemberExpression": - if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression"); - break; + if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression") + break case "ObjectPattern": - for (var i = 0; i < expr.properties.length; i++) - this.checkLVal(expr.properties[i].value, isBinding, checkClashes); - break; + for (let i = 0; i < expr.properties.length; i++) + this.checkLVal(expr.properties[i].value, isBinding, checkClashes) + break case "ArrayPattern": - for (var i = 0; i < expr.elements.length; i++) { - var elem = expr.elements[i]; - if (elem) this.checkLVal(elem, isBinding, checkClashes); + for (let i = 0; i < expr.elements.length; i++) { + let elem = expr.elements[i] + if (elem) this.checkLVal(elem, isBinding, checkClashes) } - break; + break case "AssignmentPattern": - this.checkLVal(expr.left, isBinding, checkClashes); - break; + this.checkLVal(expr.left, isBinding, checkClashes) + break case "RestElement": - this.checkLVal(expr.argument, isBinding, checkClashes); - break; + this.checkLVal(expr.argument, isBinding, checkClashes) + break default: - this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue"); + this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue") } -}; +} diff --git a/src/node.js b/src/node.js index e756a9df0f..250f2dde0a 100644 --- a/src/node.js +++ b/src/node.js @@ -8,50 +8,50 @@ const pp = Parser.prototype export class Node {} pp.startNode = function() { - var node = new Node; - node.start = this.start; + let node = new Node + node.start = this.start if (this.options.locations) - node.loc = new SourceLocation(this, this.startLoc); + node.loc = new SourceLocation(this, this.startLoc) if (this.options.directSourceFile) - node.sourceFile = this.options.directSourceFile; + node.sourceFile = this.options.directSourceFile if (this.options.ranges) - node.range = [this.start, 0]; - return node; -}; + node.range = [this.start, 0] + return node +} pp.startNodeAt = function(pos) { - var node = new Node, start = pos; + let node = new Node, start = pos if (this.options.locations) { - node.loc = new SourceLocation(this, start[1]); - start = pos[0]; + node.loc = new SourceLocation(this, start[1]) + start = pos[0] } - node.start = start; + node.start = start if (this.options.directSourceFile) - node.sourceFile = this.options.directSourceFile; + node.sourceFile = this.options.directSourceFile if (this.options.ranges) - node.range = [start, 0]; - return node; -}; + node.range = [start, 0] + return node +} // Finish an AST node, adding `type` and `end` properties. pp.finishNode = function(node, type) { - node.type = type; - node.end = this.lastTokEnd; + node.type = type + node.end = this.lastTokEnd if (this.options.locations) - node.loc.end = this.lastTokEndLoc; + node.loc.end = this.lastTokEndLoc if (this.options.ranges) - node.range[1] = this.lastTokEnd; - return node; -}; + node.range[1] = this.lastTokEnd + return node +} // Finish node at given position pp.finishNodeAt = function(node, type, pos) { - if (this.options.locations) { node.loc.end = pos[1]; pos = pos[0]; } - node.type = type; - node.end = pos; + if (this.options.locations) { node.loc.end = pos[1]; pos = pos[0] } + node.type = type + node.end = pos if (this.options.ranges) - node.range[1] = pos; - return node; -}; + node.range[1] = pos + return node +} diff --git a/src/options.js b/src/options.js index 713345a6b3..cc0967e3d3 100644 --- a/src/options.js +++ b/src/options.js @@ -92,13 +92,13 @@ export function getOptions(opts) { options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt] if (isArray(options.onToken)) { - var tokens = options.onToken; + let tokens = options.onToken options.onToken = (token) => tokens.push(token) } if (isArray(options.onComment)) - options.onComment = pushComment(options, options.onComment); + options.onComment = pushComment(options, options.onComment) - return options; + return options } function pushComment(options, array) { diff --git a/src/parseutil.js b/src/parseutil.js index 24461fabd3..3d464a22dc 100644 --- a/src/parseutil.js +++ b/src/parseutil.js @@ -10,38 +10,38 @@ const pp = Parser.prototype pp.isUseStrict = function(stmt) { return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && - stmt.expression.type === "Literal" && stmt.expression.value === "use strict"; -}; + stmt.expression.type === "Literal" && stmt.expression.value === "use strict" +} // Predicate that tests whether the next token is of the given // type, and if yes, consumes it as a side effect. pp.eat = function(type) { if (this.type === type) { - this.next(); - return true; + this.next() + return true } else { - return false; + return false } -}; +} // Tests whether parsed token is a contextual keyword. pp.isContextual = function(name) { - return this.type === tt.name && this.value === name; -}; + return this.type === tt.name && this.value === name +} // Consumes contextual keyword if possible. pp.eatContextual = function(name) { - return this.value === name && this.eat(tt.name); -}; + return this.value === name && this.eat(tt.name) +} // Asserts that following token is given contextual keyword. pp.expectContextual = function(name) { - if (!this.eatContextual(name)) this.unexpected(); -}; + if (!this.eatContextual(name)) this.unexpected() +} // Test whether a semicolon can be inserted at the current position. @@ -49,41 +49,41 @@ pp.canInsertSemicolon = function() { return this.type === tt.eof || this.type === tt.braceR || lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) -}; +} pp.insertSemicolon = function() { if (this.canInsertSemicolon()) { if (this.options.onInsertedSemicolon) this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc) - return true; + return true } -}; +} // Consume a semicolon, or, failing that, see if we are allowed to // pretend that there is a semicolon at this position. pp.semicolon = function() { - if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected(); -}; + if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected() +} pp.afterTrailingComma = function(tokType) { if (this.type == tokType) { if (this.options.onTrailingComma) - this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); - this.next(); - return true; + this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc) + this.next() + return true } -}; +} // Expect a token of a given type. If found, consume it, otherwise, // raise an unexpected token error. pp.expect = function(type) { - this.eat(type) || this.unexpected(); -}; + this.eat(type) || this.unexpected() +} // Raise an unexpected token error. pp.unexpected = function(pos) { - this.raise(pos != null ? pos : this.start, "Unexpected token"); -}; + this.raise(pos != null ? pos : this.start, "Unexpected token") +} diff --git a/src/statement.js b/src/statement.js index 9014d062ea..d6b94bea24 100644 --- a/src/statement.js +++ b/src/statement.js @@ -12,68 +12,68 @@ const pp = Parser.prototype // to its body instead of creating a new node. pp.parseTopLevel = function(node) { - var first = true; - if (!node.body) node.body = []; + let first = true + if (!node.body) node.body = [] while (this.type !== tt.eof) { - var stmt = this.parseStatement(true, true); - node.body.push(stmt); - if (first && this.isUseStrict(stmt)) this.setStrict(true); - first = false; + let stmt = this.parseStatement(true, true) + node.body.push(stmt) + if (first && this.isUseStrict(stmt)) this.setStrict(true) + first = false } - this.next(); + this.next() if (this.options.ecmaVersion >= 6) { - node.sourceType = this.options.sourceType; + node.sourceType = this.options.sourceType } - return this.finishNode(node, "Program"); -}; + return this.finishNode(node, "Program") +} -var loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}; +const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"} // Parse a single statement. // // If expecting a statement and finding a slash operator, parse a // regular expression literal. This is to handle cases like -// `if (foo) /blah/.exec(foo);`, where looking at the previous token +// `if (foo) /blah/.exec(foo)`, where looking at the previous token // does not help. pp.parseStatement = function(declaration, topLevel) { - var starttype = this.type, node = this.startNode(); + let starttype = this.type, node = this.startNode() // Most types of statements are recognized by the keyword they // start with. Many are trivial to parse, some require a bit of // complexity. switch (starttype) { - case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword); - case tt._debugger: return this.parseDebuggerStatement(node); - case tt._do: return this.parseDoStatement(node); - case tt._for: return this.parseForStatement(node); + case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword) + case tt._debugger: return this.parseDebuggerStatement(node) + case tt._do: return this.parseDoStatement(node) + case tt._for: return this.parseForStatement(node) case tt._function: - if (!declaration && this.options.ecmaVersion >= 6) this.unexpected(); - return this.parseFunctionStatement(node); + if (!declaration && this.options.ecmaVersion >= 6) this.unexpected() + return this.parseFunctionStatement(node) case tt._class: - if (!declaration) this.unexpected(); - return this.parseClass(node, true); - case tt._if: return this.parseIfStatement(node); - case tt._return: return this.parseReturnStatement(node); - case tt._switch: return this.parseSwitchStatement(node); - case tt._throw: return this.parseThrowStatement(node); - case tt._try: return this.parseTryStatement(node); - case tt._let: case tt._const: if (!declaration) this.unexpected(); // NOTE: falls through to _var - case tt._var: return this.parseVarStatement(node, starttype); - case tt._while: return this.parseWhileStatement(node); - case tt._with: return this.parseWithStatement(node); - case tt.braceL: return this.parseBlock(); // no point creating a function for this - case tt.semi: return this.parseEmptyStatement(node); + if (!declaration) this.unexpected() + return this.parseClass(node, true) + case tt._if: return this.parseIfStatement(node) + case tt._return: return this.parseReturnStatement(node) + case tt._switch: return this.parseSwitchStatement(node) + case tt._throw: return this.parseThrowStatement(node) + case tt._try: return this.parseTryStatement(node) + case tt._let: case tt._const: if (!declaration) this.unexpected() // NOTE: falls through to _var + case tt._var: return this.parseVarStatement(node, starttype) + case tt._while: return this.parseWhileStatement(node) + case tt._with: return this.parseWithStatement(node) + case tt.braceL: return this.parseBlock() + case tt.semi: return this.parseEmptyStatement(node) case tt._export: case tt._import: if (!this.options.allowImportExportEverywhere) { if (!topLevel) - this.raise(this.start, "'import' and 'export' may only appear at the top level"); + this.raise(this.start, "'import' and 'export' may only appear at the top level") if (!this.inModule) - this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); + this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'") } - return starttype === tt._import ? this.parseImport(node) : this.parseExport(node); + return starttype === tt._import ? this.parseImport(node) : this.parseExport(node) // If the statement does not start with a statement keyword or a // brace, it's an ExpressionStatement or LabeledStatement. We @@ -81,55 +81,55 @@ pp.parseStatement = function(declaration, topLevel) { // next token is a colon and the expression was a simple // Identifier node, we switch to interpreting it as a label. default: - var maybeName = this.value, expr = this.parseExpression(); + let maybeName = this.value, expr = this.parseExpression() if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) - return this.parseLabeledStatement(node, maybeName, expr); - else return this.parseExpressionStatement(node, expr); + return this.parseLabeledStatement(node, maybeName, expr) + else return this.parseExpressionStatement(node, expr) } -}; +} pp.parseBreakContinueStatement = function(node, keyword) { - var isBreak = keyword == "break"; - this.next(); - if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null; - else if (this.type !== tt.name) this.unexpected(); + let isBreak = keyword == "break" + this.next() + if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null + else if (this.type !== tt.name) this.unexpected() else { - node.label = this.parseIdent(); - this.semicolon(); + node.label = this.parseIdent() + this.semicolon() } // Verify that there is an actual destination to break or // continue to. for (var i = 0; i < this.labels.length; ++i) { - var lab = this.labels[i]; + let lab = this.labels[i] if (node.label == null || lab.name === node.label.name) { - if (lab.kind != null && (isBreak || lab.kind === "loop")) break; - if (node.label && isBreak) break; + if (lab.kind != null && (isBreak || lab.kind === "loop")) break + if (node.label && isBreak) break } } - if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword); - return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); -}; + if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword) + return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement") +} pp.parseDebuggerStatement = function(node) { - this.next(); - this.semicolon(); - return this.finishNode(node, "DebuggerStatement"); -}; + this.next() + this.semicolon() + return this.finishNode(node, "DebuggerStatement") +} pp.parseDoStatement = function(node) { - this.next(); - this.labels.push(loopLabel); - node.body = this.parseStatement(false); - this.labels.pop(); - this.expect(tt._while); - node.test = this.parseParenExpression(); + this.next() + this.labels.push(loopLabel) + node.body = this.parseStatement(false) + this.labels.pop() + this.expect(tt._while) + node.test = this.parseParenExpression() if (this.options.ecmaVersion >= 6) - this.eat(tt.semi); + this.eat(tt.semi) else - this.semicolon(); - return this.finishNode(node, "DoWhileStatement"); -}; + this.semicolon() + return this.finishNode(node, "DoWhileStatement") +} // Disambiguating between a `for` and a `for`/`in` or `for`/`of` // loop is non-trivial. Basically, we have to parse the init `var` @@ -140,65 +140,65 @@ pp.parseDoStatement = function(node) { // is a regular `for` loop. pp.parseForStatement = function(node) { - this.next(); - this.labels.push(loopLabel); - this.expect(tt.parenL); - if (this.type === tt.semi) return this.parseFor(node, null); + this.next() + this.labels.push(loopLabel) + this.expect(tt.parenL) + if (this.type === tt.semi) return this.parseFor(node, null) if (this.type === tt._var || this.type === tt._let || this.type === tt._const) { - var init = this.startNode(), varKind = this.type; - this.next(); - this.parseVar(init, true, varKind); - this.finishNode(init, "VariableDeclaration"); + let init = this.startNode(), varKind = this.type + this.next() + this.parseVar(init, true, varKind) + this.finishNode(init, "VariableDeclaration") if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 && !(varKind !== tt._var && init.declarations[0].init)) - return this.parseForIn(node, init); - return this.parseFor(node, init); + return this.parseForIn(node, init) + return this.parseFor(node, init) } - var refShorthandDefaultPos = {start: 0}; - var init = this.parseExpression(true, refShorthandDefaultPos); + let refShorthandDefaultPos = {start: 0} + let init = this.parseExpression(true, refShorthandDefaultPos) if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) { - this.toAssignable(init); - this.checkLVal(init); - return this.parseForIn(node, init); + this.toAssignable(init) + this.checkLVal(init) + return this.parseForIn(node, init) } else if (refShorthandDefaultPos.start) { - this.unexpected(refShorthandDefaultPos.start); + this.unexpected(refShorthandDefaultPos.start) } - return this.parseFor(node, init); -}; + return this.parseFor(node, init) +} pp.parseFunctionStatement = function(node) { - this.next(); - return this.parseFunction(node, true); -}; + this.next() + return this.parseFunction(node, true) +} pp.parseIfStatement = function(node) { - this.next(); - node.test = this.parseParenExpression(); - node.consequent = this.parseStatement(false); - node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null; - return this.finishNode(node, "IfStatement"); -}; + this.next() + node.test = this.parseParenExpression() + node.consequent = this.parseStatement(false) + node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null + return this.finishNode(node, "IfStatement") +} pp.parseReturnStatement = function(node) { if (!this.inFunction && !this.options.allowReturnOutsideFunction) - this.raise(this.start, "'return' outside of function"); - this.next(); + this.raise(this.start, "'return' outside of function") + this.next() // In `return` (and `break`/`continue`), the keywords with // optional arguments, we eagerly look for a semicolon or the // possibility to insert one. - if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null; - else { node.argument = this.parseExpression(); this.semicolon(); } - return this.finishNode(node, "ReturnStatement"); -}; + if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null + else { node.argument = this.parseExpression(); this.semicolon() } + return this.finishNode(node, "ReturnStatement") +} pp.parseSwitchStatement = function(node) { - this.next(); - node.discriminant = this.parseParenExpression(); - node.cases = []; - this.expect(tt.braceL); - this.labels.push(switchLabel); + this.next() + node.discriminant = this.parseParenExpression() + node.cases = [] + this.expect(tt.braceL) + this.labels.push(switchLabel) // Statements under must be grouped (by label) in SwitchCase // nodes. `cur` is used to keep the node that we are currently @@ -206,241 +206,357 @@ pp.parseSwitchStatement = function(node) { for (var cur, sawDefault; this.type != tt.braceR;) { if (this.type === tt._case || this.type === tt._default) { - var isCase = this.type === tt._case; - if (cur) this.finishNode(cur, "SwitchCase"); - node.cases.push(cur = this.startNode()); - cur.consequent = []; - this.next(); - if (isCase) cur.test = this.parseExpression(); - else { - if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses"); sawDefault = true; - cur.test = null; + let isCase = this.type === tt._case + if (cur) this.finishNode(cur, "SwitchCase") + node.cases.push(cur = this.startNode()) + cur.consequent = [] + this.next() + if (isCase) { + cur.test = this.parseExpression() + } else { + if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses") + sawDefault = true + cur.test = null } - this.expect(tt.colon); + this.expect(tt.colon) } else { - if (!cur) this.unexpected(); - cur.consequent.push(this.parseStatement(true)); + if (!cur) this.unexpected() + cur.consequent.push(this.parseStatement(true)) } } - if (cur) this.finishNode(cur, "SwitchCase"); - this.next(); // Closing brace - this.labels.pop(); - return this.finishNode(node, "SwitchStatement"); -}; + if (cur) this.finishNode(cur, "SwitchCase") + this.next() // Closing brace + this.labels.pop() + return this.finishNode(node, "SwitchStatement") +} pp.parseThrowStatement = function(node) { - this.next(); + this.next() if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) - this.raise(this.lastTokEnd, "Illegal newline after throw"); - node.argument = this.parseExpression(); - this.semicolon(); - return this.finishNode(node, "ThrowStatement"); -}; + this.raise(this.lastTokEnd, "Illegal newline after throw") + node.argument = this.parseExpression() + this.semicolon() + return this.finishNode(node, "ThrowStatement") +} // Reused empty array added for node fields that are always empty. const empty = [] pp.parseTryStatement = function(node) { - this.next(); - node.block = this.parseBlock(); - node.handler = null; + this.next() + node.block = this.parseBlock() + node.handler = null if (this.type === tt._catch) { - var clause = this.startNode(); - this.next(); - this.expect(tt.parenL); - clause.param = this.parseBindingAtom(); - this.checkLVal(clause.param, true); - this.expect(tt.parenR); - clause.guard = null; - clause.body = this.parseBlock(); - node.handler = this.finishNode(clause, "CatchClause"); + let clause = this.startNode() + this.next() + this.expect(tt.parenL) + clause.param = this.parseBindingAtom() + this.checkLVal(clause.param, true) + this.expect(tt.parenR) + clause.guard = null + clause.body = this.parseBlock() + node.handler = this.finishNode(clause, "CatchClause") } - node.guardedHandlers = empty; - node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null; + node.guardedHandlers = empty + node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null if (!node.handler && !node.finalizer) - this.raise(node.start, "Missing catch or finally clause"); - return this.finishNode(node, "TryStatement"); -}; + this.raise(node.start, "Missing catch or finally clause") + return this.finishNode(node, "TryStatement") +} pp.parseVarStatement = function(node, kind) { - this.next(); - this.parseVar(node, false, kind); - this.semicolon(); - return this.finishNode(node, "VariableDeclaration"); -}; + this.next() + this.parseVar(node, false, kind) + this.semicolon() + return this.finishNode(node, "VariableDeclaration") +} pp.parseWhileStatement = function(node) { - this.next(); - node.test = this.parseParenExpression(); - this.labels.push(loopLabel); - node.body = this.parseStatement(false); - this.labels.pop(); - return this.finishNode(node, "WhileStatement"); -}; + this.next() + node.test = this.parseParenExpression() + this.labels.push(loopLabel) + node.body = this.parseStatement(false) + this.labels.pop() + return this.finishNode(node, "WhileStatement") +} pp.parseWithStatement = function(node) { - if (this.strict) this.raise(this.start, "'with' in strict mode"); - this.next(); - node.object = this.parseParenExpression(); - node.body = this.parseStatement(false); - return this.finishNode(node, "WithStatement"); -}; + if (this.strict) this.raise(this.start, "'with' in strict mode") + this.next() + node.object = this.parseParenExpression() + node.body = this.parseStatement(false) + return this.finishNode(node, "WithStatement") +} pp.parseEmptyStatement = function(node) { - this.next(); - return this.finishNode(node, "EmptyStatement"); -}; + this.next() + return this.finishNode(node, "EmptyStatement") +} pp.parseLabeledStatement = function(node, maybeName, expr) { - for (var i = 0; i < this.labels.length; ++i) - if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared"); - var kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null; - this.labels.push({name: maybeName, kind: kind}); - node.body = this.parseStatement(true); - this.labels.pop(); - node.label = expr; - return this.finishNode(node, "LabeledStatement"); -}; + for (let i = 0; i < this.labels.length; ++i) + if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared") + let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null + this.labels.push({name: maybeName, kind: kind}) + node.body = this.parseStatement(true) + this.labels.pop() + node.label = expr + return this.finishNode(node, "LabeledStatement") +} pp.parseExpressionStatement = function(node, expr) { - node.expression = expr; - this.semicolon(); - return this.finishNode(node, "ExpressionStatement"); -}; + node.expression = expr + this.semicolon() + return this.finishNode(node, "ExpressionStatement") +} // Parse a semicolon-enclosed block of statements, handling `"use // strict"` declarations when `allowStrict` is true (used for // function bodies). pp.parseBlock = function(allowStrict) { - var node = this.startNode(), first = true, oldStrict; - node.body = []; - this.expect(tt.braceL); + let node = this.startNode(), first = true, oldStrict + node.body = [] + this.expect(tt.braceL) while (!this.eat(tt.braceR)) { - var stmt = this.parseStatement(true); - node.body.push(stmt); + let stmt = this.parseStatement(true) + node.body.push(stmt) if (first && allowStrict && this.isUseStrict(stmt)) { - oldStrict = this.strict; - this.setStrict(this.strict = true); + oldStrict = this.strict + this.setStrict(this.strict = true) } - first = false; + first = false } - if (oldStrict === false) this.setStrict(false); - return this.finishNode(node, "BlockStatement"); -}; + if (oldStrict === false) this.setStrict(false) + return this.finishNode(node, "BlockStatement") +} // Parse a regular `for` loop. The disambiguation code in // `parseStatement` will already have parsed the init statement or // expression. pp.parseFor = function(node, init) { - node.init = init; - this.expect(tt.semi); - node.test = this.type === tt.semi ? null : this.parseExpression(); - this.expect(tt.semi); - node.update = this.type === tt.parenR ? null : this.parseExpression(); - this.expect(tt.parenR); - node.body = this.parseStatement(false); - this.labels.pop(); - return this.finishNode(node, "ForStatement"); -}; + node.init = init + this.expect(tt.semi) + node.test = this.type === tt.semi ? null : this.parseExpression() + this.expect(tt.semi) + node.update = this.type === tt.parenR ? null : this.parseExpression() + this.expect(tt.parenR) + node.body = this.parseStatement(false) + this.labels.pop() + return this.finishNode(node, "ForStatement") +} // Parse a `for`/`in` and `for`/`of` loop, which are almost // same from parser's perspective. pp.parseForIn = function(node, init) { - var type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"; - this.next(); - node.left = init; - node.right = this.parseExpression(); - this.expect(tt.parenR); - node.body = this.parseStatement(false); - this.labels.pop(); - return this.finishNode(node, type); -}; + let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement" + this.next() + node.left = init + node.right = this.parseExpression() + this.expect(tt.parenR) + node.body = this.parseStatement(false) + this.labels.pop() + return this.finishNode(node, type) +} // Parse a list of variable declarations. pp.parseVar = function(node, noIn, kind) { - node.declarations = []; - node.kind = kind.keyword; + node.declarations = [] + node.kind = kind.keyword for (;;) { - var decl = this.startNode(); - decl.id = this.parseBindingAtom(); - this.checkLVal(decl.id, true); + let decl = this.startNode() + decl.id = this.parseBindingAtom() + this.checkLVal(decl.id, true) if (this.eat(tt.eq)) { - decl.init = this.parseMaybeAssign(noIn); + decl.init = this.parseMaybeAssign(noIn) } else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) { - this.unexpected(); + this.unexpected() } else if (decl.id.type != "Identifier") { this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value") } else { - decl.init = null; + decl.init = null } - node.declarations.push(this.finishNode(decl, "VariableDeclarator")); - if (!this.eat(tt.comma)) break; + node.declarations.push(this.finishNode(decl, "VariableDeclarator")) + if (!this.eat(tt.comma)) break } - return node; -}; + return node +} // Parse a function declaration or literal (depending on the // `isStatement` parameter). pp.parseFunction = function(node, isStatement, allowExpressionBody) { - this.initFunction(node); - if (this.options.ecmaVersion >= 6) { - node.generator = this.eat(tt.star); - } - if (isStatement || this.type === tt.name) { - node.id = this.parseIdent(); - } - this.expect(tt.parenL); - node.params = this.parseBindingList(tt.parenR, false, false); - this.parseFunctionBody(node, allowExpressionBody); - return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); -}; + this.initFunction(node) + if (this.options.ecmaVersion >= 6) + node.generator = this.eat(tt.star) + if (isStatement || this.type === tt.name) + node.id = this.parseIdent() + this.expect(tt.parenL) + node.params = this.parseBindingList(tt.parenR, false, false) + this.parseFunctionBody(node, allowExpressionBody) + return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression") +} // Parse a class declaration or literal (depending on the // `isStatement` parameter). pp.parseClass = function(node, isStatement) { - this.next(); - node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null; - node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null; - var classBody = this.startNode(); - classBody.body = []; - this.expect(tt.braceL); + this.next() + node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null + node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null + let classBody = this.startNode() + classBody.body = [] + this.expect(tt.braceL) while (!this.eat(tt.braceR)) { - if (this.eat(tt.semi)) continue; - var method = this.startNode(); - var isGenerator = this.eat(tt.star); - this.parsePropertyName(method); + if (this.eat(tt.semi)) continue + let method = this.startNode() + let isGenerator = this.eat(tt.star) + this.parsePropertyName(method) if (this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && method.key.name === "static") { - if (isGenerator) this.unexpected(); - method['static'] = true; - isGenerator = this.eat(tt.star); - this.parsePropertyName(method); + if (isGenerator) this.unexpected() + method['static'] = true + isGenerator = this.eat(tt.star) + this.parsePropertyName(method) } else { - method['static'] = false; + method['static'] = false } - method.kind = "method"; + method.kind = "method" if (!method.computed && !isGenerator) { if (method.key.type === "Identifier") { if (this.type !== tt.parenL && (method.key.name === "get" || method.key.name === "set")) { - method.kind = method.key.name; - this.parsePropertyName(method); + method.kind = method.key.name + this.parsePropertyName(method) } else if (!method['static'] && method.key.name === "constructor") { - method.kind = "constructor"; + method.kind = "constructor" } } else if (!method['static'] && method.key.type === "Literal" && method.key.value === "constructor") { - method.kind = "constructor"; + method.kind = "constructor" } } - method.value = this.parseMethod(isGenerator); - classBody.body.push(this.finishNode(method, "MethodDefinition")); + method.value = this.parseMethod(isGenerator) + classBody.body.push(this.finishNode(method, "MethodDefinition")) } - node.body = this.finishNode(classBody, "ClassBody"); - return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); -}; + node.body = this.finishNode(classBody, "ClassBody") + return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression") +} + +// Parses module export declaration. + +pp.parseExport = function(node) { + this.next() + // export * from '...' + if (this.eat(tt.star)) { + this.expectContextual("from") + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + this.semicolon() + return this.finishNode(node, "ExportAllDeclaration") + } + if (this.eat(tt._default)) { // export default ... + let expr = this.parseMaybeAssign() + if (expr.id) { + switch (expr.type) { + case "FunctionExpression": expr.type = "FunctionDeclaration"; break + case "ClassExpression": expr.type = "ClassDeclaration"; break + } + } + node.declaration = expr + this.semicolon() + return this.finishNode(node, "ExportDefaultDeclaration") + } + // export var|const|let|function|class ... + if (this.type.keyword) { + node.declaration = this.parseStatement(true) + node.specifiers = [] + node.source = null + } else { // export { x, y as z } [from '...'] + node.declaration = null + node.specifiers = this.parseExportSpecifiers() + if (this.eatContextual("from")) { + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + } else { + node.source = null + } + this.semicolon() + } + return this.finishNode(node, "ExportNamedDeclaration") +} + +// Parses a comma-separated list of module exports. + +pp.parseExportSpecifiers = function() { + let nodes = [], first = true + // export { x, y as z } [from '...'] + this.expect(tt.braceL) + while (!this.eat(tt.braceR)) { + if (!first) { + this.expect(tt.comma) + if (this.afterTrailingComma(tt.braceR)) break + } else first = false + + let node = this.startNode() + node.local = this.parseIdent(this.type === tt._default) + node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local + nodes.push(this.finishNode(node, "ExportSpecifier")) + } + return nodes +} + +// Parses import declaration. + +pp.parseImport = function(node) { + this.next() + // import '...' + if (this.type === tt.string) { + node.specifiers = empty + node.source = this.parseExprAtom() + node.kind = "" + } else { + node.specifiers = this.parseImportSpecifiers() + this.expectContextual("from") + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + } + this.semicolon() + return this.finishNode(node, "ImportDeclaration") +} + +// Parses a comma-separated list of module imports. + +pp.parseImportSpecifiers = function() { + let nodes = [], first = true + if (this.type === tt.name) { + // import defaultObj, { x, y as z } from '...' + let node = this.startNode() + node.local = this.parseIdent() + this.checkLVal(node.local, true) + nodes.push(this.finishNode(node, "ImportDefaultSpecifier")) + if (!this.eat(tt.comma)) return nodes + } + if (this.type === tt.star) { + let node = this.startNode() + this.next() + this.expectContextual("as") + node.local = this.parseIdent() + this.checkLVal(node.local, true) + nodes.push(this.finishNode(node, "ImportNamespaceSpecifier")) + return nodes + } + this.expect(tt.braceL) + while (!this.eat(tt.braceR)) { + if (!first) { + this.expect(tt.comma) + if (this.afterTrailingComma(tt.braceR)) break + } else first = false + + let node = this.startNode() + node.imported = this.parseIdent(true) + node.local = this.eatContextual("as") ? this.parseIdent() : node.imported + this.checkLVal(node.local, true) + nodes.push(this.finishNode(node, "ImportSpecifier")) + } + return nodes +} diff --git a/src/tokencontext.js b/src/tokencontext.js index a23b5fab16..a3cabb58e2 100644 --- a/src/tokencontext.js +++ b/src/tokencontext.js @@ -32,72 +32,72 @@ pp.initialContext = function() { } pp.braceIsBlock = function(prevType) { - var parent; + let parent if (prevType === tt.colon && (parent = this.curContext()).token == "{") - return !parent.isExpr; + return !parent.isExpr if (prevType === tt._return) - return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)); + return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof) - return true; + return true if (prevType == tt.braceL) - return this.curContext() === types.b_stat; - return !this.exprAllowed; -}; + return this.curContext() === types.b_stat + return !this.exprAllowed +} pp.updateContext = function(prevType) { - var update, type = this.type; + let update, type = this.type if (type.keyword && prevType == tt.dot) - this.exprAllowed = false; + this.exprAllowed = false else if (update = type.updateContext) - update.call(this, prevType); + update.call(this, prevType) else - this.exprAllowed = type.beforeExpr; -}; + this.exprAllowed = type.beforeExpr +} // Token-specific context update code tt.parenR.updateContext = tt.braceR.updateContext = function() { - var out = this.context.pop(); + let out = this.context.pop() if (out === types.b_stat && this.curContext() === types.f_expr) { - this.context.pop(); - this.exprAllowed = false; + this.context.pop() + this.exprAllowed = false } else if (out === types.b_tmpl) { - this.exprAllowed = true; + this.exprAllowed = true } else { - this.exprAllowed = !(out && out.isExpr); + this.exprAllowed = !(out && out.isExpr) } -}; +} tt.braceL.updateContext = function(prevType) { - this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr); - this.exprAllowed = true; -}; + this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr) + this.exprAllowed = true +} tt.dollarBraceL.updateContext = function() { - this.context.push(types.b_tmpl); - this.exprAllowed = true; -}; + this.context.push(types.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 ? types.p_stat : types.p_expr); - this.exprAllowed = true; -}; + let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while + this.context.push(statementParens ? types.p_stat : types.p_expr) + this.exprAllowed = true +} tt.incDec.updateContext = function() { // tokExprAllowed stays unchanged -}; +} tt._function.updateContext = function() { if (this.curContext() !== types.b_stat) - this.context.push(types.f_expr); - this.exprAllowed = false; -}; + this.context.push(types.f_expr) + this.exprAllowed = false +} tt.backQuote.updateContext = function() { if (this.curContext() === types.q_tmpl) - this.context.pop(); + this.context.pop() else - this.context.push(types.q_tmpl); - this.exprAllowed = false; -}; + this.context.push(types.q_tmpl) + this.exprAllowed = false +} diff --git a/src/tokenize.js b/src/tokenize.js index 470a9b49b6..16eb0f93be 100644 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -10,14 +10,14 @@ import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace export class Token { constructor(p) { - this.type = p.type; - this.value = p.value; - this.start = p.start; - this.end = p.end; + 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); + this.loc = new SourceLocation(p, p.startLoc, p.endLoc) if (p.options.ranges) - this.range = [p.start, p.end]; + this.range = [p.start, p.end] } } @@ -29,52 +29,52 @@ const pp = Parser.prototype pp.next = function() { if (this.options.onToken) - this.options.onToken(new Token(this)); + this.options.onToken(new Token(this)) - this.lastTokEnd = this.end; - this.lastTokStart = this.start; - this.lastTokEndLoc = this.endLoc; - this.lastTokStartLoc = this.startLoc; - this.nextToken(); -}; + 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); -}; + 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; + let self = this return {next: function () { - var token = self.getToken(); + let 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; + 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.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1 + --this.curLine } } - this.nextToken(); -}; + this.nextToken() +} pp.curContext = function() { - return this.context[this.context.length - 1]; -}; + return this.context[this.context.length - 1] +} // Read a single token, updating the parser object's token-related // properties. @@ -95,91 +95,91 @@ 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.readWord() - return this.getTokenFromCode(code); -}; + 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; -}; + 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 +} 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; + let startLoc = this.options.onComment && this.options.locations && 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; - var match; + lineBreakG.lastIndex = start + let match while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) { - ++this.curLine; - this.lineStart = match.index + match[0].length; + ++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()); -}; + 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); + let start = this.pos + let startLoc = this.options.onComment && this.options.locations && 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); + ++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()); -}; + 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); + let ch = this.input.charCodeAt(this.pos) if (ch === 32) { // ' ' - ++this.pos; + ++this.pos } else if (ch === 13) { - ++this.pos; - var next = this.input.charCodeAt(this.pos); + ++this.pos + let next = this.input.charCodeAt(this.pos) if (next === 10) { - ++this.pos; + ++this.pos } if (this.options.locations) { - ++this.curLine; - this.lineStart = this.pos; + ++this.curLine + this.lineStart = this.pos } } else if (ch === 10 || ch === 8232 || ch === 8233) { - ++this.pos; + ++this.pos if (this.options.locations) { - ++this.curLine; - this.lineStart = this.pos; + ++this.curLine + this.lineStart = this.pos } } else if (ch > 8 && ch < 14) { - ++this.pos; + ++this.pos } else if (ch === 47) { // '/' - var next = this.input.charCodeAt(this.pos + 1); + let next = this.input.charCodeAt(this.pos + 1) if (next === 42) { // '*' - this.skipBlockComment(); + this.skipBlockComment() } else if (next === 47) { // '/' - this.skipLineComment(2); - } else break; + this.skipLineComment(2) + } else break } else if (ch === 160) { // '\xa0' - ++this.pos; + ++this.pos } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { - ++this.pos; + ++this.pos } else { - break; + break } } -}; +} // Called at the end of every token. Sets `end`, `val`, and // maintains `context` and `exprAllowed`, and skips the space after @@ -187,14 +187,14 @@ pp.skipSpace = function() { // 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.end = this.pos + if (this.options.locations) this.endLoc = this.curPosition() + let prevType = this.type + this.type = type + this.value = val - this.updateContext(prevType); -}; + this.updateContext(prevType) +} // ### Token reading @@ -206,117 +206,117 @@ pp.finishToken = function(type, val) { // 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); + 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 (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.' - this.pos += 3; - return this.finishToken(tt.ellipsis); + this.pos += 3 + return this.finishToken(tt.ellipsis) } else { - ++this.pos; - return this.finishToken(tt.dot); + ++this.pos + return this.finishToken(tt.dot) } -}; +} pp.readToken_slash = function() { // '/' - var next = this.input.charCodeAt(this.pos + 1); + 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); -}; + if (next === 61) return this.finishOp(tt.assign, 2) + return this.finishOp(tt.slash, 1) +} pp.readToken_mult_modulo = function(code) { // '%*' - var next = this.input.charCodeAt(this.pos + 1); - if (next === 61) return this.finishOp(tt.assign, 2); - return this.finishOp(code === 42 ? tt.star : tt.modulo, 1); -}; + let next = this.input.charCodeAt(this.pos + 1) + if (next === 61) return this.finishOp(tt.assign, 2) + return this.finishOp(code === 42 ? tt.star : tt.modulo, 1) +} 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); -}; + 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() { // '^' - var next = this.input.charCodeAt(this.pos + 1); - if (next === 61) return this.finishOp(tt.assign, 2); - return this.finishOp(tt.bitwiseXOR, 1); -}; + let 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); + 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(); + this.skipLineComment(3) + this.skipSpace() + return this.nextToken() } - return this.finishOp(tt.incDec, 2); + return this.finishOp(tt.incDec, 2) } - if (next === 61) return this.finishOp(tt.assign, 2); - return this.finishOp(tt.plusMin, 1); -}; + 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; + 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); + 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) unexpected(); + if (this.inModule) unexpected() // `