react / wstein / node_modules / browserify / node_modules / module-deps / node_modules / detective / node_modules / acorn / src / statement.js
80559 viewsimport {types as tt} from "./tokentype"1import {Parser} from "./state"2import {lineBreak} from "./whitespace"34const pp = Parser.prototype56// ### Statement parsing78// Parse a program. Initializes the parser, reads any number of9// statements, and wraps them in a Program node. Optionally takes a10// `program` argument. If present, the statements will be appended11// to its body instead of creating a new node.1213pp.parseTopLevel = function(node) {14let first = true15if (!node.body) node.body = []16while (this.type !== tt.eof) {17let stmt = this.parseStatement(true, true)18node.body.push(stmt)19if (first && this.isUseStrict(stmt)) this.setStrict(true)20first = false21}22this.next()23if (this.options.ecmaVersion >= 6) {24node.sourceType = this.options.sourceType25}26return this.finishNode(node, "Program")27}2829const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}3031// Parse a single statement.32//33// If expecting a statement and finding a slash operator, parse a34// regular expression literal. This is to handle cases like35// `if (foo) /blah/.exec(foo)`, where looking at the previous token36// does not help.3738pp.parseStatement = function(declaration, topLevel) {39let starttype = this.type, node = this.startNode()4041// Most types of statements are recognized by the keyword they42// start with. Many are trivial to parse, some require a bit of43// complexity.4445switch (starttype) {46case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword)47case tt._debugger: return this.parseDebuggerStatement(node)48case tt._do: return this.parseDoStatement(node)49case tt._for: return this.parseForStatement(node)50case tt._function:51if (!declaration && this.options.ecmaVersion >= 6) this.unexpected()52return this.parseFunctionStatement(node)53case tt._class:54if (!declaration) this.unexpected()55return this.parseClass(node, true)56case tt._if: return this.parseIfStatement(node)57case tt._return: return this.parseReturnStatement(node)58case tt._switch: return this.parseSwitchStatement(node)59case tt._throw: return this.parseThrowStatement(node)60case tt._try: return this.parseTryStatement(node)61case tt._let: case tt._const: if (!declaration) this.unexpected() // NOTE: falls through to _var62case tt._var: return this.parseVarStatement(node, starttype)63case tt._while: return this.parseWhileStatement(node)64case tt._with: return this.parseWithStatement(node)65case tt.braceL: return this.parseBlock()66case tt.semi: return this.parseEmptyStatement(node)67case tt._export:68case tt._import:69if (!this.options.allowImportExportEverywhere) {70if (!topLevel)71this.raise(this.start, "'import' and 'export' may only appear at the top level")72if (!this.inModule)73this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'")74}75return starttype === tt._import ? this.parseImport(node) : this.parseExport(node)7677// If the statement does not start with a statement keyword or a78// brace, it's an ExpressionStatement or LabeledStatement. We79// simply start parsing an expression, and afterwards, if the80// next token is a colon and the expression was a simple81// Identifier node, we switch to interpreting it as a label.82default:83let maybeName = this.value, expr = this.parseExpression()84if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon))85return this.parseLabeledStatement(node, maybeName, expr)86else return this.parseExpressionStatement(node, expr)87}88}8990pp.parseBreakContinueStatement = function(node, keyword) {91let isBreak = keyword == "break"92this.next()93if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null94else if (this.type !== tt.name) this.unexpected()95else {96node.label = this.parseIdent()97this.semicolon()98}99100// Verify that there is an actual destination to break or101// continue to.102for (var i = 0; i < this.labels.length; ++i) {103let lab = this.labels[i]104if (node.label == null || lab.name === node.label.name) {105if (lab.kind != null && (isBreak || lab.kind === "loop")) break106if (node.label && isBreak) break107}108}109if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword)110return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")111}112113pp.parseDebuggerStatement = function(node) {114this.next()115this.semicolon()116return this.finishNode(node, "DebuggerStatement")117}118119pp.parseDoStatement = function(node) {120this.next()121this.labels.push(loopLabel)122node.body = this.parseStatement(false)123this.labels.pop()124this.expect(tt._while)125node.test = this.parseParenExpression()126if (this.options.ecmaVersion >= 6)127this.eat(tt.semi)128else129this.semicolon()130return this.finishNode(node, "DoWhileStatement")131}132133// Disambiguating between a `for` and a `for`/`in` or `for`/`of`134// loop is non-trivial. Basically, we have to parse the init `var`135// statement or expression, disallowing the `in` operator (see136// the second parameter to `parseExpression`), and then check137// whether the next token is `in` or `of`. When there is no init138// part (semicolon immediately after the opening parenthesis), it139// is a regular `for` loop.140141pp.parseForStatement = function(node) {142this.next()143this.labels.push(loopLabel)144this.expect(tt.parenL)145if (this.type === tt.semi) return this.parseFor(node, null)146if (this.type === tt._var || this.type === tt._let || this.type === tt._const) {147let init = this.startNode(), varKind = this.type148this.next()149this.parseVar(init, true, varKind)150this.finishNode(init, "VariableDeclaration")151if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 &&152!(varKind !== tt._var && init.declarations[0].init))153return this.parseForIn(node, init)154return this.parseFor(node, init)155}156let refShorthandDefaultPos = {start: 0}157let init = this.parseExpression(true, refShorthandDefaultPos)158if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {159this.toAssignable(init)160this.checkLVal(init)161return this.parseForIn(node, init)162} else if (refShorthandDefaultPos.start) {163this.unexpected(refShorthandDefaultPos.start)164}165return this.parseFor(node, init)166}167168pp.parseFunctionStatement = function(node) {169this.next()170return this.parseFunction(node, true)171}172173pp.parseIfStatement = function(node) {174this.next()175node.test = this.parseParenExpression()176node.consequent = this.parseStatement(false)177node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null178return this.finishNode(node, "IfStatement")179}180181pp.parseReturnStatement = function(node) {182if (!this.inFunction && !this.options.allowReturnOutsideFunction)183this.raise(this.start, "'return' outside of function")184this.next()185186// In `return` (and `break`/`continue`), the keywords with187// optional arguments, we eagerly look for a semicolon or the188// possibility to insert one.189190if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null191else { node.argument = this.parseExpression(); this.semicolon() }192return this.finishNode(node, "ReturnStatement")193}194195pp.parseSwitchStatement = function(node) {196this.next()197node.discriminant = this.parseParenExpression()198node.cases = []199this.expect(tt.braceL)200this.labels.push(switchLabel)201202// Statements under must be grouped (by label) in SwitchCase203// nodes. `cur` is used to keep the node that we are currently204// adding statements to.205206for (var cur, sawDefault; this.type != tt.braceR;) {207if (this.type === tt._case || this.type === tt._default) {208let isCase = this.type === tt._case209if (cur) this.finishNode(cur, "SwitchCase")210node.cases.push(cur = this.startNode())211cur.consequent = []212this.next()213if (isCase) {214cur.test = this.parseExpression()215} else {216if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses")217sawDefault = true218cur.test = null219}220this.expect(tt.colon)221} else {222if (!cur) this.unexpected()223cur.consequent.push(this.parseStatement(true))224}225}226if (cur) this.finishNode(cur, "SwitchCase")227this.next() // Closing brace228this.labels.pop()229return this.finishNode(node, "SwitchStatement")230}231232pp.parseThrowStatement = function(node) {233this.next()234if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))235this.raise(this.lastTokEnd, "Illegal newline after throw")236node.argument = this.parseExpression()237this.semicolon()238return this.finishNode(node, "ThrowStatement")239}240241// Reused empty array added for node fields that are always empty.242243const empty = []244245pp.parseTryStatement = function(node) {246this.next()247node.block = this.parseBlock()248node.handler = null249if (this.type === tt._catch) {250let clause = this.startNode()251this.next()252this.expect(tt.parenL)253clause.param = this.parseBindingAtom()254this.checkLVal(clause.param, true)255this.expect(tt.parenR)256clause.guard = null257clause.body = this.parseBlock()258node.handler = this.finishNode(clause, "CatchClause")259}260node.guardedHandlers = empty261node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null262if (!node.handler && !node.finalizer)263this.raise(node.start, "Missing catch or finally clause")264return this.finishNode(node, "TryStatement")265}266267pp.parseVarStatement = function(node, kind) {268this.next()269this.parseVar(node, false, kind)270this.semicolon()271return this.finishNode(node, "VariableDeclaration")272}273274pp.parseWhileStatement = function(node) {275this.next()276node.test = this.parseParenExpression()277this.labels.push(loopLabel)278node.body = this.parseStatement(false)279this.labels.pop()280return this.finishNode(node, "WhileStatement")281}282283pp.parseWithStatement = function(node) {284if (this.strict) this.raise(this.start, "'with' in strict mode")285this.next()286node.object = this.parseParenExpression()287node.body = this.parseStatement(false)288return this.finishNode(node, "WithStatement")289}290291pp.parseEmptyStatement = function(node) {292this.next()293return this.finishNode(node, "EmptyStatement")294}295296pp.parseLabeledStatement = function(node, maybeName, expr) {297for (let i = 0; i < this.labels.length; ++i)298if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared")299let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null300this.labels.push({name: maybeName, kind: kind})301node.body = this.parseStatement(true)302this.labels.pop()303node.label = expr304return this.finishNode(node, "LabeledStatement")305}306307pp.parseExpressionStatement = function(node, expr) {308node.expression = expr309this.semicolon()310return this.finishNode(node, "ExpressionStatement")311}312313// Parse a semicolon-enclosed block of statements, handling `"use314// strict"` declarations when `allowStrict` is true (used for315// function bodies).316317pp.parseBlock = function(allowStrict) {318let node = this.startNode(), first = true, oldStrict319node.body = []320this.expect(tt.braceL)321while (!this.eat(tt.braceR)) {322let stmt = this.parseStatement(true)323node.body.push(stmt)324if (first && allowStrict && this.isUseStrict(stmt)) {325oldStrict = this.strict326this.setStrict(this.strict = true)327}328first = false329}330if (oldStrict === false) this.setStrict(false)331return this.finishNode(node, "BlockStatement")332}333334// Parse a regular `for` loop. The disambiguation code in335// `parseStatement` will already have parsed the init statement or336// expression.337338pp.parseFor = function(node, init) {339node.init = init340this.expect(tt.semi)341node.test = this.type === tt.semi ? null : this.parseExpression()342this.expect(tt.semi)343node.update = this.type === tt.parenR ? null : this.parseExpression()344this.expect(tt.parenR)345node.body = this.parseStatement(false)346this.labels.pop()347return this.finishNode(node, "ForStatement")348}349350// Parse a `for`/`in` and `for`/`of` loop, which are almost351// same from parser's perspective.352353pp.parseForIn = function(node, init) {354let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"355this.next()356node.left = init357node.right = this.parseExpression()358this.expect(tt.parenR)359node.body = this.parseStatement(false)360this.labels.pop()361return this.finishNode(node, type)362}363364// Parse a list of variable declarations.365366pp.parseVar = function(node, isFor, kind) {367node.declarations = []368node.kind = kind.keyword369for (;;) {370let decl = this.startNode()371this.parseVarId(decl)372if (this.eat(tt.eq)) {373decl.init = this.parseMaybeAssign(isFor)374} else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) {375this.unexpected()376} else if (decl.id.type != "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) {377this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value")378} else {379decl.init = null380}381node.declarations.push(this.finishNode(decl, "VariableDeclarator"))382if (!this.eat(tt.comma)) break383}384return node385}386387pp.parseVarId = function(decl) {388decl.id = this.parseBindingAtom()389this.checkLVal(decl.id, true)390}391392// Parse a function declaration or literal (depending on the393// `isStatement` parameter).394395pp.parseFunction = function(node, isStatement, allowExpressionBody) {396this.initFunction(node)397if (this.options.ecmaVersion >= 6)398node.generator = this.eat(tt.star)399if (isStatement || this.type === tt.name)400node.id = this.parseIdent()401this.parseFunctionParams(node)402this.parseFunctionBody(node, allowExpressionBody)403return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")404}405406pp.parseFunctionParams = function(node) {407this.expect(tt.parenL)408node.params = this.parseBindingList(tt.parenR, false, false)409}410411// Parse a class declaration or literal (depending on the412// `isStatement` parameter).413414pp.parseClass = function(node, isStatement) {415this.next()416this.parseClassId(node, isStatement)417this.parseClassSuper(node)418let classBody = this.startNode()419let hadConstructor = false420classBody.body = []421this.expect(tt.braceL)422while (!this.eat(tt.braceR)) {423if (this.eat(tt.semi)) continue424let method = this.startNode()425let isGenerator = this.eat(tt.star)426let isMaybeStatic = this.type === tt.name && this.value === "static"427this.parsePropertyName(method)428method.static = isMaybeStatic && this.type !== tt.parenL429if (method.static) {430if (isGenerator) this.unexpected()431isGenerator = this.eat(tt.star)432this.parsePropertyName(method)433}434method.kind = "method"435if (!method.computed) {436let {key} = method437let isGetSet = false438if (!isGenerator && key.type === "Identifier" && this.type !== tt.parenL && (key.name === "get" || key.name === "set")) {439isGetSet = true440method.kind = key.name441key = this.parsePropertyName(method)442}443if (!method.static && (key.type === "Identifier" && key.name === "constructor" ||444key.type === "Literal" && key.value === "constructor")) {445if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class")446if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier")447if (isGenerator) this.raise(key.start, "Constructor can't be a generator")448method.kind = "constructor"449hadConstructor = true450}451}452this.parseClassMethod(classBody, method, isGenerator)453}454node.body = this.finishNode(classBody, "ClassBody")455return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")456}457458pp.parseClassMethod = function(classBody, method, isGenerator) {459method.value = this.parseMethod(isGenerator)460classBody.body.push(this.finishNode(method, "MethodDefinition"))461}462463pp.parseClassId = function(node, isStatement) {464node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null465}466467pp.parseClassSuper = function(node) {468node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null469}470471// Parses module export declaration.472473pp.parseExport = function(node) {474this.next()475// export * from '...'476if (this.eat(tt.star)) {477this.expectContextual("from")478node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()479this.semicolon()480return this.finishNode(node, "ExportAllDeclaration")481}482if (this.eat(tt._default)) { // export default ...483let expr = this.parseMaybeAssign()484let needsSemi = true485if (expr.type == "FunctionExpression" ||486expr.type == "ClassExpression") {487needsSemi = false488if (expr.id) {489expr.type = expr.type == "FunctionExpression"490? "FunctionDeclaration"491: "ClassDeclaration"492}493}494node.declaration = expr495if (needsSemi) this.semicolon()496return this.finishNode(node, "ExportDefaultDeclaration")497}498// export var|const|let|function|class ...499if (this.shouldParseExportStatement()) {500node.declaration = this.parseStatement(true)501node.specifiers = []502node.source = null503} else { // export { x, y as z } [from '...']504node.declaration = null505node.specifiers = this.parseExportSpecifiers()506if (this.eatContextual("from")) {507node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()508} else {509node.source = null510}511this.semicolon()512}513return this.finishNode(node, "ExportNamedDeclaration")514}515516pp.shouldParseExportStatement = function() {517return this.type.keyword518}519520// Parses a comma-separated list of module exports.521522pp.parseExportSpecifiers = function() {523let nodes = [], first = true524// export { x, y as z } [from '...']525this.expect(tt.braceL)526while (!this.eat(tt.braceR)) {527if (!first) {528this.expect(tt.comma)529if (this.afterTrailingComma(tt.braceR)) break530} else first = false531532let node = this.startNode()533node.local = this.parseIdent(this.type === tt._default)534node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local535nodes.push(this.finishNode(node, "ExportSpecifier"))536}537return nodes538}539540// Parses import declaration.541542pp.parseImport = function(node) {543this.next()544// import '...'545if (this.type === tt.string) {546node.specifiers = empty547node.source = this.parseExprAtom()548node.kind = ""549} else {550node.specifiers = this.parseImportSpecifiers()551this.expectContextual("from")552node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()553}554this.semicolon()555return this.finishNode(node, "ImportDeclaration")556}557558// Parses a comma-separated list of module imports.559560pp.parseImportSpecifiers = function() {561let nodes = [], first = true562if (this.type === tt.name) {563// import defaultObj, { x, y as z } from '...'564let node = this.startNode()565node.local = this.parseIdent()566this.checkLVal(node.local, true)567nodes.push(this.finishNode(node, "ImportDefaultSpecifier"))568if (!this.eat(tt.comma)) return nodes569}570if (this.type === tt.star) {571let node = this.startNode()572this.next()573this.expectContextual("as")574node.local = this.parseIdent()575this.checkLVal(node.local, true)576nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"))577return nodes578}579this.expect(tt.braceL)580while (!this.eat(tt.braceR)) {581if (!first) {582this.expect(tt.comma)583if (this.afterTrailingComma(tt.braceR)) break584} else first = false585586let node = this.startNode()587node.imported = this.parseIdent(true)588node.local = this.eatContextual("as") ? this.parseIdent() : node.imported589this.checkLVal(node.local, true)590nodes.push(this.finishNode(node, "ImportSpecifier"))591}592return nodes593}594595596