react / react-0.13.3 / examples / basic-commonjs / node_modules / browserify / node_modules / module-deps / node_modules / detective / node_modules / acorn / src / expression.js
80762 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 (prec != null && (!noIn || this.type !== tt._in)) {149if (prec > minPrec) {150let node = this.startNodeAt(leftStartPos, leftStartLoc)151node.left = left152node.operator = this.value153let op = this.type154this.next()155let startPos = this.start, startLoc = this.startLoc156node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn)157this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression")158return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)159}160}161return left162}163164// Parse unary operators, both prefix and postfix.165166pp.parseMaybeUnary = function(refShorthandDefaultPos) {167if (this.type.prefix) {168let node = this.startNode(), update = this.type === tt.incDec169node.operator = this.value170node.prefix = true171this.next()172node.argument = this.parseMaybeUnary()173if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)174if (update) this.checkLVal(node.argument)175else if (this.strict && node.operator === "delete" &&176node.argument.type === "Identifier")177this.raise(node.start, "Deleting local variable in strict mode")178return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")179}180let startPos = this.start, startLoc = this.startLoc181let expr = this.parseExprSubscripts(refShorthandDefaultPos)182if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr183while (this.type.postfix && !this.canInsertSemicolon()) {184let node = this.startNodeAt(startPos, startLoc)185node.operator = this.value186node.prefix = false187node.argument = expr188this.checkLVal(expr)189this.next()190expr = this.finishNode(node, "UpdateExpression")191}192return expr193}194195// Parse call, dot, and `[]`-subscript expressions.196197pp.parseExprSubscripts = function(refShorthandDefaultPos) {198let startPos = this.start, startLoc = this.startLoc199let expr = this.parseExprAtom(refShorthandDefaultPos)200if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr201return this.parseSubscripts(expr, startPos, startLoc)202}203204pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {205for (;;) {206if (this.eat(tt.dot)) {207let node = this.startNodeAt(startPos, startLoc)208node.object = base209node.property = this.parseIdent(true)210node.computed = false211base = this.finishNode(node, "MemberExpression")212} else if (this.eat(tt.bracketL)) {213let node = this.startNodeAt(startPos, startLoc)214node.object = base215node.property = this.parseExpression()216node.computed = true217this.expect(tt.bracketR)218base = this.finishNode(node, "MemberExpression")219} else if (!noCalls && this.eat(tt.parenL)) {220let node = this.startNodeAt(startPos, startLoc)221node.callee = base222node.arguments = this.parseExprList(tt.parenR, false)223base = this.finishNode(node, "CallExpression")224} else if (this.type === tt.backQuote) {225let node = this.startNodeAt(startPos, startLoc)226node.tag = base227node.quasi = this.parseTemplate()228base = this.finishNode(node, "TaggedTemplateExpression")229} else {230return base231}232}233}234235// Parse an atomic expression — either a single token that is an236// expression, an expression started by a keyword like `function` or237// `new`, or an expression wrapped in punctuation like `()`, `[]`,238// or `{}`.239240pp.parseExprAtom = function(refShorthandDefaultPos) {241let node, canBeArrow = this.potentialArrowAt == this.start242switch (this.type) {243case tt._this:244case tt._super:245let type = this.type === tt._this ? "ThisExpression" : "Super"246node = this.startNode()247this.next()248return this.finishNode(node, type)249250case tt._yield:251if (this.inGenerator) this.unexpected()252253case tt.name:254let startPos = this.start, startLoc = this.startLoc255let id = this.parseIdent(this.type !== tt.name)256if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow))257return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id])258return id259260case tt.regexp:261let value = this.value262node = this.parseLiteral(value.value)263node.regex = {pattern: value.pattern, flags: value.flags}264return node265266case tt.num: case tt.string:267return this.parseLiteral(this.value)268269case tt._null: case tt._true: case tt._false:270node = this.startNode()271node.value = this.type === tt._null ? null : this.type === tt._true272node.raw = this.type.keyword273this.next()274return this.finishNode(node, "Literal")275276case tt.parenL:277return this.parseParenAndDistinguishExpression(canBeArrow)278279case tt.bracketL:280node = this.startNode()281this.next()282// check whether this is array comprehension or regular array283if (this.options.ecmaVersion >= 7 && this.type === tt._for) {284return this.parseComprehension(node, false)285}286node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos)287return this.finishNode(node, "ArrayExpression")288289case tt.braceL:290return this.parseObj(false, refShorthandDefaultPos)291292case tt._function:293node = this.startNode()294this.next()295return this.parseFunction(node, false)296297case tt._class:298return this.parseClass(this.startNode(), false)299300case tt._new:301return this.parseNew()302303case tt.backQuote:304return this.parseTemplate()305306default:307this.unexpected()308}309}310311pp.parseLiteral = function(value) {312let node = this.startNode()313node.value = value314node.raw = this.input.slice(this.start, this.end)315this.next()316return this.finishNode(node, "Literal")317}318319pp.parseParenExpression = function() {320this.expect(tt.parenL)321let val = this.parseExpression()322this.expect(tt.parenR)323return val324}325326pp.parseParenAndDistinguishExpression = function(canBeArrow) {327let startPos = this.start, startLoc = this.startLoc, val328if (this.options.ecmaVersion >= 6) {329this.next()330331if (this.options.ecmaVersion >= 7 && this.type === tt._for) {332return this.parseComprehension(this.startNodeAt(startPos, startLoc), true)333}334335let innerStartPos = this.start, innerStartLoc = this.startLoc336let exprList = [], first = true337let refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart338while (this.type !== tt.parenR) {339first ? first = false : this.expect(tt.comma)340if (this.type === tt.ellipsis) {341spreadStart = this.start342exprList.push(this.parseParenItem(this.parseRest()))343break344} else {345if (this.type === tt.parenL && !innerParenStart) {346innerParenStart = this.start347}348exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem))349}350}351let innerEndPos = this.start, innerEndLoc = this.startLoc352this.expect(tt.parenR)353354if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {355if (innerParenStart) this.unexpected(innerParenStart)356return this.parseParenArrowList(startPos, startLoc, exprList)357}358359if (!exprList.length) this.unexpected(this.lastTokStart)360if (spreadStart) this.unexpected(spreadStart)361if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)362363if (exprList.length > 1) {364val = this.startNodeAt(innerStartPos, innerStartLoc)365val.expressions = exprList366this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc)367} else {368val = exprList[0]369}370} else {371val = this.parseParenExpression()372}373374if (this.options.preserveParens) {375let par = this.startNodeAt(startPos, startLoc)376par.expression = val377return this.finishNode(par, "ParenthesizedExpression")378} else {379return val380}381}382383pp.parseParenItem = function(item) {384return item385}386387pp.parseParenArrowList = function(startPos, startLoc, exprList) {388return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)389}390391// New's precedence is slightly tricky. It must allow its argument392// to be a `[]` or dot subscript expression, but not a call — at393// least, not without wrapping it in parentheses. Thus, it uses the394395const empty = []396397pp.parseNew = function() {398let node = this.startNode()399let meta = this.parseIdent(true)400if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {401node.meta = meta402node.property = this.parseIdent(true)403if (node.property.name !== "target")404this.raise(node.property.start, "The only valid meta property for new is new.target")405return this.finishNode(node, "MetaProperty")406}407let startPos = this.start, startLoc = this.startLoc408node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true)409if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false)410else node.arguments = empty411return this.finishNode(node, "NewExpression")412}413414// Parse template expression.415416pp.parseTemplateElement = function() {417let elem = this.startNode()418elem.value = {419raw: this.input.slice(this.start, this.end),420cooked: this.value421}422this.next()423elem.tail = this.type === tt.backQuote424return this.finishNode(elem, "TemplateElement")425}426427pp.parseTemplate = function() {428let node = this.startNode()429this.next()430node.expressions = []431let curElt = this.parseTemplateElement()432node.quasis = [curElt]433while (!curElt.tail) {434this.expect(tt.dollarBraceL)435node.expressions.push(this.parseExpression())436this.expect(tt.braceR)437node.quasis.push(curElt = this.parseTemplateElement())438}439this.next()440return this.finishNode(node, "TemplateLiteral")441}442443// Parse an object literal or binding pattern.444445pp.parseObj = function(isPattern, refShorthandDefaultPos) {446let node = this.startNode(), first = true, propHash = {}447node.properties = []448this.next()449while (!this.eat(tt.braceR)) {450if (!first) {451this.expect(tt.comma)452if (this.afterTrailingComma(tt.braceR)) break453} else first = false454455let prop = this.startNode(), isGenerator, startPos, startLoc456if (this.options.ecmaVersion >= 6) {457prop.method = false458prop.shorthand = false459if (isPattern || refShorthandDefaultPos) {460startPos = this.start461startLoc = this.startLoc462}463if (!isPattern)464isGenerator = this.eat(tt.star)465}466this.parsePropertyName(prop)467this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos)468this.checkPropClash(prop, propHash)469node.properties.push(this.finishNode(prop, "Property"))470}471return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")472}473474pp.parsePropertyValue = function(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos) {475if (this.eat(tt.colon)) {476prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos)477prop.kind = "init"478} else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) {479if (isPattern) this.unexpected()480prop.kind = "init"481prop.method = true482prop.value = this.parseMethod(isGenerator)483} else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&484(prop.key.name === "get" || prop.key.name === "set") &&485(this.type != tt.comma && this.type != tt.braceR)) {486if (isGenerator || isPattern) this.unexpected()487prop.kind = prop.key.name488this.parsePropertyName(prop)489prop.value = this.parseMethod(false)490} else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {491prop.kind = "init"492if (isPattern) {493if (this.isKeyword(prop.key.name) ||494(this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) ||495(!this.options.allowReserved && this.isReservedWord(prop.key.name)))496this.raise(prop.key.start, "Binding " + prop.key.name)497prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)498} else if (this.type === tt.eq && refShorthandDefaultPos) {499if (!refShorthandDefaultPos.start)500refShorthandDefaultPos.start = this.start501prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)502} else {503prop.value = prop.key504}505prop.shorthand = true506} else this.unexpected()507}508509pp.parsePropertyName = function(prop) {510if (this.options.ecmaVersion >= 6) {511if (this.eat(tt.bracketL)) {512prop.computed = true513prop.key = this.parseMaybeAssign()514this.expect(tt.bracketR)515return prop.key516} else {517prop.computed = false518}519}520return prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true)521}522523// Initialize empty function node.524525pp.initFunction = function(node) {526node.id = null527if (this.options.ecmaVersion >= 6) {528node.generator = false529node.expression = false530}531}532533// Parse object or class method.534535pp.parseMethod = function(isGenerator) {536let node = this.startNode()537this.initFunction(node)538this.expect(tt.parenL)539node.params = this.parseBindingList(tt.parenR, false, false)540let allowExpressionBody541if (this.options.ecmaVersion >= 6) {542node.generator = isGenerator543allowExpressionBody = true544} else {545allowExpressionBody = false546}547this.parseFunctionBody(node, allowExpressionBody)548return this.finishNode(node, "FunctionExpression")549}550551// Parse arrow function expression with given parameters.552553pp.parseArrowExpression = function(node, params) {554this.initFunction(node)555node.params = this.toAssignableList(params, true)556this.parseFunctionBody(node, true)557return this.finishNode(node, "ArrowFunctionExpression")558}559560// Parse function body and check parameters.561562pp.parseFunctionBody = function(node, allowExpression) {563let isExpression = allowExpression && this.type !== tt.braceL564565if (isExpression) {566node.body = this.parseMaybeAssign()567node.expression = true568} else {569// Start a new scope with regard to labels and the `inFunction`570// flag (restore them to their old value afterwards).571let oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels572this.inFunction = true; this.inGenerator = node.generator; this.labels = []573node.body = this.parseBlock(true)574node.expression = false575this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels576}577578// If this is a strict mode function, verify that argument names579// are not repeated, and it does not try to bind the words `eval`580// or `arguments`.581if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) {582let nameHash = {}, oldStrict = this.strict583this.strict = true584if (node.id)585this.checkLVal(node.id, true)586for (let i = 0; i < node.params.length; i++)587this.checkLVal(node.params[i], true, nameHash)588this.strict = oldStrict589}590}591592// Parses a comma-separated list of expressions, and returns them as593// an array. `close` is the token type that ends the list, and594// `allowEmpty` can be turned on to allow subsequent commas with595// nothing in between them to be parsed as `null` (which is needed596// for array literals).597598pp.parseExprList = function(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {599let elts = [], first = true600while (!this.eat(close)) {601if (!first) {602this.expect(tt.comma)603if (allowTrailingComma && this.afterTrailingComma(close)) break604} else first = false605606if (allowEmpty && this.type === tt.comma) {607elts.push(null)608} else {609if (this.type === tt.ellipsis)610elts.push(this.parseSpread(refShorthandDefaultPos))611else612elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos))613}614}615return elts616}617618// Parse the next token as an identifier. If `liberal` is true (used619// when parsing properties), it will also convert keywords into620// identifiers.621622pp.parseIdent = function(liberal) {623let node = this.startNode()624if (liberal && this.options.allowReserved == "never") liberal = false625if (this.type === tt.name) {626if (!liberal &&627((!this.options.allowReserved && this.isReservedWord(this.value)) ||628(this.strict && reservedWords.strict(this.value)) &&629(this.options.ecmaVersion >= 6 ||630this.input.slice(this.start, this.end).indexOf("\\") == -1)))631this.raise(this.start, "The keyword '" + this.value + "' is reserved")632node.name = this.value633} else if (liberal && this.type.keyword) {634node.name = this.type.keyword635} else {636this.unexpected()637}638this.next()639return this.finishNode(node, "Identifier")640}641642// Parses yield expression inside generator.643644pp.parseYield = function() {645let node = this.startNode()646this.next()647if (this.type == tt.semi || this.canInsertSemicolon() || (this.type != tt.star && !this.type.startsExpr)) {648node.delegate = false649node.argument = null650} else {651node.delegate = this.eat(tt.star)652node.argument = this.parseMaybeAssign()653}654return this.finishNode(node, "YieldExpression")655}656657// Parses array and generator comprehensions.658659pp.parseComprehension = function(node, isGenerator) {660node.blocks = []661while (this.type === tt._for) {662let block = this.startNode()663this.next()664this.expect(tt.parenL)665block.left = this.parseBindingAtom()666this.checkLVal(block.left, true)667this.expectContextual("of")668block.right = this.parseExpression()669this.expect(tt.parenR)670node.blocks.push(this.finishNode(block, "ComprehensionBlock"))671}672node.filter = this.eat(tt._if) ? this.parseParenExpression() : null673node.body = this.parseExpression()674this.expect(isGenerator ? tt.parenR : tt.bracketR)675node.generator = isGenerator676return this.finishNode(node, "ComprehensionExpression")677}678679680