text: import {types as tt} from "./tokentype"
  import {Parser} from "./state"
  import {has} from "./util"
  
  const pp = Parser.prototype
  
  // Convert existing expression atom to assignable pattern
  // if possible.
  
  pp.toAssignable = function(node, isBinding) {
    if (this.options.ecmaVersion >= 6 && node) {
      switch (node.type) {
      case "Identifier":
      case "ObjectPattern":
      case "ArrayPattern":
        break
  
      case "ObjectExpression":
        node.type = "ObjectPattern"
        for (let i = 0; i < node.properties.length; i++) {
          let prop = node.properties[i]
          if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter")
          this.toAssignable(prop.value, isBinding)
        }
        break
  
      case "ArrayExpression":
        node.type = "ArrayPattern"
        this.toAssignableList(node.elements, isBinding)
        break
  
      case "AssignmentExpression":
        if (node.operator === "=") {
          node.type = "AssignmentPattern"
          delete node.operator
          // falls through to AssignmentPattern
        } else {
          this.raise(node.left.end, "Only '=' operator can be used for specifying default value.")
          break
        }
  
      case "AssignmentPattern":
        if (node.right.type === "YieldExpression")
          this.raise(node.right.start, "Yield expression cannot be a default value")
        break
  
      case "ParenthesizedExpression":
        node.expression = this.toAssignable(node.expression, isBinding)
        break
  
      case "MemberExpression":
        if (!isBinding) break
  
      default:
        this.raise(node.start, "Assigning to rvalue")
      }
    }
    return node
  }
  
  // Convert list of expression atoms to binding list.
  
  pp.toAssignableList = function(exprList, isBinding) {
    let end = exprList.length
    if (end) {
      let last = exprList[end - 1]
      if (last && last.type == "RestElement") {
        --end
      } else if (last && last.type == "SpreadElement") {
        last.type = "RestElement"
        let arg = last.argument
        this.toAssignable(arg, isBinding)
        if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern")
          this.unexpected(arg.start)
        --end
      }
  
      if (isBinding && last.type === "RestElement" && last.argument.type !== "Identifier")
        this.unexpected(last.argument.start)
    }
    for (let i = 0; i < end; i++) {
      let elt = exprList[i]
      if (elt) this.toAssignable(elt, isBinding)
    }
    return exprList
  }
  
  // Parses spread element.
  
  pp.parseSpread = function(refDestructuringErrors) {
    let node = this.startNode()
    this.next()
    node.argument = this.parseMaybeAssign(refDestructuringErrors)
    return this.finishNode(node, "SpreadElement")
  }
  
  pp.parseRest = function(allowNonIdent) {
    let node = this.startNode()
    this.next()
  
    // RestElement inside of a function parameter must be an identifier
    if (allowNonIdent) node.argument = this.type === tt.name ? this.parseIdent() : this.unexpected()
    else node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected()
  
    return this.finishNode(node, "RestElement")
  }
  
  // Parses lvalue (assignable) atom.
  
  pp.parseBindingAtom = function() {
    if (this.options.ecmaVersion < 6) return this.parseIdent()
    switch (this.type) {
    case tt.name:
      return this.parseIdent()
  
    case tt.bracketL:
      let node = this.startNode()
      this.next()
      node.elements = this.parseBindingList(tt.bracketR, true, true)
      return this.finishNode(node, "ArrayPattern")
  
    case tt.braceL:
      return this.parseObj(true)
  
    default:
      this.unexpected()
    }
  }
  
  pp.parseBindingList = function(close, allowEmpty, allowTrailingComma, allowNonIdent) {
    let elts = [], first = true
    while (!this.eat(close)) {
      if (first) first = false
      else this.expect(tt.comma)
      if (allowEmpty && this.type === tt.comma) {
        elts.push(null)
      } else if (allowTrailingComma && this.afterTrailingComma(close)) {
        break
      } else if (this.type === tt.ellipsis) {
        let rest = this.parseRest(allowNonIdent)
        this.parseBindingListItem(rest)
        elts.push(rest)
        if (this.type === tt.comma) this.raise(this.start, "Comma is not permitted after the rest element")
        this.expect(close)
        break
      } else {
        let elem = this.parseMaybeDefault(this.start, this.startLoc)
        this.parseBindingListItem(elem)
        elts.push(elem)
      }
    }
    return elts
  }
  
  pp.parseBindingListItem = function(param) {
    return param
  }
  
  // Parses assignment pattern around given atom if possible.
  
  pp.parseMaybeDefault = function(startPos, startLoc, left) {
    left = left || this.parseBindingAtom()
    if (this.options.ecmaVersion < 6 || !this.eat(tt.eq)) return left
    let node = this.startNodeAt(startPos, startLoc)
    node.left = left
    node.right = this.parseMaybeAssign()
    return this.finishNode(node, "AssignmentPattern")
  }
  
  // Verify that a node is an lval — something that can be assigned
  // to.
  
  pp.checkLVal = function(expr, isBinding, checkClashes) {
    switch (expr.type) {
    case "Identifier":
      if (this.strict && this.reservedWordsStrictBind.test(expr.name))
        this.raiseRecoverable(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode")
      if (checkClashes) {
        if (has(checkClashes, expr.name))
          this.raiseRecoverable(expr.start, "Argument name clash")
        checkClashes[expr.name] = true
      }
      break
  
    case "MemberExpression":
      if (isBinding) this.raiseRecoverable(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression")
      break
  
    case "ObjectPattern":
      for (let i = 0; i < expr.properties.length; i++)
        this.checkLVal(expr.properties[i].value, isBinding, checkClashes)
      break
  
    case "ArrayPattern":
      for (let i = 0; i < expr.elements.length; i++) {
        let elem = expr.elements[i]
        if (elem) this.checkLVal(elem, isBinding, checkClashes)
      }
      break
  
    case "AssignmentPattern":
      this.checkLVal(expr.left, isBinding, checkClashes)
      break
  
    case "RestElement":
      this.checkLVal(expr.argument, isBinding, checkClashes)
      break
  
    case "ParenthesizedExpression":
      this.checkLVal(expr.expression, isBinding, checkClashes)
      break
  
    default:
      this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue")
    }
  }
  