diff --git a/acorn.js b/acorn.js index 9555ddfacf..d87244cc9b 100644 --- a/acorn.js +++ b/acorn.js @@ -323,8 +323,8 @@ function initParserState() { lastStart = lastEnd = tokPos; - if (options.locations) lastEndLoc = new Position; - inFunction = inGenerator = inAsync = strict = false; + if (options.locations) lastEndLoc = curPosition(); + inFunction = inGenerator = inAsync = false; labels = []; skipSpace(); readToken(); @@ -644,7 +644,7 @@ tokType = _eof; tokContext = [b_stat]; tokExprAllowed = true; - inType = false; + inType = strict = false; if (tokPos === 0 && options.allowHashBang && input.slice(0, 2) === '#!') { skipLineComment(2); } @@ -1266,38 +1266,37 @@ function readString(quote) { var isJSX = curTokContext() === j_oTag; - ++tokPos; - var out = ""; + var out = "", chunkStart = ++tokPos; for (;;) { if (tokPos >= inputLen) raise(tokStart, "Unterminated string constant"); var ch = input.charCodeAt(tokPos); - if (ch === quote) { - ++tokPos; - return finishToken(_string, out); - } + if (ch === quote) break; if (ch === 92 && !isJSX) { // '\' + out += input.slice(chunkStart, tokPos); out += readEscapedChar(); + chunkStart = tokPos; } else if (ch === 38 && isJSX) { // '&' + out += input.slice(chunkStart, tokPos); out += readJSXEntity(); + chunkStart = tokPos; } else { + if (isNewLine(ch)) raise(tokStart, "Unterminated string constant"); ++tokPos; - if (isNewLine(ch)) { - raise(tokStart, "Unterminated string constant"); - } - out += String.fromCharCode(ch); // '\' - } + } } + out += input.slice(chunkStart, tokPos++); + return finishToken(_string, out); } // Reads template string tokens. function readTmplToken() { - var out = "", start = tokPos; + var out = "", chunkStart = tokPos; for (;;) { if (tokPos >= inputLen) raise(tokStart, "Unterminated template"); var ch = input.charCodeAt(tokPos); if (ch === 96 || ch === 36 && input.charCodeAt(tokPos + 1) === 123) { // '`', '${' - if (tokPos === start && tokType === _template) { + if (tokPos === tokStart && tokType === _template) { if (ch === 36) { tokPos += 2; return finishToken(_dollarBraceL); @@ -1306,23 +1305,29 @@ return finishToken(_backQuote); } } + out += input.slice(chunkStart, tokPos); return finishToken(_template, out); } if (ch === 92) { // '\' + out += input.slice(chunkStart, tokPos); out += readEscapedChar(); + chunkStart = tokPos; + } else if (isNewLine(ch)) { + out += input.slice(chunkStart, tokPos); + ++tokPos; + if (ch === 13 && input.charCodeAt(tokPos) === 10) { + ++tokPos; + out += "\n"; + } else { + out += String.fromCharCode(ch); + } + if (options.locations) { + ++tokCurLine; + tokLineStart = tokPos; + } + chunkStart = tokPos; } else { ++tokPos; - if (isNewLine(ch)) { - if (ch === 13 && input.charCodeAt(tokPos) === 10) { - ++tokPos; - ch = 10; - } - if (options.locations) { - ++tokCurLine; - tokLineStart = tokPos; - } - } - out += String.fromCharCode(ch); } } } @@ -2040,20 +2045,19 @@ // Read an identifier, and return it as a string. Sets `containsEsc` // to whether the word contained a '\u' escape. // - // Only builds up the word character-by-character when it actually - // containeds an escape, as a micro-optimization. + // Incrementally adds only escaped chars, adding other chunks as-is + // as a micro-optimization. function readWord1() { containsEsc = false; - var word, first = true, start = tokPos; + var word = "", first = true, chunkStart = tokPos; for (;;) { var ch = input.charCodeAt(tokPos); - if (isIdentifierChar(ch) || (inXJSTag && ch === 45)) { - if (containsEsc) word += nextChar(); + if (isIdentifierChar(ch)) { ++tokPos; - } else if (ch === 92 && !inXJSTag) { // "\" - if (!containsEsc) word = input.slice(start, tokPos); + } else if (ch === 92) { // "\" containsEsc = true; + word += input.slice(chunkStart, tokPos); if (input.charCodeAt(++tokPos) != 117) // "u" raise(tokPos, "Expecting Unicode escape sequence \\uXXXX"); ++tokPos; @@ -2063,12 +2067,13 @@ if (!(first ? isIdentifierStart(esc) : isIdentifierChar(esc))) raise(tokPos - 4, "Invalid Unicode escape"); word += escStr; + chunkStart = tokPos; } else { break; } first = false; } - return containsEsc ? word : input.slice(start, tokPos); + return word + input.slice(chunkStart, tokPos); } // Read an identifier or keyword token. Will check for reserved @@ -2301,51 +2306,41 @@ // Convert existing expression atom to assignable pattern // if possible. - function toAssignable(node, allowSpread, checkType) { + function toAssignable(node) { if (options.ecmaVersion >= 6 && node) { switch (node.type) { case "Identifier": case "MemberExpression": + case "VirtualPropertyExpression": case "ObjectPattern": case "ArrayPattern": case "AssignmentPattern": - case "RestElement": break; case "ObjectExpression": node.type = "ObjectPattern"; for (var i = 0; i < node.properties.length; i++) { var prop = node.properties[i]; - if (prop.type === "Property" && prop.kind !== "init") unexpected(prop.key.start); - toAssignable(prop.value, false, checkType); + if (prop.kind !== "init") raise(prop.key.start, "Object pattern can't contain getter or setter"); + toAssignable(prop.value); } break; case "ArrayExpression": node.type = "ArrayPattern"; - toAssignableList(node.elements, checkType); - break; - - case "SpreadElement": - if (allowSpread) { - node.type = "RestElement"; - toAssignable(node.argument, false, checkType); - checkSpreadAssign(node.argument); - } else { - unexpected(node.start); - } + toAssignableList(node.elements); break; case "AssignmentExpression": if (node.operator === "=") { node.type = "AssignmentPattern"; } else { - unexpected(node.left.end); + raise(node.left.end, "Only '=' operator can be used for specifying default value."); } break; default: - if (checkType) unexpected(node.start); + raise(node.start, "Assigning to rvalue"); } } return node; @@ -2353,27 +2348,42 @@ // Convert list of expression atoms to binding list. - function toAssignableList(exprList, checkType) { - for (var i = 0; i < exprList.length; i++) { - toAssignable(exprList[i], i === exprList.length - 1, checkType); + function toAssignableList(exprList) { + if (exprList.length) { + for (var i = 0; i < exprList.length - 1; i++) { + toAssignable(exprList[i]); + } + var last = exprList[exprList.length - 1]; + switch (last.type) { + case "RestElement": + break; + case "SpreadElement": + last.type = "RestElement"; + var arg = last.argument; + toAssignable(arg); + if (arg.type !== "Identifier" && arg.type !== "ArrayPattern") + unexpected(arg.start); + break; + default: + toAssignable(last); + } } return exprList; } // Parses spread element. - function parseSpread() { + function parseSpread(refShorthandDefaultPos) { var node = startNode(); next(); - node.argument = parseMaybeAssign(); + node.argument = parseMaybeAssign(refShorthandDefaultPos); return finishNode(node, "SpreadElement"); } function parseRest() { var node = startNode(); next(); - node.argument = parseAssignableAtom(); - checkSpreadAssign(node.argument); + node.argument = tokType === _name || tokType === _bracketL ? parseAssignableAtom() : unexpected(); return finishNode(node, "RestElement"); } @@ -2444,13 +2454,6 @@ return finishNode(node, "AssignmentPattern"); } - // Checks if node can be assignable spread argument. - - function checkSpreadAssign(node) { - if (node.type !== "Identifier" && node.type !== "ArrayPattern") - unexpected(node.start); - } - // Verify that argument names are not repeated, and it does not // try to bind the words `eval` or `arguments`. @@ -2539,12 +2542,19 @@ } break; - case "SpreadProperty": case "AssignmentPattern": + checkLVal(expr.left); + break; + + case "SpreadProperty": case "VirtualPropertyExpression": case "RestElement": break; + case "RestElement": + checkLVal(expr.argument); + break; + default: raise(expr.start, "Assigning to rvalue"); } @@ -2615,14 +2625,25 @@ raise(tokStart, "'import' and 'export' may only appear at the top level"); return starttype === _import ? parseImport(node) : parseExport(node); + case _name: + if (tokVal === "async") { + var id = parseIdent(); + + if (tokType === _function) { + next(); + return parseFunction(node, true, true); + } else { + unexpected(); + } + } + // If the statement does not start with a statement keyword or a // brace, it's an ExpressionStatement or LabeledStatement. We // simply start parsing an expression, and afterwards, if the // next token is a colon and the expression was a simple // Identifier node, we switch to interpreting it as a label. default: - var maybeName = tokVal, expr = parseExpression(false, false, true); - if (expr.type === "FunctionDeclaration") return expr; + var maybeName = tokVal, expr = parseExpression(false, true); if (starttype === _name && expr.type === "Identifier") { if (eat(_colon)) { @@ -2714,10 +2735,14 @@ return parseForIn(node, init); return parseFor(node, init); } - var init = parseExpression(false, true); + var refShorthandDefaultPos = {start: 0}; + var init = parseExpression(true, refShorthandDefaultPos); if (tokType === _in || (options.ecmaVersion >= 6 && isContextual("of"))) { + toAssignable(init); checkLVal(init); return parseForIn(node, init); + } else if (refShorthandDefaultPos.start) { + unexpected(refShorthandDefaultPos.start); } return parseFor(node, init); } @@ -2937,7 +2962,7 @@ finishNode(decl.id, decl.id.type); } - decl.init = eat(_eq) ? parseExpression(true, noIn) : (kind === _const.keyword ? unexpected() : null); + decl.init = eat(_eq) ? parseMaybeAssign(noIn) : (kind === _const.keyword ? unexpected() : null); node.declarations.push(finishNode(decl, "VariableDeclarator")); if (!eat(_comma)) break; } @@ -2952,17 +2977,20 @@ // and, *if* the syntactic construct they handle is present, wrap // the AST node that the inner parser gave them in another node. - // Parse a full expression. The arguments are used to forbid comma - // sequences (in argument lists, array literals, or object literals) - // or the `in` operator (in for loops initalization expressions). + // Parse a full expression. The optional arguments are used to + // forbid the `in` operator (in for loops initalization expressions) + // and provide reference for storing '=' operator inside shorthand + // property assignment in contexts where both object expression + // and object pattern might appear (so it's possible to raise + // delayed syntax error at correct position). - function parseExpression(noComma, noIn, isStatement) { + function parseExpression(noIn, refShorthandDefaultPos) { var start = storeCurrentPos(); - var expr = parseMaybeAssign(noIn, isStatement); - if (!noComma && tokType === _comma) { + var expr = parseMaybeAssign(noIn, refShorthandDefaultPos); + if (tokType === _comma) { var node = startNodeAt(start); node.expressions = [expr]; - while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn)); + while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn, refShorthandDefaultPos)); return finishNode(node, "SequenceExpression"); } return expr; @@ -2971,26 +2999,37 @@ // Parse an assignment expression. This includes applications of // operators like `+=`. - function parseMaybeAssign(noIn, isStatement) { + function parseMaybeAssign(noIn, refShorthandDefaultPos) { + var failOnShorthandAssign; + if (!refShorthandDefaultPos) { + refShorthandDefaultPos = {start: 0}; + failOnShorthandAssign = true; + } else { + failOnShorthandAssign = false; + } var start = storeCurrentPos(); - var left = parseMaybeConditional(noIn, isStatement); + var left = parseMaybeConditional(noIn, refShorthandDefaultPos); if (tokType.isAssign) { var node = startNodeAt(start); node.operator = tokVal; node.left = tokType === _eq ? toAssignable(left) : left; + refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly checkLVal(left); next(); node.right = parseMaybeAssign(noIn); return finishNode(node, "AssignmentExpression"); + } else if (failOnShorthandAssign && refShorthandDefaultPos.start) { + unexpected(refShorthandDefaultPos.start); } return left; } // Parse a ternary conditional (`?:`) operator. - function parseMaybeConditional(noIn, isStatement) { + function parseMaybeConditional(noIn, refShorthandDefaultPos) { var start = storeCurrentPos(); - var expr = parseExprOps(noIn, isStatement); + var expr = parseExprOps(noIn, refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; if (eat(_question)) { var node = startNodeAt(start); if (options.playground && eat(_eq)) { @@ -3001,9 +3040,9 @@ return finishNode(node, "AssignmentExpression"); } node.test = expr; - node.consequent = parseExpression(true); + node.consequent = parseMaybeAssign(); expect(_colon); - node.alternate = parseExpression(true, noIn); + node.alternate = parseMaybeAssign(noIn); return finishNode(node, "ConditionalExpression"); } return expr; @@ -3011,9 +3050,11 @@ // Start the precedence parser. - function parseExprOps(noIn, isStatement) { + function parseExprOps(noIn, refShorthandDefaultPos) { var start = storeCurrentPos(); - return parseExprOp(parseMaybeUnary(isStatement), start, -1, noIn); + var expr = parseMaybeUnary(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + return parseExprOp(expr, start, -1, noIn); } // Parse binary operators with the operator precedence parsing @@ -3042,13 +3083,14 @@ // Parse unary operators, both prefix and postfix. - function parseMaybeUnary(isStatement) { + function parseMaybeUnary(refShorthandDefaultPos) { if (tokType.prefix) { var node = startNode(), update = tokType.isUpdate; node.operator = tokVal; node.prefix = true; next(); node.argument = parseMaybeUnary(); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) unexpected(refShorthandDefaultPos.start); if (update) checkLVal(node.argument); else if (strict && node.operator === "delete" && node.argument.type === "Identifier") @@ -3056,7 +3098,8 @@ return finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); } var start = storeCurrentPos(); - var expr = parseExprSubscripts(isStatement); + var expr = parseExprSubscripts(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; while (tokType.postfix && !canInsertSemicolon()) { var node = startNodeAt(start); node.operator = tokVal; @@ -3071,9 +3114,11 @@ // Parse call, dot, and `[]`-subscript expressions. - function parseExprSubscripts(isStatement) { + function parseExprSubscripts(refShorthandDefaultPos) { var start = storeCurrentPos(); - return parseSubscripts(parseExprAtom(isStatement), start); + var expr = parseExprAtom(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + return parseSubscripts(expr, start); } function parseSubscripts(base, start, noCalls) { @@ -3123,7 +3168,7 @@ // `new`, or an expression wrapped in punctuation like `()`, `[]`, // or `{}`. - function parseExprAtom(isStatement) { + function parseExprAtom(refShorthandDefaultPos) { switch (tokType) { case _this: var node = startNode(); @@ -3184,7 +3229,7 @@ if (canInsertSemicolon()) return id; next(); - return parseFunction(node, isStatement, true); + return parseFunction(node, false, true); } } else if (id.name === "await") { if (inAsync) return parseAwait(node); @@ -3227,12 +3272,12 @@ // check whether this is array comprehension or regular array if (options.ecmaVersion >= 7 && tokType === _for) { return parseComprehension(node, false); - } - node.elements = parseExprList(_bracketR, true, true); + } + node.elements = parseExprList(_bracketR, true, true, refShorthandDefaultPos); return finishNode(node, "ArrayExpression"); case _braceL: - return parseObj(); + return parseObj(false, refShorthandDefaultPos); case _function: var node = startNode(); @@ -3284,7 +3329,8 @@ return parseComprehension(startNodeAt(start), true); } - var innerStart = storeCurrentPos(), exprList = [], first = true, spreadStart, innerParenStart; + var innerStart = storeCurrentPos(), exprList = [], first = true; + var refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart; while (tokType !== _parenR) { first ? first = false : expect(_comma); if (tokType === _ellipsis) { @@ -3295,7 +3341,7 @@ if (tokType === _parenL && !innerParenStart) { innerParenStart = tokStart; } - exprList.push(parseMaybeAssign()); + exprList.push(parseMaybeAssign(false, refShorthandDefaultPos)); } } var innerEnd = storeCurrentPos(); @@ -3308,6 +3354,7 @@ if (!exprList.length) unexpected(lastStart); if (spreadStart) unexpected(spreadStart); + if (refShorthandDefaultPos.start) unexpected(refShorthandDefaultPos.start); if (exprList.length > 1) { val = startNodeAt(innerStart); @@ -3374,7 +3421,7 @@ // Parse an object literal or binding pattern. - function parseObj(isPattern) { + function parseObj(isPattern, refShorthandDefaultPos) { var node = startNode(), first = true, propHash = {}; node.properties = []; next(); @@ -3400,7 +3447,7 @@ isGenerator = eat(_star); } } - if (options.ecmaVersion >= 7 && tokType === _name && tokVal === "async") { + if (options.ecmaVersion >= 7 && isContextual("async")) { var asyncId = parseIdent(); if (tokType === _colon || tokType === _parenL) { prop.key = asyncId; @@ -3417,7 +3464,7 @@ if (tokType !== _parenL) unexpected(); } if (eat(_colon)) { - prop.value = isPattern ? parseMaybeDefault(start) : parseMaybeAssign(); + prop.value = isPattern ? parseMaybeDefault(start) : parseMaybeAssign(false, refShorthandDefaultPos); prop.kind = "init"; } else if (options.ecmaVersion >= 6 && tokType === _parenL) { if (isPattern) unexpected(); @@ -3433,7 +3480,15 @@ prop.value = parseMethod(false, false); } else if (options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") { prop.kind = "init"; - prop.value = isPattern ? parseMaybeDefault(start, prop.key) : prop.key; + if (isPattern) { + prop.value = parseMaybeDefault(start, prop.key); + } else if (tokType === _eq && refShorthandDefaultPos) { + if (!refShorthandDefaultPos.start) + refShorthandDefaultPos.start = tokStart; + prop.value = parseMaybeDefault(start, prop.key); + } else { + prop.value = prop.key; + } prop.shorthand = true; } else unexpected(); @@ -3532,7 +3587,7 @@ var oldInAsync = inAsync; inAsync = node.async; if (isExpression) { - node.body = parseExpression(true); + node.body = parseMaybeAssign(); node.expression = true; } else { // Start a new scope with regard to labels and the `inFunction` @@ -3579,7 +3634,7 @@ if (node.superClass && isRelational("<")) { node.superTypeParameters = parseTypeParameterInstantiation(); } - if (tokType === _name && tokVal === "implements") { + if (isContextual("implements")) { next(); node.implements = parseClassImplements(); } @@ -3589,7 +3644,7 @@ while (!eat(_braceR)) { if (eat(_semi)) continue; var method = startNode(); - if (options.ecmaVersion >= 7 && tokType === _name && tokVal === "private") { + if (options.ecmaVersion >= 7 && isContextual("private")) { next(); classBody.body.push(parsePrivate(method)); continue; @@ -3661,7 +3716,7 @@ // nothing in between them to be parsed as `null` (which is needed // for array literals). - function parseExprList(close, allowTrailingComma, allowEmpty) { + function parseExprList(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) { var elts = [], first = true; while (!eat(close)) { if (!first) { @@ -3672,7 +3727,10 @@ if (allowEmpty && tokType === _comma) { elts.push(null); } else { - elts.push(tokType === _ellipsis ? parseSpread() : parseMaybeAssign()); + if (tokType === _ellipsis) + elts.push(parseSpread(refShorthandDefaultPos)); + else + elts.push(parseMaybeAssign(false, refShorthandDefaultPos)); } } return elts; @@ -3704,10 +3762,10 @@ // Parses module export declaration. - function parseExport(node) { + function parseExport(node) { next(); // export var|const|let|function|class ...; - if (tokType === _var || tokType === _const || tokType === _let || tokType === _function || tokType === _class || tokType === _name && tokVal === 'async') { + if (tokType === _var || tokType === _const || tokType === _let || tokType === _function || tokType === _class) { node.declaration = parseStatement(true); node['default'] = false; node.specifiers = null; @@ -3715,7 +3773,7 @@ } else // export default ...; if (eat(_default)) { - var declar = node.declaration = parseExpression(true); + var declar = node.declaration = parseMaybeAssign(true); if (declar.id) { if (declar.type === "FunctionExpression") { declar.type = "FunctionDeclaration"; @@ -3839,7 +3897,7 @@ node.argument = null; } else { node.delegate = eat(_star); - node.argument = parseExpression(true); + node.argument = parseMaybeAssign(); } return finishNode(node, "YieldExpression"); } @@ -4149,7 +4207,7 @@ return parseDeclareFunction(node); } else if (tokType === _var) { return parseDeclareVariable(node); - } else if (tokType === _name && tokVal === "module") { + } else if (isContextual("module")) { return parseDeclareModule(node); } else { unexpected(); @@ -4368,7 +4426,7 @@ while (tokType !== _braceR) { var start = storeCurrentPos(); node = startNode(); - if (allowStatic && tokType === _name && tokVal === "static") { + if (allowStatic && isContextual("static")) { next(); isStatic = true; } diff --git a/acorn_loose.js b/acorn_loose.js deleted file mode 100644 index c3f17d352e..0000000000 --- a/acorn_loose.js +++ /dev/null @@ -1,1146 +0,0 @@ -// 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) === '.') { - 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]); - } else { - node = new Node(pos); - } - if (options.directSourceFile) - node.sourceFile = options.directSourceFile; - if (options.ranges) - node.range = [pos[0], 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() { - if (token.type === tt.slash || token.type === tt.assign && token.value === "/=") - next(true); - - 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(false, true); - if (token.type === tt._in || isContextual("of")) { - return parseForIn(node, checkLVal(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 = parseIdent(); - 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) ? parseExpression(true, 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(noComma, noIn) { - var start = storeCurrentPos(); - var expr = parseMaybeAssign(noIn); - if (!noComma && 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 = parseExpression(true); - node.alternate = expect(tt.colon) ? parseExpression(true, 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 = parseExpression(true); - } - 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(); - 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; - if (options.ecmaVersion >= 6) { - if (isClass) { - prop['static'] = false; - } else { - prop.method = false; - prop.shorthand = false; - } - isGenerator = eat(tt.star); - } - parsePropertyName(prop); - if (isDummy(prop.key)) { if (isDummy(parseExpression(true))) 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 = parseExpression(true); - } 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"; - prop.value = options.ecmaVersion >= 6 ? prop.key : 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 ? parseExpression(true) : 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 ? parseExpression(true) : parseBlock(); - return finishNode(node, "ArrowFunctionExpression"); - } - - function parseExport() { - var node = startNode(); - next(); - node['default'] = eat(tt._default); - node.specifiers = node.source = null; - if (node['default']) { - node.declaration = parseExpression(); - 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 = startNode(); - elt.name = parseIdent(); - elt.id.name = "default"; - finishNode(elt.id, "Identifier"); - 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 = parseExpression(true); - 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; - } -}); diff --git a/test/tests-harmony.js b/test/tests-harmony.js index 9fc9dec59c..596e5f7ab0 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -13745,7 +13745,7 @@ testFail("[2] = 42", "Assigning to rvalue (1:1)", {ecmaVersion: 6}); testFail("({ obj:20 }) = 42", "Assigning to rvalue (1:7)", {ecmaVersion: 6}); -testFail("( { get x() {} } ) = 0", "Unexpected token (1:8)", {ecmaVersion: 6}); +testFail("( { get x() {} } ) = 0", "Object pattern can't contain getter or setter (1:8)", {ecmaVersion: 6}); testFail("x \n is y", "Unexpected token (2:4)", {ecmaVersion: 6}); @@ -13801,9 +13801,9 @@ testFail("\"use strict\"; (a) => 00", "Invalid number (1:21)", {ecmaVersion: 6}) testFail("() <= 42", "Unexpected token (1:1)", {ecmaVersion: 6}); -testFail("(10) => 00", "Unexpected token (1:1)", {ecmaVersion: 6}); +testFail("(10) => 00", "Assigning to rvalue (1:1)", {ecmaVersion: 6}); -testFail("(10, 20) => 00", "Unexpected token (1:1)", {ecmaVersion: 6}); +testFail("(10, 20) => 00", "Assigning to rvalue (1:1)", {ecmaVersion: 6}); testFail("yield v", "Unexpected token (1:6)", {ecmaVersion: 6}); @@ -14037,7 +14037,7 @@ testFail("\"use strict\"; function x(a, ...[a]){}", "Argument name clash in stri testFail("(...a, b) => {}", "Unexpected token (1:5)", {ecmaVersion: 6}); -testFail("([ 5 ]) => {}", "Unexpected token (1:3)", {ecmaVersion: 6}); +testFail("([ 5 ]) => {}", "Assigning to rvalue (1:3)", {ecmaVersion: 6}); testFail("({ 5 }) => {}", "Unexpected token (1:5)", {ecmaVersion: 6}); @@ -14045,7 +14045,7 @@ testFail("(...[ 5 ]) => {}", "Unexpected token (1:6)", {ecmaVersion: 6}); testFail("[...{ a }] = b", "Unexpected token (1:4)", {ecmaVersion: 6}); -testFail("[...a, b] = c", "Unexpected token (1:1)", {ecmaVersion: 6}); +testFail("[...a, b] = c", "Assigning to rvalue (1:1)", {ecmaVersion: 6}); testFail("({ t(eval) { \"use strict\"; } });", "Defining 'eval' in strict mode (1:5)", {ecmaVersion: 6}); @@ -14120,7 +14120,7 @@ testFail("\"use strict\"; (eval) => 42", "Defining 'eval' in strict mode (1:15)" testFail("(eval) => { \"use strict\"; 42 }", "Defining 'eval' in strict mode (1:1)", {ecmaVersion: 6}); -testFail("({ get test() { } }) => 42", "Unexpected token (1:7)", {ecmaVersion: 6}); +testFail("({ get test() { } }) => 42", "Object pattern can't contain getter or setter (1:7)", {ecmaVersion: 6}); /* Regression tests */ @@ -14509,6 +14509,259 @@ test("var [localVar = defaultValue] = obj", { loose: false }); +test("({x = 0} = obj)", { + type: "Program", + range: [0, 15], + body: [{ + type: "ExpressionStatement", + range: [0, 15], + expression: { + type: "AssignmentExpression", + range: [1, 14], + operator: "=", + left: { + type: "ObjectPattern", + range: [1, 8], + properties: [{ + type: "Property", + range: [2, 7], + method: false, + shorthand: true, + computed: false, + key: { + type: "Identifier", + range: [2, 3], + name: "x" + }, + kind: "init", + value: { + type: "AssignmentPattern", + range: [6, 7], + operator: "=", + left: { + type: "Identifier", + range: [2, 3], + name: "x" + }, + right: { + type: "Literal", + range: [6, 7], + value: 0 + } + } + }] + }, + right: { + type: "Identifier", + range: [11, 14], + name: "obj" + } + } + }] +}, { + ecmaVersion: 6, + ranges: true, + loose: false +}); + +test("({x = 0}) => x", { + type: "Program", + range: [0, 14], + body: [{ + type: "ExpressionStatement", + range: [0, 14], + expression: { + type: "ArrowFunctionExpression", + range: [0, 14], + id: null, + generator: false, + expression: true, + params: [{ + type: "ObjectPattern", + range: [1, 8], + properties: [{ + type: "Property", + range: [2, 7], + method: false, + shorthand: true, + computed: false, + key: { + type: "Identifier", + range: [2, 3], + name: "x" + }, + kind: "init", + value: { + type: "AssignmentPattern", + range: [6, 7], + operator: "=", + left: { + type: "Identifier", + range: [2, 3], + name: "x" + }, + right: { + type: "Literal", + range: [6, 7], + value: 0 + } + } + }] + }], + body: { + type: "Identifier", + range: [13, 14], + name: "x" + } + } + }] +}, { + ecmaVersion: 6, + ranges: true, + loose: false +}); + +test("[a, {b: {c = 1}}] = arr", { + type: "Program", + range: [0, 23], + body: [{ + type: "ExpressionStatement", + range: [0, 23], + expression: { + type: "AssignmentExpression", + range: [0, 23], + operator: "=", + left: { + type: "ArrayPattern", + range: [0, 17], + elements: [ + { + type: "Identifier", + range: [1, 2], + name: "a" + }, + { + type: "ObjectPattern", + range: [4, 16], + properties: [{ + type: "Property", + range: [5, 15], + method: false, + shorthand: false, + computed: false, + key: { + type: "Identifier", + range: [5, 6], + name: "b" + }, + value: { + type: "ObjectPattern", + range: [8, 15], + properties: [{ + type: "Property", + range: [9, 14], + method: false, + shorthand: true, + computed: false, + key: { + type: "Identifier", + range: [9, 10], + name: "c" + }, + kind: "init", + value: { + type: "AssignmentPattern", + range: [13, 14], + operator: "=", + left: { + type: "Identifier", + range: [9, 10], + name: "c" + }, + right: { + type: "Literal", + range: [13, 14], + value: 1 + } + } + }] + }, + kind: "init" + }] + } + ] + }, + right: { + type: "Identifier", + range: [20, 23], + name: "arr" + } + } + }] +}, { + ecmaVersion: 6, + ranges: true, + loose: false +}); + +test("for ({x = 0} in arr);", { + type: "Program", + range: [0, 21], + body: [{ + type: "ForInStatement", + range: [0, 21], + left: { + type: "ObjectPattern", + range: [5, 12], + properties: [{ + type: "Property", + range: [6, 11], + method: false, + shorthand: true, + computed: false, + key: { + type: "Identifier", + range: [6, 7], + name: "x" + }, + kind: "init", + value: { + type: "AssignmentPattern", + range: [10, 11], + operator: "=", + left: { + type: "Identifier", + range: [6, 7], + name: "x" + }, + right: { + type: "Literal", + range: [10, 11], + value: 0 + } + } + }] + }, + right: { + type: "Identifier", + range: [16, 19], + name: "arr" + }, + body: { + type: "EmptyStatement", + range: [20, 21] + } + }] +}, { + ecmaVersion: 6, + ranges: true, + loose: false +}); + +testFail("obj = {x = 0}", "Unexpected token (1:9)", {ecmaVersion: 6}); + +testFail("f({x = 0})", "Unexpected token (1:5)", {ecmaVersion: 6}); + // https://github.com/marijnh/acorn/issues/191 test("try {} catch ({message}) {}", { @@ -14760,3 +15013,6 @@ testFail("if (1) let x = 10;", "Unexpected token (1:7)", {ecmaVersion: 6}); testFail("for (;;) const x = 10;", "Unexpected token (1:9)", {ecmaVersion: 6}); testFail("while (1) function foo(){}", "Unexpected token (1:10)", {ecmaVersion: 6}); testFail("if (1) ; else class Cls {}", "Unexpected token (1:14)", {ecmaVersion: 6}); + +testFail("'use strict'; [...eval] = arr", "Assigning to eval in strict mode (1:18)", {ecmaVersion: 6}); +testFail("'use strict'; ({eval = defValue} = obj)", "Assigning to eval in strict mode (1:16)", {ecmaVersion: 6});