react / wstein / node_modules / browserify / node_modules / module-deps / node_modules / detective / node_modules / acorn / src / expression.js
80559 views// A recursive descent parser operates by defining functions for all1// syntactic elements, and recursively calling those, each function2// advancing the input stream and returning an AST node. Precedence3// of constructs (for example, the fact that `!x[1]` means `!(x[1])`4// instead of `(!x)[1]` is handled by the fact that the parser5// function that parses unary prefix operators is called first, and6// in turn calls the function that parses `[]` subscripts — that7// way, it'll receive the node for `x[1]` already parsed, and wraps8// *that* in the unary operator node.9//10// Acorn uses an [operator precedence parser][opp] to handle binary11// operator precedence, because it is much more compact than using12// the technique outlined above, which uses different, nesting13// functions to specify precedence, for all of the ten binary14// precedence levels that JavaScript defines.15//16// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser1718import {types as tt} from "./tokentype"19import {Parser} from "./state"20import {reservedWords} from "./identifier"21import {has} from "./util"2223const pp = Parser.prototype2425// Check if property name clashes with already added.26// Object/class getters and setters are not allowed to clash —27// either with each other or with an init property — and in28// strict mode, init properties are also not allowed to be repeated.2930pp.checkPropClash = function(prop, propHash) {31if (this.options.ecmaVersion >= 6) return32let key = prop.key, name33switch (key.type) {34case "Identifier": name = key.name; break35case "Literal": name = String(key.value); break36default: return37}38let kind = prop.kind || "init", other39if (has(propHash, name)) {40other = propHash[name]41let isGetSet = kind !== "init"42if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init))43this.raise(key.start, "Redefinition of property")44} else {45other = propHash[name] = {46init: false,47get: false,48set: false49}50}51other[kind] = true52}5354// ### Expression parsing5556// These nest, from the most general expression type at the top to57// 'atomic', nondivisible expression types at the bottom. Most of58// the functions will simply let the function(s) below them parse,59// and, *if* the syntactic construct they handle is present, wrap60// the AST node that the inner parser gave them in another node.6162// Parse a full expression. The optional arguments are used to63// forbid the `in` operator (in for loops initalization expressions)64// and provide reference for storing '=' operator inside shorthand65// property assignment in contexts where both object expression66// and object pattern might appear (so it's possible to raise67// delayed syntax error at correct position).6869pp.parseExpression = function(noIn, refShorthandDefaultPos) {70let startPos = this.start, startLoc = this.startLoc71let expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos)72if (this.type === tt.comma) {73let node = this.startNodeAt(startPos, startLoc)74node.expressions = [expr]75while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos))76return this.finishNode(node, "SequenceExpression")77}78return expr79}8081// Parse an assignment expression. This includes applications of82// operators like `+=`.8384pp.parseMaybeAssign = function(noIn, refShorthandDefaultPos, afterLeftParse) {85if (this.type == tt._yield && this.inGenerator) return this.parseYield()8687let failOnShorthandAssign88if (!refShorthandDefaultPos) {89refShorthandDefaultPos = {start: 0}90failOnShorthandAssign = true91} else {92failOnShorthandAssign = false93}94let startPos = this.start, startLoc = this.startLoc95if (this.type == tt.parenL || this.type == tt.name)96this.potentialArrowAt = this.start97let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos)98if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc)99if (this.type.isAssign) {100let node = this.startNodeAt(startPos, startLoc)101node.operator = this.value102node.left = this.type === tt.eq ? this.toAssignable(left) : left103refShorthandDefaultPos.start = 0 // reset because shorthand default was used correctly104this.checkLVal(left)105this.next()106node.right = this.parseMaybeAssign(noIn)107return this.finishNode(node, "AssignmentExpression")108} else if (failOnShorthandAssign && refShorthandDefaultPos.start) {109this.unexpected(refShorthandDefaultPos.start)110}111return left112}113114// Parse a ternary conditional (`?:`) operator.115116pp.parseMaybeConditional = function(noIn, refShorthandDefaultPos) {117let startPos = this.start, startLoc = this.startLoc118let expr = this.parseExprOps(noIn, refShorthandDefaultPos)119if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr120if (this.eat(tt.question)) {121let node = this.startNodeAt(startPos, startLoc)122node.test = expr123node.consequent = this.parseMaybeAssign()124this.expect(tt.colon)125node.alternate = this.parseMaybeAssign(noIn)126return this.finishNode(node, "ConditionalExpression")127}128return expr129}130131// Start the precedence parser.132133pp.parseExprOps = function(noIn, refShorthandDefaultPos) {134let startPos = this.start, startLoc = this.startLoc135let expr = this.parseMaybeUnary(refShorthandDefaultPos)136if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr137return this.parseExprOp(expr, startPos, startLoc, -1, noIn)138}139140// Parse binary operators with the operator precedence parsing141// algorithm. `left` is the left-hand side of the operator.142// `minPrec` provides context that allows the function to stop and143// defer further parser to one of its callers when it encounters an144// operator that has a lower precedence than the set it is parsing.145146pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {147let prec = this.type.binop148if (Array.isArray(leftStartPos)){149if (this.options.locations && noIn === undefined) {150// shift arguments to left by one151noIn = minPrec152minPrec = leftStartLoc153// flatten leftStartPos154leftStartLoc = leftStartPos[1]155leftStartPos = leftStartPos[0]156}157}158if (prec != null && (!noIn || this.type !== tt._in)) {159if (prec > minPrec) {160let node = this.startNodeAt(leftStartPos, leftStartLoc)161node.left = left162node.operator = this.value163let op = this.type164this.next()165let startPos = this.start, startLoc = this.startLoc166node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn)167this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression")168return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)169}170}171return left172}173174// Parse unary operators, both prefix and postfix.175176pp.parseMaybeUnary = function(refShorthandDefaultPos) {177if (this.type.prefix) {178let node = this.startNode(), update = this.type === tt.incDec179node.operator = this.value180node.prefix = true181this.next()182node.argument = this.parseMaybeUnary()183if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)184if (update) this.checkLVal(node.argument)185else if (this.strict && node.operator === "delete" &&186node.argument.type === "Identifier")187this.raise(node.start, "Deleting local variable in strict mode")188return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")189}190let startPos = this.start, startLoc = this.startLoc191let expr = this.parseExprSubscripts(refShorthandDefaultPos)192if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr193while (this.type.postfix && !this.canInsertSemicolon()) {194let node = this.startNodeAt(startPos, startLoc)195node.operator = this.value196node.prefix = false197node.argument = expr198this.checkLVal(expr)199this.next()200expr = this.finishNode(node, "UpdateExpression")201}202return expr203}204205// Parse call, dot, and `[]`-subscript expressions.206207pp.parseExprSubscripts = function(refShorthandDefaultPos) {208let startPos = this.start, startLoc = this.startLoc209let expr = this.parseExprAtom(refShorthandDefaultPos)210if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr211return this.parseSubscripts(expr, startPos, startLoc)212}213214pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {215if (Array.isArray(startPos)){216if (this.options.locations && noCalls === undefined) {217// shift arguments to left by one218noCalls = startLoc219// flatten startPos220startLoc = startPos[1]221startPos = startPos[0]222}223}224for (;;) {225if (this.eat(tt.dot)) {226let node = this.startNodeAt(startPos, startLoc)227node.object = base228node.property = this.parseIdent(true)229node.computed = false230base = this.finishNode(node, "MemberExpression")231} else if (this.eat(tt.bracketL)) {232let node = this.startNodeAt(startPos, startLoc)233node.object = base234node.property = this.parseExpression()235node.computed = true236this.expect(tt.bracketR)237base = this.finishNode(node, "MemberExpression")238} else if (!noCalls && this.eat(tt.parenL)) {239let node = this.startNodeAt(startPos, startLoc)240node.callee = base241node.arguments = this.parseExprList(tt.parenR, false)242base = this.finishNode(node, "CallExpression")243} else if (this.type === tt.backQuote) {244let node = this.startNodeAt(startPos, startLoc)245node.tag = base246node.quasi = this.parseTemplate()247base = this.finishNode(node, "TaggedTemplateExpression")248} else {249return base250}251}252}253254// Parse an atomic expression — either a single token that is an255// expression, an expression started by a keyword like `function` or256// `new`, or an expression wrapped in punctuation like `()`, `[]`,257// or `{}`.258259pp.parseExprAtom = function(refShorthandDefaultPos) {260let node, canBeArrow = this.potentialArrowAt == this.start261switch (this.type) {262case tt._this:263case tt._super:264let type = this.type === tt._this ? "ThisExpression" : "Super"265node = this.startNode()266this.next()267return this.finishNode(node, type)268269case tt._yield:270if (this.inGenerator) this.unexpected()271272case tt.name:273let startPos = this.start, startLoc = this.startLoc274let id = this.parseIdent(this.type !== tt.name)275if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow))276return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id])277return id278279case tt.regexp:280let value = this.value281node = this.parseLiteral(value.value)282node.regex = {pattern: value.pattern, flags: value.flags}283return node284285case tt.num: case tt.string:286return this.parseLiteral(this.value)287288case tt._null: case tt._true: case tt._false:289node = this.startNode()290node.value = this.type === tt._null ? null : this.type === tt._true291node.raw = this.type.keyword292this.next()293return this.finishNode(node, "Literal")294295case tt.parenL:296return this.parseParenAndDistinguishExpression(canBeArrow)297298case tt.bracketL:299node = this.startNode()300this.next()301// check whether this is array comprehension or regular array302if (this.options.ecmaVersion >= 7 && this.type === tt._for) {303return this.parseComprehension(node, false)304}305node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos)306return this.finishNode(node, "ArrayExpression")307308case tt.braceL:309return this.parseObj(false, refShorthandDefaultPos)310311case tt._function:312node = this.startNode()313this.next()314return this.parseFunction(node, false)315316case tt._class:317return this.parseClass(this.startNode(), false)318319case tt._new:320return this.parseNew()321322case tt.backQuote:323return this.parseTemplate()324325default:326this.unexpected()327}328}329330pp.parseLiteral = function(value) {331let node = this.startNode()332node.value = value333node.raw = this.input.slice(this.start, this.end)334this.next()335return this.finishNode(node, "Literal")336}337338pp.parseParenExpression = function() {339this.expect(tt.parenL)340let val = this.parseExpression()341this.expect(tt.parenR)342return val343}344345pp.parseParenAndDistinguishExpression = function(canBeArrow) {346let startPos = this.start, startLoc = this.startLoc, val347if (this.options.ecmaVersion >= 6) {348this.next()349350if (this.options.ecmaVersion >= 7 && this.type === tt._for) {351return this.parseComprehension(this.startNodeAt(startPos, startLoc), true)352}353354let innerStartPos = this.start, innerStartLoc = this.startLoc355let exprList = [], first = true356let refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart357while (this.type !== tt.parenR) {358first ? first = false : this.expect(tt.comma)359if (this.type === tt.ellipsis) {360spreadStart = this.start361exprList.push(this.parseParenItem(this.parseRest()))362break363} else {364if (this.type === tt.parenL && !innerParenStart) {365innerParenStart = this.start366}367exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem))368}369}370let innerEndPos = this.start, innerEndLoc = this.startLoc371this.expect(tt.parenR)372373if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {374if (innerParenStart) this.unexpected(innerParenStart)375return this.parseParenArrowList(startPos, startLoc, exprList)376}377378if (!exprList.length) this.unexpected(this.lastTokStart)379if (spreadStart) this.unexpected(spreadStart)380if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)381382if (exprList.length > 1) {383val = this.startNodeAt(innerStartPos, innerStartLoc)384val.expressions = exprList385this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc)386} else {387val = exprList[0]388}389} else {390val = this.parseParenExpression()391}392393if (this.options.preserveParens) {394let par = this.startNodeAt(startPos, startLoc)395par.expression = val396return this.finishNode(par, "ParenthesizedExpression")397} else {398return val399}400}401402pp.parseParenItem = function(item) {403return item404}405406pp.parseParenArrowList = function(startPos, startLoc, exprList) {407return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)408}409410// New's precedence is slightly tricky. It must allow its argument411// to be a `[]` or dot subscript expression, but not a call — at412// least, not without wrapping it in parentheses. Thus, it uses the413414const empty = []415416pp.parseNew = function() {417let node = this.startNode()418let meta = this.parseIdent(true)419if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {420node.meta = meta421node.property = this.parseIdent(true)422if (node.property.name !== "target")423this.raise(node.property.start, "The only valid meta property for new is new.target")424return this.finishNode(node, "MetaProperty")425}426let startPos = this.start, startLoc = this.startLoc427node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true)428if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false)429else node.arguments = empty430return this.finishNode(node, "NewExpression")431}432433// Parse template expression.434435pp.parseTemplateElement = function() {436let elem = this.startNode()437elem.value = {438raw: this.input.slice(this.start, this.end),439cooked: this.value440}441this.next()442elem.tail = this.type === tt.backQuote443return this.finishNode(elem, "TemplateElement")444}445446pp.parseTemplate = function() {447let node = this.startNode()448this.next()449node.expressions = []450let curElt = this.parseTemplateElement()451node.quasis = [curElt]452while (!curElt.tail) {453this.expect(tt.dollarBraceL)454node.expressions.push(this.parseExpression())455this.expect(tt.braceR)456node.quasis.push(curElt = this.parseTemplateElement())457}458this.next()459return this.finishNode(node, "TemplateLiteral")460}461462// Parse an object literal or binding pattern.463464pp.parseObj = function(isPattern, refShorthandDefaultPos) {465let node = this.startNode(), first = true, propHash = {}466node.properties = []467this.next()468while (!this.eat(tt.braceR)) {469if (!first) {470this.expect(tt.comma)471if (this.afterTrailingComma(tt.braceR)) break472} else first = false473474let prop = this.startNode(), isGenerator, startPos, startLoc475if (this.options.ecmaVersion >= 6) {476prop.method = false477prop.shorthand = false478if (isPattern || refShorthandDefaultPos) {479startPos = this.start480startLoc = this.startLoc481}482if (!isPattern)483isGenerator = this.eat(tt.star)484}485this.parsePropertyName(prop)486this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos)487this.checkPropClash(prop, propHash)488node.properties.push(this.finishNode(prop, "Property"))489}490return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")491}492493pp.parsePropertyValue = function(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos) {494if (this.eat(tt.colon)) {495prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos)496prop.kind = "init"497} else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) {498if (isPattern) this.unexpected()499prop.kind = "init"500prop.method = true501prop.value = this.parseMethod(isGenerator)502} else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&503(prop.key.name === "get" || prop.key.name === "set") &&504(this.type != tt.comma && this.type != tt.braceR)) {505if (isGenerator || isPattern) this.unexpected()506prop.kind = prop.key.name507this.parsePropertyName(prop)508prop.value = this.parseMethod(false)509} else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {510prop.kind = "init"511if (isPattern) {512if (this.isKeyword(prop.key.name) ||513(this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) ||514(!this.options.allowReserved && this.isReservedWord(prop.key.name)))515this.raise(prop.key.start, "Binding " + prop.key.name)516prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)517} else if (this.type === tt.eq && refShorthandDefaultPos) {518if (!refShorthandDefaultPos.start)519refShorthandDefaultPos.start = this.start520prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)521} else {522prop.value = prop.key523}524prop.shorthand = true525} else this.unexpected()526}527528pp.parsePropertyName = function(prop) {529if (this.options.ecmaVersion >= 6) {530if (this.eat(tt.bracketL)) {531prop.computed = true532prop.key = this.parseMaybeAssign()533this.expect(tt.bracketR)534return prop.key535} else {536prop.computed = false537}538}539return prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true)540}541542// Initialize empty function node.543544pp.initFunction = function(node) {545node.id = null546if (this.options.ecmaVersion >= 6) {547node.generator = false548node.expression = false549}550}551552// Parse object or class method.553554pp.parseMethod = function(isGenerator) {555let node = this.startNode()556this.initFunction(node)557this.expect(tt.parenL)558node.params = this.parseBindingList(tt.parenR, false, false)559let allowExpressionBody560if (this.options.ecmaVersion >= 6) {561node.generator = isGenerator562allowExpressionBody = true563} else {564allowExpressionBody = false565}566this.parseFunctionBody(node, allowExpressionBody)567return this.finishNode(node, "FunctionExpression")568}569570// Parse arrow function expression with given parameters.571572pp.parseArrowExpression = function(node, params) {573this.initFunction(node)574node.params = this.toAssignableList(params, true)575this.parseFunctionBody(node, true)576return this.finishNode(node, "ArrowFunctionExpression")577}578579// Parse function body and check parameters.580581pp.parseFunctionBody = function(node, allowExpression) {582let isExpression = allowExpression && this.type !== tt.braceL583584if (isExpression) {585node.body = this.parseMaybeAssign()586node.expression = true587} else {588// Start a new scope with regard to labels and the `inFunction`589// flag (restore them to their old value afterwards).590let oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels591this.inFunction = true; this.inGenerator = node.generator; this.labels = []592node.body = this.parseBlock(true)593node.expression = false594this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels595}596597// If this is a strict mode function, verify that argument names598// are not repeated, and it does not try to bind the words `eval`599// or `arguments`.600if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) {601let nameHash = {}, oldStrict = this.strict602this.strict = true603if (node.id)604this.checkLVal(node.id, true)605for (let i = 0; i < node.params.length; i++)606this.checkLVal(node.params[i], true, nameHash)607this.strict = oldStrict608}609}610611// Parses a comma-separated list of expressions, and returns them as612// an array. `close` is the token type that ends the list, and613// `allowEmpty` can be turned on to allow subsequent commas with614// nothing in between them to be parsed as `null` (which is needed615// for array literals).616617pp.parseExprList = function(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {618let elts = [], first = true619while (!this.eat(close)) {620if (!first) {621this.expect(tt.comma)622if (allowTrailingComma && this.afterTrailingComma(close)) break623} else first = false624625if (allowEmpty && this.type === tt.comma) {626elts.push(null)627} else {628if (this.type === tt.ellipsis)629elts.push(this.parseSpread(refShorthandDefaultPos))630else631elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos))632}633}634return elts635}636637// Parse the next token as an identifier. If `liberal` is true (used638// when parsing properties), it will also convert keywords into639// identifiers.640641pp.parseIdent = function(liberal) {642let node = this.startNode()643if (liberal && this.options.allowReserved == "never") liberal = false644if (this.type === tt.name) {645if (!liberal &&646((!this.options.allowReserved && this.isReservedWord(this.value)) ||647(this.strict && reservedWords.strict(this.value)) &&648(this.options.ecmaVersion >= 6 ||649this.input.slice(this.start, this.end).indexOf("\\") == -1)))650this.raise(this.start, "The keyword '" + this.value + "' is reserved")651node.name = this.value652} else if (liberal && this.type.keyword) {653node.name = this.type.keyword654} else {655this.unexpected()656}657this.next()658return this.finishNode(node, "Identifier")659}660661// Parses yield expression inside generator.662663pp.parseYield = function() {664let node = this.startNode()665this.next()666if (this.type == tt.semi || this.canInsertSemicolon() || (this.type != tt.star && !this.type.startsExpr)) {667node.delegate = false668node.argument = null669} else {670node.delegate = this.eat(tt.star)671node.argument = this.parseMaybeAssign()672}673return this.finishNode(node, "YieldExpression")674}675676// Parses array and generator comprehensions.677678pp.parseComprehension = function(node, isGenerator) {679node.blocks = []680while (this.type === tt._for) {681let block = this.startNode()682this.next()683this.expect(tt.parenL)684block.left = this.parseBindingAtom()685this.checkLVal(block.left, true)686this.expectContextual("of")687block.right = this.parseExpression()688this.expect(tt.parenR)689node.blocks.push(this.finishNode(block, "ComprehensionBlock"))690}691node.filter = this.eat(tt._if) ? this.parseParenExpression() : null692node.body = this.parseExpression()693this.expect(isGenerator ? tt.parenR : tt.bracketR)694node.generator = isGenerator695return this.finishNode(node, "ComprehensionExpression")696}697698699