text: "use strict";
  
  var _tokentype = require("./tokentype.js");
  
  var _state = require("./state.js");
  
  var _parseutil = require("./parseutil.js");
  
  var _whitespace = require("./whitespace.js");
  
  var _scopeflags = require("./scopeflags.js");
  
  const pp = _state.Parser.prototype;
  
  // Check if property name clashes with already added.
  // Object/class getters and setters are not allowed to clash —
  // either with each other or with an init property — and in
  // strict mode, init properties are also not allowed to be repeated.
  
  // A recursive descent parser operates by defining functions for all
  // syntactic elements, and recursively calling those, each function
  // advancing the input stream and returning an AST node. Precedence
  // of constructs (for example, the fact that `!x[1]` means `!(x[1])`
  // instead of `(!x)[1]` is handled by the fact that the parser
  // function that parses unary prefix operators is called first, and
  // in turn calls the function that parses `[]` subscripts — that
  // way, it'll receive the node for `x[1]` already parsed, and wraps
  // *that* in the unary operator node.
  //
  // Acorn uses an [operator precedence parser][opp] to handle binary
  // operator precedence, because it is much more compact than using
  // the technique outlined above, which uses different, nesting
  // functions to specify precedence, for all of the ten binary
  // precedence levels that JavaScript defines.
  //
  // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
  
  pp.checkPropClash = function (prop, propHash, refDestructuringErrors) {
    if (this.options.ecmaVersion >= 9 && prop.type === "SpreadElement") return;
    if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) return;
    let key = prop.key,
        name;
    switch (key.type) {
      case "Identifier":
        name = key.name;break;
      case "Literal":
        name = String(key.value);break;
      default:
        return;
    }
    let kind = prop.kind;
  
    if (this.options.ecmaVersion >= 6) {
      if (name === "__proto__" && kind === "init") {
        if (propHash.proto) {
          if (refDestructuringErrors) {
            if (refDestructuringErrors.doubleProto < 0) refDestructuringErrors.doubleProto = key.start;
            // Backwards-compat kludge. Can be removed in version 6.0
          } else this.raiseRecoverable(key.start, "Redefinition of __proto__ property");
        }
        propHash.proto = true;
      }
      return;
    }
    name = "$" + name;
    let other = propHash[name];
    if (other) {
      let redefinition;
      if (kind === "init") {
        redefinition = this.strict && other.init || other.get || other.set;
      } else {
        redefinition = other.init || other[kind];
      }
      if (redefinition) this.raiseRecoverable(key.start, "Redefinition of property");
    } else {
      other = propHash[name] = {
        init: false,
        get: false,
        set: false
      };
    }
    other[kind] = true;
  };
  
  // ### Expression parsing
  
  // These nest, from the most general expression type at the top to
  // 'atomic', nondivisible expression types at the bottom. Most of
  // the functions will simply let the function(s) below them parse,
  // 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 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).
  
  pp.parseExpression = function (noIn, refDestructuringErrors) {
    let startPos = this.start,
        startLoc = this.startLoc;
    let expr = this.parseMaybeAssign(noIn, refDestructuringErrors);
    if (this.type === _tokentype.types.comma) {
      let node = this.startNodeAt(startPos, startLoc);
      node.expressions = [expr];
      while (this.eat(_tokentype.types.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refDestructuringErrors));
      return this.finishNode(node, "SequenceExpression");
    }
    return expr;
  };
  
  // Parse an assignment expression. This includes applications of
  // operators like `+=`.
  
  pp.parseMaybeAssign = function (noIn, refDestructuringErrors, afterLeftParse) {
    if (this.isContextual("yield")) {
      if (this.inGenerator) return this.parseYield(noIn);
      // The tokenizer will assume an expression is allowed after
      // `yield`, but this isn't that kind of yield
      else this.exprAllowed = false;
    }
  
    let ownDestructuringErrors = false,
        oldParenAssign = -1,
        oldTrailingComma = -1;
    if (refDestructuringErrors) {
      oldParenAssign = refDestructuringErrors.parenthesizedAssign;
      oldTrailingComma = refDestructuringErrors.trailingComma;
      refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1;
    } else {
      refDestructuringErrors = new _parseutil.DestructuringErrors();
      ownDestructuringErrors = true;
    }
  
    let startPos = this.start,
        startLoc = this.startLoc;
    if (this.type === _tokentype.types.parenL || this.type === _tokentype.types.name) this.potentialArrowAt = this.start;
    let left = this.parseMaybeConditional(noIn, refDestructuringErrors);
    if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
    if (this.type.isAssign) {
      let node = this.startNodeAt(startPos, startLoc);
      node.operator = this.value;
      if (this.type === _tokentype.types.eq) left = this.toAssignable(left, false, refDestructuringErrors);
      if (!ownDestructuringErrors) {
        refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.doubleProto = -1;
      }
      if (refDestructuringErrors.shorthandAssign >= left.start) refDestructuringErrors.shorthandAssign = -1; // reset because shorthand default was used correctly
      if (this.type === _tokentype.types.eq) this.checkLValPattern(left);else this.checkLValSimple(left);
      node.left = left;
      this.next();
      node.right = this.parseMaybeAssign(noIn);
      return this.finishNode(node, "AssignmentExpression");
    } else {
      if (ownDestructuringErrors) this.checkExpressionErrors(refDestructuringErrors, true);
    }
    if (oldParenAssign > -1) refDestructuringErrors.parenthesizedAssign = oldParenAssign;
    if (oldTrailingComma > -1) refDestructuringErrors.trailingComma = oldTrailingComma;
    return left;
  };
  
  // Parse a ternary conditional (`?:`) operator.
  
  pp.parseMaybeConditional = function (noIn, refDestructuringErrors) {
    let startPos = this.start,
        startLoc = this.startLoc;
    let expr = this.parseExprOps(noIn, refDestructuringErrors);
    if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
    if (this.eat(_tokentype.types.question)) {
      let node = this.startNodeAt(startPos, startLoc);
      node.test = expr;
      node.consequent = this.parseMaybeAssign();
      this.expect(_tokentype.types.colon);
      node.alternate = this.parseMaybeAssign(noIn);
      return this.finishNode(node, "ConditionalExpression");
    }
    return expr;
  };
  
  // Start the precedence parser.
  
  pp.parseExprOps = function (noIn, refDestructuringErrors) {
    let startPos = this.start,
        startLoc = this.startLoc;
    let expr = this.parseMaybeUnary(refDestructuringErrors, false);
    if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
    return expr.start === startPos && expr.type === "ArrowFunctionExpression" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, noIn);
  };
  
  // Parse binary operators with the operator precedence parsing
  // algorithm. `left` is the left-hand side of the operator.
  // `minPrec` provides context that allows the function to stop and
  // defer further parser to one of its callers when it encounters an
  // operator that has a lower precedence than the set it is parsing.
  
  pp.parseExprOp = function (left, leftStartPos, leftStartLoc, minPrec, noIn) {
    let prec = this.type.binop;
    if (prec != null && (!noIn || this.type !== _tokentype.types._in)) {
      if (prec > minPrec) {
        let logical = this.type === _tokentype.types.logicalOR || this.type === _tokentype.types.logicalAND;
        let coalesce = this.type === _tokentype.types.coalesce;
        if (coalesce) {
          // Handle the precedence of `tt.coalesce` as equal to the range of logical expressions.
          // In other words, `node.right` shouldn't contain logical expressions in order to check the mixed error.
          prec = _tokentype.types.logicalAND.binop;
        }
        let op = this.value;
        this.next();
        let startPos = this.start,
            startLoc = this.startLoc;
        let right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn);
        let node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce);
        if (logical && this.type === _tokentype.types.coalesce || coalesce && (this.type === _tokentype.types.logicalOR || this.type === _tokentype.types.logicalAND)) {
          this.raiseRecoverable(this.start, "Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses");
        }
        return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn);
      }
    }
    return left;
  };
  
  pp.buildBinary = function (startPos, startLoc, left, right, op, logical) {
    let node = this.startNodeAt(startPos, startLoc);
    node.left = left;
    node.operator = op;
    node.right = right;
    return this.finishNode(node, logical ? "LogicalExpression" : "BinaryExpression");
  };
  
  // Parse unary operators, both prefix and postfix.
  
  pp.parseMaybeUnary = function (refDestructuringErrors, sawUnary) {
    let startPos = this.start,
        startLoc = this.startLoc,
        expr;
    if (this.isContextual("await") && (this.inAsync || !this.inFunction && this.options.allowAwaitOutsideFunction)) {
      expr = this.parseAwait();
      sawUnary = true;
    } else if (this.type.prefix) {
      let node = this.startNode(),
          update = this.type === _tokentype.types.incDec;
      node.operator = this.value;
      node.prefix = true;
      this.next();
      node.argument = this.parseMaybeUnary(null, true);
      this.checkExpressionErrors(refDestructuringErrors, true);
      if (update) this.checkLValSimple(node.argument);else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") this.raiseRecoverable(node.start, "Deleting local variable in strict mode");else sawUnary = true;
      expr = this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
    } else {
      expr = this.parseExprSubscripts(refDestructuringErrors);
      if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
      while (this.type.postfix && !this.canInsertSemicolon()) {
        let node = this.startNodeAt(startPos, startLoc);
        node.operator = this.value;
        node.prefix = false;
        node.argument = expr;
        this.checkLValSimple(expr);
        this.next();
        expr = this.finishNode(node, "UpdateExpression");
      }
    }
  
    if (!sawUnary && this.eat(_tokentype.types.starstar)) return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false), "**", false);else return expr;
  };
  
  // Parse call, dot, and `[]`-subscript expressions.
  
  pp.parseExprSubscripts = function (refDestructuringErrors) {
    let startPos = this.start,
        startLoc = this.startLoc;
    let expr = this.parseExprAtom(refDestructuringErrors);
    if (expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")") return expr;
    let result = this.parseSubscripts(expr, startPos, startLoc);
    if (refDestructuringErrors && result.type === "MemberExpression") {
      if (refDestructuringErrors.parenthesizedAssign >= result.start) refDestructuringErrors.parenthesizedAssign = -1;
      if (refDestructuringErrors.parenthesizedBind >= result.start) refDestructuringErrors.parenthesizedBind = -1;
      if (refDestructuringErrors.trailingComma >= result.start) refDestructuringErrors.trailingComma = -1;
    }
    return result;
  };
  
  pp.parseSubscripts = function (base, startPos, startLoc, noCalls) {
    let maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" && this.lastTokEnd === base.end && !this.canInsertSemicolon() && base.end - base.start === 5 && this.potentialArrowAt === base.start;
    let optionalChained = false;
  
    while (true) {
      let element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained);
  
      if (element.optional) optionalChained = true;
      if (element === base || element.type === "ArrowFunctionExpression") {
        if (optionalChained) {
          const chainNode = this.startNodeAt(startPos, startLoc);
          chainNode.expression = element;
          element = this.finishNode(chainNode, "ChainExpression");
        }
        return element;
      }
  
      base = element;
    }
  };
  
  pp.parseSubscript = function (base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained) {
    let optionalSupported = this.options.ecmaVersion >= 11;
    let optional = optionalSupported && this.eat(_tokentype.types.questionDot);
    if (noCalls && optional) this.raise(this.lastTokStart, "Optional chaining cannot appear in the callee of new expressions");
  
    let computed = this.eat(_tokentype.types.bracketL);
    if (computed || optional && this.type !== _tokentype.types.parenL && this.type !== _tokentype.types.backQuote || this.eat(_tokentype.types.dot)) {
      let node = this.startNodeAt(startPos, startLoc);
      node.object = base;
      node.property = computed ? this.parseExpression() : this.parseIdent(this.options.allowReserved !== "never");
      node.computed = !!computed;
      if (computed) this.expect(_tokentype.types.bracketR);
      if (optionalSupported) {
        node.optional = optional;
      }
      base = this.finishNode(node, "MemberExpression");
    } else if (!noCalls && this.eat(_tokentype.types.parenL)) {
      let refDestructuringErrors = new _parseutil.DestructuringErrors(),
          oldYieldPos = this.yieldPos,
          oldAwaitPos = this.awaitPos,
          oldAwaitIdentPos = this.awaitIdentPos;
      this.yieldPos = 0;
      this.awaitPos = 0;
      this.awaitIdentPos = 0;
      let exprList = this.parseExprList(_tokentype.types.parenR, this.options.ecmaVersion >= 8, false, refDestructuringErrors);
      if (maybeAsyncArrow && !optional && !this.canInsertSemicolon() && this.eat(_tokentype.types.arrow)) {
        this.checkPatternErrors(refDestructuringErrors, false);
        this.checkYieldAwaitInDefaultParams();
        if (this.awaitIdentPos > 0) this.raise(this.awaitIdentPos, "Cannot use 'await' as identifier inside an async function");
        this.yieldPos = oldYieldPos;
        this.awaitPos = oldAwaitPos;
        this.awaitIdentPos = oldAwaitIdentPos;
        return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true);
      }
      this.checkExpressionErrors(refDestructuringErrors, true);
      this.yieldPos = oldYieldPos || this.yieldPos;
      this.awaitPos = oldAwaitPos || this.awaitPos;
      this.awaitIdentPos = oldAwaitIdentPos || this.awaitIdentPos;
      let node = this.startNodeAt(startPos, startLoc);
      node.callee = base;
      node.arguments = exprList;
      if (optionalSupported) {
        node.optional = optional;
      }
      base = this.finishNode(node, "CallExpression");
    } else if (this.type === _tokentype.types.backQuote) {
      if (optional || optionalChained) {
        this.raise(this.start, "Optional chaining cannot appear in the tag of tagged template expressions");
      }
      let node = this.startNodeAt(startPos, startLoc);
      node.tag = base;
      node.quasi = this.parseTemplate({ isTagged: true });
      base = this.finishNode(node, "TaggedTemplateExpression");
    }
    return base;
  };
  
  // Parse an atomic expression — either a single token that is an
  // expression, an expression started by a keyword like `function` or
  // `new`, or an expression wrapped in punctuation like `()`, `[]`,
  // or `{}`.
  
  pp.parseExprAtom = function (refDestructuringErrors) {
    // If a division operator appears in an expression position, the
    // tokenizer got confused, and we force it to read a regexp instead.
    if (this.type === _tokentype.types.slash) this.readRegexp();
  
    let node,
        canBeArrow = this.potentialArrowAt === this.start;
    switch (this.type) {
      case _tokentype.types._super:
        if (!this.allowSuper) this.raise(this.start, "'super' keyword outside a method");
        node = this.startNode();
        this.next();
        if (this.type === _tokentype.types.parenL && !this.allowDirectSuper) this.raise(node.start, "super() call outside constructor of a subclass");
        // The `super` keyword can appear at below:
        // SuperProperty:
        //     super [ Expression ]
        //     super . IdentifierName
        // SuperCall:
        //     super ( Arguments )
        if (this.type !== _tokentype.types.dot && this.type !== _tokentype.types.bracketL && this.type !== _tokentype.types.parenL) this.unexpected();
        return this.finishNode(node, "Super");
  
      case _tokentype.types._this:
        node = this.startNode();
        this.next();
        return this.finishNode(node, "ThisExpression");
  
      case _tokentype.types.name:
        let startPos = this.start,
            startLoc = this.startLoc,
            containsEsc = this.containsEsc;
        let id = this.parseIdent(false);
        if (this.options.ecmaVersion >= 8 && !containsEsc && id.name === "async" && !this.canInsertSemicolon() && this.eat(_tokentype.types._function)) return this.parseFunction(this.startNodeAt(startPos, startLoc), 0, false, true);
        if (canBeArrow && !this.canInsertSemicolon()) {
          if (this.eat(_tokentype.types.arrow)) return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false);
          if (this.options.ecmaVersion >= 8 && id.name === "async" && this.type === _tokentype.types.name && !containsEsc) {
            id = this.parseIdent(false);
            if (this.canInsertSemicolon() || !this.eat(_tokentype.types.arrow)) this.unexpected();
            return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true);
          }
        }
        return id;
  
      case _tokentype.types.regexp:
        let value = this.value;
        node = this.parseLiteral(value.value);
        node.regex = { pattern: value.pattern, flags: value.flags };
        return node;
  
      case _tokentype.types.num:case _tokentype.types.string:
        return this.parseLiteral(this.value);
  
      case _tokentype.types._null:case _tokentype.types._true:case _tokentype.types._false:
        node = this.startNode();
        node.value = this.type === _tokentype.types._null ? null : this.type === _tokentype.types._true;
        node.raw = this.type.keyword;
        this.next();
        return this.finishNode(node, "Literal");
  
      case _tokentype.types.parenL:
        let start = this.start,
            expr = this.parseParenAndDistinguishExpression(canBeArrow);
        if (refDestructuringErrors) {
          if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr)) refDestructuringErrors.parenthesizedAssign = start;
          if (refDestructuringErrors.parenthesizedBind < 0) refDestructuringErrors.parenthesizedBind = start;
        }
        return expr;
  
      case _tokentype.types.bracketL:
        node = this.startNode();
        this.next();
        node.elements = this.parseExprList(_tokentype.types.bracketR, true, true, refDestructuringErrors);
        return this.finishNode(node, "ArrayExpression");
  
      case _tokentype.types.braceL:
        return this.parseObj(false, refDestructuringErrors);
  
      case _tokentype.types._function:
        node = this.startNode();
        this.next();
        return this.parseFunction(node, 0);
  
      case _tokentype.types._class:
        return this.parseClass(this.startNode(), false);
  
      case _tokentype.types._new:
        return this.parseNew();
  
      case _tokentype.types.backQuote:
        return this.parseTemplate();
  
      case _tokentype.types._import:
        if (this.options.ecmaVersion >= 11) {
          return this.parseExprImport();
        } else {
          return this.unexpected();
        }
  
      default:
        this.unexpected();
    }
  };
  
  pp.parseExprImport = function () {
    const node = this.startNode();
  
    // Consume `import` as an identifier for `import.meta`.
    // Because `this.parseIdent(true)` doesn't check escape sequences, it needs the check of `this.containsEsc`.
    if (this.containsEsc) this.raiseRecoverable(this.start, "Escape sequence in keyword import");
    const meta = this.parseIdent(true);
  
    switch (this.type) {
      case _tokentype.types.parenL:
        return this.parseDynamicImport(node);
      case _tokentype.types.dot:
        node.meta = meta;
        return this.parseImportMeta(node);
      default:
        this.unexpected();
    }
  };
  
  pp.parseDynamicImport = function (node) {
    this.next(); // skip `(`
  
    // Parse node.source.
    node.source = this.parseMaybeAssign();
  
    // Verify ending.
    if (!this.eat(_tokentype.types.parenR)) {
      const errorPos = this.start;
      if (this.eat(_tokentype.types.comma) && this.eat(_tokentype.types.parenR)) {
        this.raiseRecoverable(errorPos, "Trailing comma is not allowed in import()");
      } else {
        this.unexpected(errorPos);
      }
    }
  
    return this.finishNode(node, "ImportExpression");
  };
  
  pp.parseImportMeta = function (node) {
    this.next(); // skip `.`
  
    const containsEsc = this.containsEsc;
    node.property = this.parseIdent(true);
  
    if (node.property.name !== "meta") this.raiseRecoverable(node.property.start, "The only valid meta property for import is 'import.meta'");
    if (containsEsc) this.raiseRecoverable(node.start, "'import.meta' must not contain escaped characters");
    if (this.options.sourceType !== "module" && !this.options.allowImportExportEverywhere) this.raiseRecoverable(node.start, "Cannot use 'import.meta' outside a module");
  
    return this.finishNode(node, "MetaProperty");
  };
  
  pp.parseLiteral = function (value) {
    let node = this.startNode();
    node.value = value;
    node.raw = this.input.slice(this.start, this.end);
    if (node.raw.charCodeAt(node.raw.length - 1) === 110) node.bigint = node.raw.slice(0, -1).replace(/_/g, "");
    this.next();
    return this.finishNode(node, "Literal");
  };
  
  pp.parseParenExpression = function () {
    this.expect(_tokentype.types.parenL);
    let val = this.parseExpression();
    this.expect(_tokentype.types.parenR);
    return val;
  };
  
  pp.parseParenAndDistinguishExpression = function (canBeArrow) {
    let startPos = this.start,
        startLoc = this.startLoc,
        val,
        allowTrailingComma = this.options.ecmaVersion >= 8;
    if (this.options.ecmaVersion >= 6) {
      this.next();
  
      let innerStartPos = this.start,
          innerStartLoc = this.startLoc;
      let exprList = [],
          first = true,
          lastIsComma = false;
      let refDestructuringErrors = new _parseutil.DestructuringErrors(),
          oldYieldPos = this.yieldPos,
          oldAwaitPos = this.awaitPos,
          spreadStart;
      this.yieldPos = 0;
      this.awaitPos = 0;
      // Do not save awaitIdentPos to allow checking awaits nested in parameters
      while (this.type !== _tokentype.types.parenR) {
        first ? first = false : this.expect(_tokentype.types.comma);
        if (allowTrailingComma && this.afterTrailingComma(_tokentype.types.parenR, true)) {
          lastIsComma = true;
          break;
        } else if (this.type === _tokentype.types.ellipsis) {
          spreadStart = this.start;
          exprList.push(this.parseParenItem(this.parseRestBinding()));
          if (this.type === _tokentype.types.comma) this.raise(this.start, "Comma is not permitted after the rest element");
          break;
        } else {
          exprList.push(this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem));
        }
      }
      let innerEndPos = this.start,
          innerEndLoc = this.startLoc;
      this.expect(_tokentype.types.parenR);
  
      if (canBeArrow && !this.canInsertSemicolon() && this.eat(_tokentype.types.arrow)) {
        this.checkPatternErrors(refDestructuringErrors, false);
        this.checkYieldAwaitInDefaultParams();
        this.yieldPos = oldYieldPos;
        this.awaitPos = oldAwaitPos;
        return this.parseParenArrowList(startPos, startLoc, exprList);
      }
  
      if (!exprList.length || lastIsComma) this.unexpected(this.lastTokStart);
      if (spreadStart) this.unexpected(spreadStart);
      this.checkExpressionErrors(refDestructuringErrors, true);
      this.yieldPos = oldYieldPos || this.yieldPos;
      this.awaitPos = oldAwaitPos || this.awaitPos;
  
      if (exprList.length > 1) {
        val = this.startNodeAt(innerStartPos, innerStartLoc);
        val.expressions = exprList;
        this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
      } else {
        val = exprList[0];
      }
    } else {
      val = this.parseParenExpression();
    }
  
    if (this.options.preserveParens) {
      let par = this.startNodeAt(startPos, startLoc);
      par.expression = val;
      return this.finishNode(par, "ParenthesizedExpression");
    } else {
      return val;
    }
  };
  
  pp.parseParenItem = function (item) {
    return item;
  };
  
  pp.parseParenArrowList = function (startPos, startLoc, exprList) {
    return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList);
  };
  
  // New's precedence is slightly tricky. It must allow its argument to
  // be a `[]` or dot subscript expression, but not a call — at least,
  // not without wrapping it in parentheses. Thus, it uses the noCalls
  // argument to parseSubscripts to prevent it from consuming the
  // argument list.
  
  const empty = [];
  
  pp.parseNew = function () {
    if (this.containsEsc) this.raiseRecoverable(this.start, "Escape sequence in keyword new");
    let node = this.startNode();
    let meta = this.parseIdent(true);
    if (this.options.ecmaVersion >= 6 && this.eat(_tokentype.types.dot)) {
      node.meta = meta;
      let containsEsc = this.containsEsc;
      node.property = this.parseIdent(true);
      if (node.property.name !== "target") this.raiseRecoverable(node.property.start, "The only valid meta property for new is 'new.target'");
      if (containsEsc) this.raiseRecoverable(node.start, "'new.target' must not contain escaped characters");
      if (!this.inNonArrowFunction) this.raiseRecoverable(node.start, "'new.target' can only be used in functions");
      return this.finishNode(node, "MetaProperty");
    }
    let startPos = this.start,
        startLoc = this.startLoc,
        isImport = this.type === _tokentype.types._import;
    node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
    if (isImport && node.callee.type === "ImportExpression") {
      this.raise(startPos, "Cannot use new with import()");
    }
    if (this.eat(_tokentype.types.parenL)) node.arguments = this.parseExprList(_tokentype.types.parenR, this.options.ecmaVersion >= 8, false);else node.arguments = empty;
    return this.finishNode(node, "NewExpression");
  };
  
  // Parse template expression.
  
  pp.parseTemplateElement = function ({ isTagged }) {
    let elem = this.startNode();
    if (this.type === _tokentype.types.invalidTemplate) {
      if (!isTagged) {
        this.raiseRecoverable(this.start, "Bad escape sequence in untagged template literal");
      }
      elem.value = {
        raw: this.value,
        cooked: null
      };
    } else {
      elem.value = {
        raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"),
        cooked: this.value
      };
    }
    this.next();
    elem.tail = this.type === _tokentype.types.backQuote;
    return this.finishNode(elem, "TemplateElement");
  };
  
  pp.parseTemplate = function ({ isTagged = false } = {}) {
    let node = this.startNode();
    this.next();
    node.expressions = [];
    let curElt = this.parseTemplateElement({ isTagged });
    node.quasis = [curElt];
    while (!curElt.tail) {
      if (this.type === _tokentype.types.eof) this.raise(this.pos, "Unterminated template literal");
      this.expect(_tokentype.types.dollarBraceL);
      node.expressions.push(this.parseExpression());
      this.expect(_tokentype.types.braceR);
      node.quasis.push(curElt = this.parseTemplateElement({ isTagged }));
    }
    this.next();
    return this.finishNode(node, "TemplateLiteral");
  };
  
  pp.isAsyncProp = function (prop) {
    return !prop.computed && prop.key.type === "Identifier" && prop.key.name === "async" && (this.type === _tokentype.types.name || this.type === _tokentype.types.num || this.type === _tokentype.types.string || this.type === _tokentype.types.bracketL || this.type.keyword || this.options.ecmaVersion >= 9 && this.type === _tokentype.types.star) && !_whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
  };
  
  // Parse an object literal or binding pattern.
  
  pp.parseObj = function (isPattern, refDestructuringErrors) {
    let node = this.startNode(),
        first = true,
        propHash = {};
    node.properties = [];
    this.next();
    while (!this.eat(_tokentype.types.braceR)) {
      if (!first) {
        this.expect(_tokentype.types.comma);
        if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(_tokentype.types.braceR)) break;
      } else first = false;
  
      const prop = this.parseProperty(isPattern, refDestructuringErrors);
      if (!isPattern) this.checkPropClash(prop, propHash, refDestructuringErrors);
      node.properties.push(prop);
    }
    return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
  };
  
  pp.parseProperty = function (isPattern, refDestructuringErrors) {
    let prop = this.startNode(),
        isGenerator,
        isAsync,
        startPos,
        startLoc;
    if (this.options.ecmaVersion >= 9 && this.eat(_tokentype.types.ellipsis)) {
      if (isPattern) {
        prop.argument = this.parseIdent(false);
        if (this.type === _tokentype.types.comma) {
          this.raise(this.start, "Comma is not permitted after the rest element");
        }
        return this.finishNode(prop, "RestElement");
      }
      // To disallow parenthesized identifier via `this.toAssignable()`.
      if (this.type === _tokentype.types.parenL && refDestructuringErrors) {
        if (refDestructuringErrors.parenthesizedAssign < 0) {
          refDestructuringErrors.parenthesizedAssign = this.start;
        }
        if (refDestructuringErrors.parenthesizedBind < 0) {
          refDestructuringErrors.parenthesizedBind = this.start;
        }
      }
      // Parse argument.
      prop.argument = this.parseMaybeAssign(false, refDestructuringErrors);
      // To disallow trailing comma via `this.toAssignable()`.
      if (this.type === _tokentype.types.comma && refDestructuringErrors && refDestructuringErrors.trailingComma < 0) {
        refDestructuringErrors.trailingComma = this.start;
      }
      // Finish
      return this.finishNode(prop, "SpreadElement");
    }
    if (this.options.ecmaVersion >= 6) {
      prop.method = false;
      prop.shorthand = false;
      if (isPattern || refDestructuringErrors) {
        startPos = this.start;
        startLoc = this.startLoc;
      }
      if (!isPattern) isGenerator = this.eat(_tokentype.types.star);
    }
    let containsEsc = this.containsEsc;
    this.parsePropertyName(prop);
    if (!isPattern && !containsEsc && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {
      isAsync = true;
      isGenerator = this.options.ecmaVersion >= 9 && this.eat(_tokentype.types.star);
      this.parsePropertyName(prop, refDestructuringErrors);
    } else {
      isAsync = false;
    }
    this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc);
    return this.finishNode(prop, "Property");
  };
  
  pp.parsePropertyValue = function (prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc) {
    if ((isGenerator || isAsync) && this.type === _tokentype.types.colon) this.unexpected();
  
    if (this.eat(_tokentype.types.colon)) {
      prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);
      prop.kind = "init";
    } else if (this.options.ecmaVersion >= 6 && this.type === _tokentype.types.parenL) {
      if (isPattern) this.unexpected();
      prop.kind = "init";
      prop.method = true;
      prop.value = this.parseMethod(isGenerator, isAsync);
    } else if (!isPattern && !containsEsc && this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && this.type !== _tokentype.types.comma && this.type !== _tokentype.types.braceR && this.type !== _tokentype.types.eq) {
      if (isGenerator || isAsync) this.unexpected();
      prop.kind = prop.key.name;
      this.parsePropertyName(prop);
      prop.value = this.parseMethod(false);
      let paramCount = prop.kind === "get" ? 0 : 1;
      if (prop.value.params.length !== paramCount) {
        let start = prop.value.start;
        if (prop.kind === "get") this.raiseRecoverable(start, "getter should have no params");else this.raiseRecoverable(start, "setter should have exactly one param");
      } else {
        if (prop.kind === "set" && prop.value.params[0].type === "RestElement") this.raiseRecoverable(prop.value.params[0].start, "Setter cannot use rest params");
      }
    } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
      if (isGenerator || isAsync) this.unexpected();
      this.checkUnreserved(prop.key);
      if (prop.key.name === "await" && !this.awaitIdentPos) this.awaitIdentPos = startPos;
      prop.kind = "init";
      if (isPattern) {
        prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));
      } else if (this.type === _tokentype.types.eq && refDestructuringErrors) {
        if (refDestructuringErrors.shorthandAssign < 0) refDestructuringErrors.shorthandAssign = this.start;
        prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));
      } else {
        prop.value = this.copyNode(prop.key);
      }
      prop.shorthand = true;
    } else this.unexpected();
  };
  
  pp.parsePropertyName = function (prop) {
    if (this.options.ecmaVersion >= 6) {
      if (this.eat(_tokentype.types.bracketL)) {
        prop.computed = true;
        prop.key = this.parseMaybeAssign();
        this.expect(_tokentype.types.bracketR);
        return prop.key;
      } else {
        prop.computed = false;
      }
    }
    return prop.key = this.type === _tokentype.types.num || this.type === _tokentype.types.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== "never");
  };
  
  // Initialize empty function node.
  
  pp.initFunction = function (node) {
    node.id = null;
    if (this.options.ecmaVersion >= 6) node.generator = node.expression = false;
    if (this.options.ecmaVersion >= 8) node.async = false;
  };
  
  // Parse object or class method.
  
  pp.parseMethod = function (isGenerator, isAsync, allowDirectSuper) {
    let node = this.startNode(),
        oldYieldPos = this.yieldPos,
        oldAwaitPos = this.awaitPos,
        oldAwaitIdentPos = this.awaitIdentPos;
  
    this.initFunction(node);
    if (this.options.ecmaVersion >= 6) node.generator = isGenerator;
    if (this.options.ecmaVersion >= 8) node.async = !!isAsync;
  
    this.yieldPos = 0;
    this.awaitPos = 0;
    this.awaitIdentPos = 0;
    this.enterScope((0, _scopeflags.functionFlags)(isAsync, node.generator) | _scopeflags.SCOPE_SUPER | (allowDirectSuper ? _scopeflags.SCOPE_DIRECT_SUPER : 0));
  
    this.expect(_tokentype.types.parenL);
    node.params = this.parseBindingList(_tokentype.types.parenR, false, this.options.ecmaVersion >= 8);
    this.checkYieldAwaitInDefaultParams();
    this.parseFunctionBody(node, false, true);
  
    this.yieldPos = oldYieldPos;
    this.awaitPos = oldAwaitPos;
    this.awaitIdentPos = oldAwaitIdentPos;
    return this.finishNode(node, "FunctionExpression");
  };
  
  // Parse arrow function expression with given parameters.
  
  pp.parseArrowExpression = function (node, params, isAsync) {
    let oldYieldPos = this.yieldPos,
        oldAwaitPos = this.awaitPos,
        oldAwaitIdentPos = this.awaitIdentPos;
  
    this.enterScope((0, _scopeflags.functionFlags)(isAsync, false) | _scopeflags.SCOPE_ARROW);
    this.initFunction(node);
    if (this.options.ecmaVersion >= 8) node.async = !!isAsync;
  
    this.yieldPos = 0;
    this.awaitPos = 0;
    this.awaitIdentPos = 0;
  
    node.params = this.toAssignableList(params, true);
    this.parseFunctionBody(node, true, false);
  
    this.yieldPos = oldYieldPos;
    this.awaitPos = oldAwaitPos;
    this.awaitIdentPos = oldAwaitIdentPos;
    return this.finishNode(node, "ArrowFunctionExpression");
  };
  
  // Parse function body and check parameters.
  
  pp.parseFunctionBody = function (node, isArrowFunction, isMethod) {
    let isExpression = isArrowFunction && this.type !== _tokentype.types.braceL;
    let oldStrict = this.strict,
        useStrict = false;
  
    if (isExpression) {
      node.body = this.parseMaybeAssign();
      node.expression = true;
      this.checkParams(node, false);
    } else {
      let nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params);
      if (!oldStrict || nonSimple) {
        useStrict = this.strictDirective(this.end);
        // If this is a strict mode function, verify that argument names
        // are not repeated, and it does not try to bind the words `eval`
        // or `arguments`.
        if (useStrict && nonSimple) this.raiseRecoverable(node.start, "Illegal 'use strict' directive in function with non-simple parameter list");
      }
      // Start a new scope with regard to labels and the `inFunction`
      // flag (restore them to their old value afterwards).
      let oldLabels = this.labels;
      this.labels = [];
      if (useStrict) this.strict = true;
  
      // Add the params to varDeclaredNames to ensure that an error is thrown
      // if a let/const declaration in the function clashes with one of the params.
      this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params));
      // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'
      if (this.strict && node.id) this.checkLValSimple(node.id, _scopeflags.BIND_OUTSIDE);
      node.body = this.parseBlock(false, undefined, useStrict && !oldStrict);
      node.expression = false;
      this.adaptDirectivePrologue(node.body.body);
      this.labels = oldLabels;
    }
    this.exitScope();
  };
  
  pp.isSimpleParamList = function (params) {
    for (var _iterator = params, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
      var _ref;
  
      if (_isArray) {
        if (_i >= _iterator.length) break;
        _ref = _iterator[_i++];
      } else {
        _i = _iterator.next();
        if (_i.done) break;
        _ref = _i.value;
      }
  
      let param = _ref;
  
      if (param.type !== "Identifier") return false;
    }return true;
  };
  
  // Checks function params for various disallowed patterns such as using "eval"
  // or "arguments" and duplicate parameters.
  
  pp.checkParams = function (node, allowDuplicates) {
    let nameHash = Object.create(null);
    for (var _iterator2 = node.params, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
      var _ref2;
  
      if (_isArray2) {
        if (_i2 >= _iterator2.length) break;
        _ref2 = _iterator2[_i2++];
      } else {
        _i2 = _iterator2.next();
        if (_i2.done) break;
        _ref2 = _i2.value;
      }
  
      let param = _ref2;
  
      this.checkLValInnerPattern(param, _scopeflags.BIND_VAR, allowDuplicates ? null : nameHash);
    }
  };
  
  // Parses a comma-separated list of expressions, and returns them as
  // an array. `close` is the token type that ends the list, and
  // `allowEmpty` can be turned on to allow subsequent commas with
  // nothing in between them to be parsed as `null` (which is needed
  // for array literals).
  
  pp.parseExprList = function (close, allowTrailingComma, allowEmpty, refDestructuringErrors) {
    let elts = [],
        first = true;
    while (!this.eat(close)) {
      if (!first) {
        this.expect(_tokentype.types.comma);
        if (allowTrailingComma && this.afterTrailingComma(close)) break;
      } else first = false;
  
      let elt;
      if (allowEmpty && this.type === _tokentype.types.comma) elt = null;else if (this.type === _tokentype.types.ellipsis) {
        elt = this.parseSpread(refDestructuringErrors);
        if (refDestructuringErrors && this.type === _tokentype.types.comma && refDestructuringErrors.trailingComma < 0) refDestructuringErrors.trailingComma = this.start;
      } else {
        elt = this.parseMaybeAssign(false, refDestructuringErrors);
      }
      elts.push(elt);
    }
    return elts;
  };
  
  pp.checkUnreserved = function ({ start, end, name }) {
    if (this.inGenerator && name === "yield") this.raiseRecoverable(start, "Cannot use 'yield' as identifier inside a generator");
    if (this.inAsync && name === "await") this.raiseRecoverable(start, "Cannot use 'await' as identifier inside an async function");
    if (this.keywords.test(name)) this.raise(start, `Unexpected keyword '${name}'`);
    if (this.options.ecmaVersion < 6 && this.input.slice(start, end).indexOf("\\") !== -1) return;
    const re = this.strict ? this.reservedWordsStrict : this.reservedWords;
    if (re.test(name)) {
      if (!this.inAsync && name === "await") this.raiseRecoverable(start, "Cannot use keyword 'await' outside an async function");
      this.raiseRecoverable(start, `The keyword '${name}' is reserved`);
    }
  };
  
  // Parse the next token as an identifier. If `liberal` is true (used
  // when parsing properties), it will also convert keywords into
  // identifiers.
  
  pp.parseIdent = function (liberal, isBinding) {
    let node = this.startNode();
    if (this.type === _tokentype.types.name) {
      node.name = this.value;
    } else if (this.type.keyword) {
      node.name = this.type.keyword;
  
      // To fix https://github.com/acornjs/acorn/issues/575
      // `class` and `function` keywords push new context into this.context.
      // But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.
      // If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword
      if ((node.name === "class" || node.name === "function") && (this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {
        this.context.pop();
      }
    } else {
      this.unexpected();
    }
    this.next(!!liberal);
    this.finishNode(node, "Identifier");
    if (!liberal) {
      this.checkUnreserved(node);
      if (node.name === "await" && !this.awaitIdentPos) this.awaitIdentPos = node.start;
    }
    return node;
  };
  
  // Parses yield expression inside generator.
  
  pp.parseYield = function (noIn) {
    if (!this.yieldPos) this.yieldPos = this.start;
  
    let node = this.startNode();
    this.next();
    if (this.type === _tokentype.types.semi || this.canInsertSemicolon() || this.type !== _tokentype.types.star && !this.type.startsExpr) {
      node.delegate = false;
      node.argument = null;
    } else {
      node.delegate = this.eat(_tokentype.types.star);
      node.argument = this.parseMaybeAssign(noIn);
    }
    return this.finishNode(node, "YieldExpression");
  };
  
  pp.parseAwait = function () {
    if (!this.awaitPos) this.awaitPos = this.start;
  
    let node = this.startNode();
    this.next();
    node.argument = this.parseMaybeUnary(null, true);
    return this.finishNode(node, "AwaitExpression");
  };