// Acorn: Loose parser // // This module provides an alternative parser (`parse_dammit`) that // exposes that same interface as `parse`, but will try to parse // anything as JavaScript, repairing syntax error the best it can. // There are circumstances in which it will raise an error and give // up, but they are very rare. The resulting AST will be a mostly // valid JavaScript AST (as per the [Mozilla parser API][api], except // that: // // - Return outside functions is allowed // // - Label consistency (no conflicts, break only to existing labels) // is not enforced. // // - Bogus Identifier nodes with a name of `"✖"` are inserted whenever // the parser got too confused to return anything meaningful. // // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API // // The expected use for this is to *first* try `acorn.parse`, and only // if that fails switch to `parse_dammit`. The loose parser might // parse badly indented code incorrectly, so **don't** use it as // your default parser. // // Quite a lot of acorn.js is duplicated here. The alternative was to // add a *lot* of extra cruft to that file, making it less readable // and slower. Copying and editing the code allowed me to make // invasive changes and simplifications without creating a complicated // tangle. (function(root, mod) { if (typeof exports == "object" && typeof module == "object") return mod(exports, require("./acorn")); // CommonJS if (typeof define == "function" && define.amd) return define(["exports", "./acorn"], mod); // AMD mod(root.acorn || (root.acorn = {}), root.acorn); // Plain browser env })(this, function(exports, acorn) { "use strict"; var tt = acorn.tokTypes; var options, input, fetchToken, context; acorn.defaultOptions.tabSize = 4; exports.parse_dammit = function(inpt, opts) { if (!opts) opts = {}; input = String(inpt); fetchToken = acorn.tokenize(input, opts); options = fetchToken.options; sourceFile = options.sourceFile || null; context = []; nextLineStart = 0; ahead.length = 0; next(); return parseTopLevel(); }; var lastEnd, token = {start: 0, end: 0}, ahead = []; var curLineStart, nextLineStart, curIndent, lastEndLoc, sourceFile; function next() { lastEnd = token.end; if (options.locations) lastEndLoc = token.loc && token.loc.end; token = ahead.shift() || readToken(); if (options.onToken) options.onToken(token); if (token.start >= nextLineStart) { while (token.start >= nextLineStart) { curLineStart = nextLineStart; nextLineStart = lineEnd(curLineStart) + 1; } curIndent = indentationAfter(curLineStart); } } function readToken() { for (;;) { try { var tok = fetchToken(); if (tok.type === tt.dot && input.substr(tok.end, 1) === '.' && options.ecmaVersion >= 6) { tok = fetchToken(); tok.start--; tok.type = tt.ellipsis; } return tok; } catch(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; if (/unterminated/i.test(msg)) { pos = lineEnd(e.pos + 1); if (/string/.test(msg)) { replace = {start: e.pos, end: pos, type: tt.string, value: input.slice(e.pos + 1, pos)}; } else if (/regular expr/i.test(msg)) { var re = 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: input.slice(e.pos, pos)}; } else if (/comment/.test(msg)) { replace = fetchToken.current(); } else { replace = false; } } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number/i.test(msg)) { while (pos < input.length && !isSpace(input.charCodeAt(pos))) ++pos; } else if (/character escape|expected hexadecimal/i.test(msg)) { while (pos < input.length) { var ch = input.charCodeAt(pos++); if (ch === 34 || ch === 39 || isNewline(ch)) break; } } else if (/unexpected character/i.test(msg)) { pos++; replace = false; } else if (/regular expression/i.test(msg)) { replace = true; } else { throw e; } resetTo(pos); if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"}; if (replace) { if (options.locations) { replace.loc = new SourceLocation(acorn.getLineInfo(input, replace.start)); replace.loc.end = acorn.getLineInfo(input, replace.end); } return replace; } } } } function resetTo(pos) { for (;;) { try { var ch = input.charAt(pos - 1); var reAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) || /[enwfd]/.test(ch) && /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(input.slice(pos - 10, pos)); return fetchToken.jumpTo(pos, reAllowed); } catch(e) { if (!(e instanceof SyntaxError && /unterminated comment/i.test(e.message))) throw e; pos = lineEnd(e.pos + 1); if (pos >= input.length) return; } } } function lookAhead(n) { while (n > ahead.length) ahead.push(readToken()); return ahead[n-1]; } var newline = /[\n\r\u2028\u2029]/; function isNewline(ch) { return ch === 10 || ch === 13 || ch === 8232 || ch === 8329; } function isSpace(ch) { return (ch < 14 && ch > 8) || ch === 32 || ch === 160 || isNewline(ch); } function pushCx() { context.push(curIndent); } function popCx() { curIndent = context.pop(); } function lineEnd(pos) { while (pos < input.length && !isNewline(input.charCodeAt(pos))) ++pos; return pos; } function indentationAfter(pos) { for (var count = 0;; ++pos) { var ch = input.charCodeAt(pos); if (ch === 32) ++count; else if (ch === 9) count += options.tabSize; else return count; } } function closes(closeTok, indent, line, blockHeuristic) { if (token.type === closeTok || token.type === tt.eof) return true; if (line != curLineStart && curIndent < indent && tokenStartsLine() && (!blockHeuristic || nextLineStart >= input.length || indentationAfter(nextLineStart) < indent)) return true; return false; } function tokenStartsLine() { for (var p = token.start - 1; p >= curLineStart; --p) { var ch = input.charCodeAt(p); if (ch !== 9 && ch !== 32) return false; } return true; } function Node(start) { this.type = null; this.start = start; this.end = null; } Node.prototype = acorn.Node.prototype; function SourceLocation(start) { this.start = start || token.loc.start || {line: 1, column: 0}; this.end = null; if (sourceFile !== null) this.source = sourceFile; } function startNode() { var node = new Node(token.start); if (options.locations) node.loc = new SourceLocation(); if (options.directSourceFile) node.sourceFile = options.directSourceFile; if (options.ranges) node.range = [token.start, 0]; return node; } function storeCurrentPos() { return options.locations ? [token.start, token.loc.start] : token.start; } function startNodeAt(pos) { var node; if (options.locations) { node = new Node(pos[0]); node.loc = new SourceLocation(pos[1]); pos = pos[0]; } else { node = new Node(pos); } if (options.directSourceFile) node.sourceFile = options.directSourceFile; if (options.ranges) node.range = [pos, 0]; return node; } function finishNode(node, type) { node.type = type; node.end = lastEnd; if (options.locations) node.loc.end = lastEndLoc; if (options.ranges) node.range[1] = lastEnd; return node; } function dummyIdent() { var dummy = startNode(); dummy.name = "✖"; return finishNode(dummy, "Identifier"); } function isDummy(node) { return node.name == "✖"; } function eat(type) { if (token.type === type) { next(); return true; } else { return false; } } function isContextual(name) { return token.type === tt.name && token.value === name; } function eatContextual(name) { return token.value === name && eat(tt.name); } function canInsertSemicolon() { return (token.type === tt.eof || token.type === tt.braceR || newline.test(input.slice(lastEnd, token.start))); } function semicolon() { return eat(tt.semi); } function expect(type) { if (eat(type)) return true; if (lookAhead(1).type == type) { next(); next(); return true; } if (lookAhead(2).type == type) { next(); next(); next(); return true; } } function checkLVal(expr) { if (!expr) return expr; switch (expr.type) { case "Identifier": case "MemberExpression": case "ObjectPattern": case "ArrayPattern": case "RestElement": case "AssignmentPattern": return expr; default: return dummyIdent(); } } function parseTopLevel() { var node = startNodeAt(options.locations ? [0, acorn.getLineInfo(input, 0)] : 0); node.body = []; while (token.type !== tt.eof) node.body.push(parseStatement()); lastEnd = token.end; lastEndLoc = token.loc && token.loc.end; return finishNode(node, "Program"); } function parseStatement() { var starttype = token.type, node = startNode(); switch (starttype) { case tt._break: case tt._continue: next(); var isBreak = starttype === tt._break; if (semicolon() || canInsertSemicolon()) { node.label = null; } else { node.label = token.type === tt.name ? parseIdent() : null; semicolon(); } return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); case tt._debugger: next(); semicolon(); return finishNode(node, "DebuggerStatement"); case tt._do: next(); node.body = parseStatement(); node.test = eat(tt._while) ? parseParenExpression() : dummyIdent(); semicolon(); return finishNode(node, "DoWhileStatement"); case tt._for: next(); pushCx(); expect(tt.parenL); if (token.type === tt.semi) return parseFor(node, null); if (token.type === tt._var || token.type === tt._let) { var init = parseVar(true); if (init.declarations.length === 1 && (token.type === tt._in || isContextual("of"))) { return parseForIn(node, init); } return parseFor(node, init); } var init = parseExpression(true); if (token.type === tt._in || isContextual("of")) { return parseForIn(node, toAssignable(init)); } return parseFor(node, init); case tt._function: next(); return parseFunction(node, true); case tt._if: next(); node.test = parseParenExpression(); node.consequent = parseStatement(); node.alternate = eat(tt._else) ? parseStatement() : null; return finishNode(node, "IfStatement"); case tt._return: next(); if (eat(tt.semi) || canInsertSemicolon()) node.argument = null; else { node.argument = parseExpression(); semicolon(); } return finishNode(node, "ReturnStatement"); case tt._switch: var blockIndent = curIndent, line = curLineStart; next(); node.discriminant = parseParenExpression(); node.cases = []; pushCx(); expect(tt.braceL); for (var cur; !closes(tt.braceR, blockIndent, line, true);) { if (token.type === tt._case || token.type === tt._default) { var isCase = token.type === tt._case; if (cur) finishNode(cur, "SwitchCase"); node.cases.push(cur = startNode()); cur.consequent = []; next(); if (isCase) cur.test = parseExpression(); else cur.test = null; expect(tt.colon); } else { if (!cur) { node.cases.push(cur = startNode()); cur.consequent = []; cur.test = null; } cur.consequent.push(parseStatement()); } } if (cur) finishNode(cur, "SwitchCase"); popCx(); eat(tt.braceR); return finishNode(node, "SwitchStatement"); case tt._throw: next(); node.argument = parseExpression(); semicolon(); return finishNode(node, "ThrowStatement"); case tt._try: next(); node.block = parseBlock(); node.handler = null; if (token.type === tt._catch) { var clause = startNode(); next(); expect(tt.parenL); clause.param = toAssignable(parseExprAtom()); expect(tt.parenR); clause.guard = null; clause.body = parseBlock(); node.handler = finishNode(clause, "CatchClause"); } node.finalizer = eat(tt._finally) ? parseBlock() : null; if (!node.handler && !node.finalizer) return node.block; return finishNode(node, "TryStatement"); case tt._var: case tt._let: case tt._const: return parseVar(); case tt._while: next(); node.test = parseParenExpression(); node.body = parseStatement(); return finishNode(node, "WhileStatement"); case tt._with: next(); node.object = parseParenExpression(); node.body = parseStatement(); return finishNode(node, "WithStatement"); case tt.braceL: return parseBlock(); case tt.semi: next(); return finishNode(node, "EmptyStatement"); case tt._class: return parseObj(true, true); case tt._import: return parseImport(); case tt._export: return parseExport(); default: var expr = parseExpression(); if (isDummy(expr)) { next(); if (token.type === tt.eof) return finishNode(node, "EmptyStatement"); return parseStatement(); } else if (starttype === tt.name && expr.type === "Identifier" && eat(tt.colon)) { node.body = parseStatement(); node.label = expr; return finishNode(node, "LabeledStatement"); } else { node.expression = expr; semicolon(); return finishNode(node, "ExpressionStatement"); } } } function parseBlock() { var node = startNode(); pushCx(); expect(tt.braceL); var blockIndent = curIndent, line = curLineStart; node.body = []; while (!closes(tt.braceR, blockIndent, line, true)) node.body.push(parseStatement()); popCx(); eat(tt.braceR); return finishNode(node, "BlockStatement"); } function parseFor(node, init) { node.init = init; node.test = node.update = null; if (eat(tt.semi) && token.type !== tt.semi) node.test = parseExpression(); if (eat(tt.semi) && token.type !== tt.parenR) node.update = parseExpression(); popCx(); expect(tt.parenR); node.body = parseStatement(); return finishNode(node, "ForStatement"); } function parseForIn(node, init) { var type = token.type === tt._in ? "ForInStatement" : "ForOfStatement"; next(); node.left = init; node.right = parseExpression(); popCx(); expect(tt.parenR); node.body = parseStatement(); return finishNode(node, type); } function parseVar(noIn) { var node = startNode(); node.kind = token.type.keyword; next(); node.declarations = []; do { var decl = startNode(); decl.id = options.ecmaVersion >= 6 ? toAssignable(parseExprAtom()) : parseIdent(); decl.init = eat(tt.eq) ? parseMaybeAssign(noIn) : null; node.declarations.push(finishNode(decl, "VariableDeclarator")); } while (eat(tt.comma)); if (!node.declarations.length) { var decl = startNode(); decl.id = dummyIdent(); node.declarations.push(finishNode(decl, "VariableDeclarator")); } if (!noIn) semicolon(); return finishNode(node, "VariableDeclaration"); } function parseExpression(noIn) { var start = storeCurrentPos(); var expr = parseMaybeAssign(noIn); if (token.type === tt.comma) { var node = startNodeAt(start); node.expressions = [expr]; while (eat(tt.comma)) node.expressions.push(parseMaybeAssign(noIn)); return finishNode(node, "SequenceExpression"); } return expr; } function parseParenExpression() { pushCx(); expect(tt.parenL); var val = parseExpression(); popCx(); expect(tt.parenR); return val; } function parseMaybeAssign(noIn) { var start = storeCurrentPos(); var left = parseMaybeConditional(noIn); if (token.type.isAssign) { var node = startNodeAt(start); node.operator = token.value; node.left = token.type === tt.eq ? toAssignable(left) : checkLVal(left); next(); node.right = parseMaybeAssign(noIn); return finishNode(node, "AssignmentExpression"); } return left; } function parseMaybeConditional(noIn) { var start = storeCurrentPos(); var expr = parseExprOps(noIn); if (eat(tt.question)) { var node = startNodeAt(start); node.test = expr; node.consequent = parseMaybeAssign(); node.alternate = expect(tt.colon) ? parseMaybeAssign(noIn) : dummyIdent(); return finishNode(node, "ConditionalExpression"); } return expr; } function parseExprOps(noIn) { var start = storeCurrentPos(); var indent = curIndent, line = curLineStart; return parseExprOp(parseMaybeUnary(noIn), start, -1, noIn, indent, line); } function parseExprOp(left, start, minPrec, noIn, indent, line) { if (curLineStart != line && curIndent < indent && tokenStartsLine()) return left; var prec = token.type.binop; if (prec != null && (!noIn || token.type !== tt._in)) { if (prec > minPrec) { var node = startNodeAt(start); node.left = left; node.operator = token.value; next(); if (curLineStart != line && curIndent < indent && tokenStartsLine()) { node.right = dummyIdent(); } else { var rightStart = storeCurrentPos(); node.right = parseExprOp(parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line); } finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression"); return parseExprOp(node, start, minPrec, noIn, indent, line); } } return left; } function parseMaybeUnary(noIn) { if (token.type.prefix) { var node = startNode(), update = token.type.isUpdate; node.operator = token.value; node.prefix = true; next(); node.argument = parseMaybeUnary(noIn); if (update) node.argument = checkLVal(node.argument); return finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); } else if (token.type === tt.ellipsis) { var node = startNode(); next(); node.argument = parseMaybeUnary(noIn); return finishNode(node, "SpreadElement"); } var start = storeCurrentPos(); var expr = parseExprSubscripts(); while (token.type.postfix && !canInsertSemicolon()) { var node = startNodeAt(start); node.operator = token.value; node.prefix = false; node.argument = checkLVal(expr); next(); expr = finishNode(node, "UpdateExpression"); } return expr; } function parseExprSubscripts() { var start = storeCurrentPos(); return parseSubscripts(parseExprAtom(), start, false, curIndent, curLineStart); } function parseSubscripts(base, start, noCalls, startIndent, line) { for (;;) { if (curLineStart != line && curIndent <= startIndent && tokenStartsLine()) { if (token.type == tt.dot && curIndent == startIndent) --startIndent; else return base; } if (eat(tt.dot)) { var node = startNodeAt(start); node.object = base; if (curLineStart != line && curIndent <= startIndent && tokenStartsLine()) node.property = dummyIdent(); else node.property = parsePropertyAccessor() || dummyIdent(); node.computed = false; base = finishNode(node, "MemberExpression"); } else if (token.type == tt.bracketL) { pushCx(); next(); var node = startNodeAt(start); node.object = base; node.property = parseExpression(); node.computed = true; popCx(); expect(tt.bracketR); base = finishNode(node, "MemberExpression"); } else if (!noCalls && token.type == tt.parenL) { pushCx(); var node = startNodeAt(start); node.callee = base; node.arguments = parseExprList(tt.parenR); base = finishNode(node, "CallExpression"); } else if (token.type == tt.backQuote) { var node = startNodeAt(start); node.tag = base; node.quasi = parseTemplate(); base = finishNode(node, "TaggedTemplateExpression"); } else { return base; } } } function parseExprAtom() { switch (token.type) { case tt._this: var node = startNode(); next(); return finishNode(node, "ThisExpression"); case tt.name: var start = storeCurrentPos(); var id = parseIdent(); return eat(tt.arrow) ? parseArrowExpression(startNodeAt(start), [id]) : id; case tt.regexp: var node = startNode(); var val = token.value; node.regex = {pattern: val.pattern, flags: val.flags}; node.value = val.value; node.raw = input.slice(token.start, token.end); next(); return finishNode(node, "Literal"); case tt.num: case tt.string: var node = startNode(); node.value = token.value; node.raw = input.slice(token.start, token.end); next(); return finishNode(node, "Literal"); case tt._null: case tt._true: case tt._false: var node = startNode(); node.value = token.type.atomValue; node.raw = token.type.keyword; next(); return finishNode(node, "Literal"); case tt.parenL: var start = storeCurrentPos(); next(); var val = parseExpression(); expect(tt.parenR); if (eat(tt.arrow)) { return parseArrowExpression(startNodeAt(start), val.expressions || (isDummy(val) ? [] : [val])); } if (options.preserveParens) { var par = startNodeAt(start); par.expression = val; val = finishNode(par, "ParenthesizedExpression"); } return val; case tt.bracketL: var node = startNode(); pushCx(); node.elements = parseExprList(tt.bracketR, true); return finishNode(node, "ArrayExpression"); case tt.braceL: return parseObj(); case tt._class: return parseObj(true); case tt._function: var node = startNode(); next(); return parseFunction(node, false); case tt._new: return parseNew(); case tt._yield: var node = startNode(); next(); if (semicolon() || canInsertSemicolon()) { node.delegate = false; node.argument = null; } else { node.delegate = eat(tt.star); node.argument = parseMaybeAssign(); } return finishNode(node, "YieldExpression"); case tt.backQuote: return parseTemplate(); default: return dummyIdent(); } } function parseNew() { var node = startNode(), startIndent = curIndent, line = curLineStart; next(); var start = storeCurrentPos(); node.callee = parseSubscripts(parseExprAtom(), start, true, startIndent, line); if (token.type == tt.parenL) { pushCx(); node.arguments = parseExprList(tt.parenR); } else { node.arguments = []; } return finishNode(node, "NewExpression"); } function parseTemplateElement() { var elem = startNode(); elem.value = { raw: input.slice(token.start, token.end), cooked: token.value }; next(); elem.tail = token.type === tt.backQuote; return finishNode(elem, "TemplateElement"); } function parseTemplate() { var node = startNode(); next(); node.expressions = []; var curElt = parseTemplateElement(); node.quasis = [curElt]; while (!curElt.tail) { next(); node.expressions.push(parseExpression()); if (expect(tt.braceR)) { curElt = parseTemplateElement(); } else { curElt = startNode(); curElt.value = {cooked: '', raw: ''}; curElt.tail = true; } node.quasis.push(curElt); } expect(tt.backQuote); return finishNode(node, "TemplateLiteral"); } function parseObj(isClass, isStatement) { var node = startNode(); if (isClass) { next(); if (token.type === tt.name) node.id = parseIdent(); else if (isStatement) node.id = dummyIdent(); else node.id = null; node.superClass = eat(tt._extends) ? parseExpression() : null; node.body = startNode(); node.body.body = []; } else { node.properties = []; } pushCx(); var indent = curIndent + 1, line = curLineStart; eat(tt.braceL); if (curIndent + 1 < indent) { indent = curIndent; line = curLineStart; } while (!closes(tt.braceR, indent, line)) { if (isClass && semicolon()) continue; var prop = startNode(), isGenerator, start; if (options.ecmaVersion >= 6) { if (isClass) { prop['static'] = false; } else { start = storeCurrentPos(); prop.method = false; prop.shorthand = false; } isGenerator = eat(tt.star); } parsePropertyName(prop); if (isDummy(prop.key)) { if (isDummy(parseMaybeAssign())) next(); eat(tt.comma); continue; } if (isClass) { if (prop.key.type === "Identifier" && !prop.computed && prop.key.name === "static" && (token.type != tt.parenL && token.type != tt.braceL)) { prop['static'] = true; isGenerator = eat(tt.star); parsePropertyName(prop); } else { prop['static'] = false; } } if (!isClass && eat(tt.colon)) { prop.kind = "init"; prop.value = parseMaybeAssign(); } else if (options.ecmaVersion >= 6 && (token.type === tt.parenL || token.type === tt.braceL)) { if (isClass) { prop.kind = ""; } else { prop.kind = "init"; prop.method = true; } prop.value = parseMethod(isGenerator); } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" && !prop.computed && (prop.key.name === "get" || prop.key.name === "set") && (token.type != tt.comma && token.type != tt.braceR)) { prop.kind = prop.key.name; parsePropertyName(prop); prop.value = parseMethod(false); } else if (isClass) { prop.kind = ""; prop.value = parseMethod(isGenerator); } else { prop.kind = "init"; if (options.ecmaVersion >= 6) { if (eat(tt.eq)) { var assign = startNodeAt(start); assign.operator = "="; assign.left = prop.key; assign.right = parseMaybeAssign(); prop.value = finishNode(assign, "AssignmentExpression"); } else { prop.value = prop.key; } } else { prop.value = dummyIdent(); } prop.shorthand = true; } if (isClass) { node.body.body.push(finishNode(prop, "MethodDefinition")); } else { node.properties.push(finishNode(prop, "Property")); eat(tt.comma); } } popCx(); if (!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) lastEnd = token.start; if (options.locations) lastEndLoc = token.loc.start; } if (isClass) { semicolon(); finishNode(node.body, "ClassBody"); return finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); } else { return finishNode(node, "ObjectExpression"); } } function parsePropertyName(prop) { if (options.ecmaVersion >= 6) { if (eat(tt.bracketL)) { prop.computed = true; prop.key = parseExpression(); expect(tt.bracketR); return; } else { prop.computed = false; } } var key = (token.type === tt.num || token.type === tt.string) ? parseExprAtom() : parseIdent(); prop.key = key || dummyIdent(); } function parsePropertyAccessor() { if (token.type === tt.name || token.type.keyword) return parseIdent(); } function parseIdent() { var node = startNode(); node.name = token.type === tt.name ? token.value : token.type.keyword; next(); return finishNode(node, "Identifier"); } function initFunction(node) { node.id = null; node.params = []; if (options.ecmaVersion >= 6) { node.generator = false; node.expression = false; } } // Convert existing expression atom to assignable pattern // if possible. function toAssignable(node) { if (options.ecmaVersion >= 6 && node) { switch (node.type) { case "ObjectExpression": node.type = "ObjectPattern"; var props = node.properties; for (var i = 0; i < props.length; i++) { toAssignable(props[i].value); } break; case "ArrayExpression": node.type = "ArrayPattern"; toAssignableList(node.elements); break; case "SpreadElement": node.type = "RestElement"; node.argument = toAssignable(node.argument); break; case "AssignmentExpression": node.type = "AssignmentPattern"; break; } } return checkLVal(node); } function toAssignableList(exprList) { for (var i = 0; i < exprList.length; i++) { toAssignable(exprList[i]); } return exprList; } function parseFunctionParams(params) { pushCx(); params = parseExprList(tt.parenR); return toAssignableList(params); } function parseFunction(node, isStatement) { initFunction(node); if (options.ecmaVersion >= 6) { node.generator = eat(tt.star); } if (token.type === tt.name) node.id = parseIdent(); else if (isStatement) node.id = dummyIdent(); node.params = parseFunctionParams(); node.body = parseBlock(); return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); } function parseMethod(isGenerator) { var node = startNode(); initFunction(node); node.params = parseFunctionParams(); node.generator = isGenerator || false; node.expression = options.ecmaVersion >= 6 && token.type !== tt.braceL; node.body = node.expression ? parseMaybeAssign() : parseBlock(); return finishNode(node, "FunctionExpression"); } function parseArrowExpression(node, params) { initFunction(node); node.params = toAssignableList(params); node.expression = token.type !== tt.braceL; node.body = node.expression ? parseMaybeAssign() : parseBlock(); return finishNode(node, "ArrowFunctionExpression"); } function parseExport() { var node = startNode(); next(); node['default'] = eat(tt._default); node.specifiers = node.source = null; if (node['default']) { var expr = parseMaybeAssign(); if (expr.id) { switch (expr.type) { case "FunctionExpression": expr.type = "FunctionDeclaration"; break; case "ClassExpression": expr.type = "ClassDeclaration"; break; } } node.declaration = expr; semicolon(); } else if (token.type.keyword) { node.declaration = parseStatement(); } else { node.declaration = null; parseSpecifierList(node, "Export"); } semicolon(); return finishNode(node, "ExportDeclaration"); } function parseImport() { var node = startNode(); next(); if (token.type === tt.string) { node.specifiers = []; node.source = parseExprAtom(); node.kind = ''; } else { if (token.type === tt.name && token.value !== "from") { var elt = startNode(); elt.id = parseIdent(); elt.name = null; elt['default'] = true; finishNode(elt, "ImportSpecifier"); eat(tt.comma); } parseSpecifierList(node, "Import"); var specs = node.specifiers; for (var i = 0; i < specs.length; i++) specs[i]['default'] = false; if (elt) node.specifiers.unshift(elt); } semicolon(); return finishNode(node, "ImportDeclaration"); } function parseSpecifierList(node, prefix) { var elts = node.specifiers = []; if (token.type === tt.star) { var elt = startNode(); next(); if (eatContextual("as")) elt.name = parseIdent(); elts.push(finishNode(elt, prefix + "BatchSpecifier")); } else { var indent = curIndent, line = curLineStart, continuedLine = nextLineStart; pushCx(); eat(tt.braceL); if (curLineStart > continuedLine) continuedLine = curLineStart; while (!closes(tt.braceR, indent + (curLineStart <= continuedLine ? 1 : 0), line)) { var elt = startNode(); if (eat(tt.star)) { if (eatContextual("as")) elt.name = parseIdent(); finishNode(elt, prefix + "BatchSpecifier"); } else { if (isContextual("from")) break; elt.id = parseIdent(); elt.name = eatContextual("as") ? parseIdent() : null; finishNode(elt, prefix + "Specifier"); } elts.push(elt); eat(tt.comma); } eat(tt.braceR); popCx(); } node.source = eatContextual("from") ? parseExprAtom() : null; } function parseExprList(close, allowEmpty) { var indent = curIndent, line = curLineStart, elts = []; next(); // Opening bracket while (!closes(close, indent + 1, line)) { if (eat(tt.comma)) { elts.push(allowEmpty ? null : dummyIdent()); continue; } var elt = parseMaybeAssign(); if (isDummy(elt)) { if (closes(close, indent, line)) break; next(); } else { elts.push(elt); } eat(tt.comma); } popCx(); if (!eat(close)) { // If there is no closing brace, make the node span to the start // of the next token (this is useful for Tern) lastEnd = token.start; if (options.locations) lastEndLoc = token.loc.start; } return elts; } });