const state = {
  VARIABLE: Symbol('VARIABLE'),
  DEFAULT: Symbol('DEFAULT'),
  REST: Symbol('REST'),
}

if (!Array.prototype.includes) {
  Array.prototype.includes = function(searchElement) {
    for (var i in this) {
      if (this[i] == searchElement) {
        return true;
      }
    }
    return false;
  }
}

const isIn = tokens => token => tokens.includes(token)
const isWhitespace = isIn([' ', '\t', '\n'])
const isOpening = isIn(['[', '{'])
const isClosing = isIn([']', '}'])
const isStrWrap = isIn(["'", '"', '`'])

class ParameterParser {
  constructor() {
    this.state = state.VARIABLE
    this.counter = 0

    this.parsed = []
    this.buffer = ''
    this.destructuringType = null
    this.destructuringKeys = []
  }

  parse(fn, scope) {
    Object.assign(this, scope)
    let i = -1
    while (i < fn.length) {
      i += 1
      const token = fn[i]
      switch (this.state) {
        case state.VARIABLE:
          if (isWhitespace(token)) continue
          switch (token) {
            case '=':
              this.pushBuffer()
              this.state = state.DEFAULT
            continue
            case '{':
              this.destructuringType = 'object'
            continue
            case '[':
              this.destructuringType = 'array'
            continue
            case ',':
              this.pushBuffer()
  
            continue
            case '.':
              if (this.buffer === '') {
                i += 2
                this.state = state.REST
                continue
              }
          }
          if (isClosing(token) && this.counter == 0 && this.destructuringType != null) {
            this.pushBuffer()
            this.pushDestructuringKeys()
            continue
          }
          this.buffer += token
          if (i === fn.length - 1) {
            this.pushBuffer()
            if (this.destructuringType != null) {
              this.pushDestructuringKeys()
            }
          }
        break

        case state.DEFAULT:
          if (this.counter === 0 && isWhitespace(token)) continue
          if (isStrWrap(token)) {
            const closingIdx = fn.indexOf(token, i+1)
            this.buffer += fn.slice(i, closingIdx + 1)
            this.pushBuffer()
            i = closingIdx
            continue
          }
          if (isClosing(token) && this.counter == 0 && this.destructuringType != null) {
            this.pushBuffer()
            this.pushDestructuringKeys()
            continue
          }
          if (isOpening(token)) {
            this.counter += 1
          } else if (isClosing(token)) {
            this.counter -= 1
          }
          if (this.counter === 0 && token === ',') {
            this.pushBuffer()
            this.state = state.VARIABLE
            continue
          }
          this.buffer += token
          if (i === fn.length - 1) {
            this.pushBuffer()
            if (this.destructuringType != null) {
              this.pushDestructuringKeys()
            }
          }
        break

        case state.REST:
          if (isClosing(token) && this.counter == 0 && this.destructuringType != null) {
            this.pushBuffer()
            this.pushDestructuringKeys()
            continue
          }
          this.buffer += token
          if (i === fn.length - 1) {
            this.pushBuffer()
          }
        break
      }
    }
    return this.parsed
  }

  pushBuffer() {
    if (this.buffer === '') return

    switch (this.state) {
      case state.VARIABLE:
        if (this.destructuringType == null) {
          this.parsed.push({
            type: 'SIMPLE',
            name: this.buffer,
          })
        } else {
          this.destructuringKeys.push({
            type: 'KEY',
            name: this.buffer,
          })
        }
      break

      case state.DEFAULT:
        let topStack
        if (this.destructuringType == null) {
          topStack = this.parsed.pop()
        } else {
          topStack = this.destructuringKeys.pop()
        }

        let defaultParam
        try{
          defaultParam = eval('(' + this.buffer + ')')
        } catch(e) {
          defaultParam = eval('(this.' + this.buffer + ')')
        }

        if (this.destructuringType == null) {
          this.parsed.push({
            type: 'DEFAULT',
            name: topStack.name,
            value: defaultParam,
          })
        } else {
          this.destructuringKeys.push({
            type: 'KEY_WITH_DEFAULT',
            name: topStack.name,
            value: defaultParam,
          })
        }
      break

      case state.REST:
        if (this.destructuringType == null) {
          this.parsed.push({
            type: 'REST',
            name: this.buffer,
          })
        } else {
          this.destructuringKeys.push({
            type: 'REST',
            name: this.buffer,
          })
        }
      break
    }
    this.buffer = ''
  }

  pushDestructuringKeys() {
    this.parsed.push({
      type: 'DESTRUCTURING',
      destructuring_type: this.destructuringType,
      keys: this.destructuringKeys,
    })
    this.destructuringKeys = []
    this.destructuringType = null
  }
}

module.exports = ParameterParser

@@ -521,16 +521,189 @@
  '%60'%5D)%0A%0A
+function betterEval(obj, scope = null)%7B%0A  const fn = new Function('%22use strict%22;return (' + obj + ')')%0A  if (scope != null) %7B%0A    return fn.call(scope)%0A  %7D%0A  return fn()%0A%7D%0A%0A
 class Pa
@@ -932,34 +932,26 @@
     
-Object.assign(this,
+this.scope =
  scope
-)
 %0A   
@@ -4253,36 +4253,42 @@
 m = 
-eval('(' + this.buffer + ')'
+betterEval(this.buffer, this.scope
 )%0A  
@@ -4335,15 +4335,20 @@
 m = 
-e
+betterE
 val('
-(
 this
@@ -4363,22 +4363,28 @@
 s.buffer
- + ')'
+, this.scope
 )%0A      

replace eval() with Function()