import Token from './Token'
import Expression from './Expression'
import Delegate from './Delegate'
import ExpressionType from '../enums/ExpressionType'
import TokenType from '../enums/TokenType'

/**
 * 表达式入口
 */
export default class Program {
  // 源程序
  SOURCE
  SOURCE_LENGTH
  /**
   * Token队列，用于回退
   */
  tokens = []
  /**
   * 字符串处理环节堆栈，双引号进入字符串处理环节，“{}”中间部分脱离字符串处理环节
   */
  inStrings = []

  /**
   * 当前获取到的字符位置
   */
  pos = 0

  /**
   * 当前是否在字符串处理环节
   */
  inString = false

  constructor (source) {
    this.SOURCE = source
    this.SOURCE_LENGTH = this.SOURCE.length
  }

  /**
   * 调用解析过程
   */
  parse () {
    // 执行完以下方法后就已经诞生了一个完整的执行流程，也就是被翻译成了一套完整的指令集。
    return this.createDelegate(this.expression())
  }

  parseExpression (expression) {
    // 执行完以下方法后就已经诞生了一个完整的执行流程，也就是被翻译成了一套完整的指令集。
    return this.createDelegate(expression)
  }

  /**
   * 编译，产生可执行单元Delegate
   */
  createDelegate (expression) {
    return new Delegate(expression, this.SOURCE)
  }

  /**
   * 获取完整表达式
   */
  expression () {
    const expression = this.CommaExp()
    if (this.pos < this.SOURCE_LENGTH) {
      throw new Error(this.GetExceptionMessage('需要逗号结尾!'))
    }
    return expression
  }

  /**
   * 逗号表达式=赋值表达式(,赋值表达式)*
   */
  CommaExp () {
    const exps = []

    const first = this.AssignExp()
    exps.push(first)

    let t = this.GetToken()

    // 没有结束
    while (t.getType() === TokenType.Oper && (t.getValue() === ',' || t.getValue() === ';' || t.getValue() === '\n')) {
      const r = this.AssignExp()
      exps.push(r)
      t = this.GetToken()
    }

    this.tokens.push(t)

    return Expression.Comma(exps, this.pos)
  }

  /**
   * 赋值表达式=对象属性=一般表达式|一般表达式
   */
  AssignExp () {
    let t = this.GetToken()

    // 把第一个Token的位置记录下来，方便后面回退
    const firstPos = t.getStartPos()

    if (t.getType() !== TokenType.Identy) {
      this.pos = firstPos
      this.tokens.length = 0 // Clear the tokens array
      this.inString = false
      return this.Exp()
    }

    let objExp = Expression.Identity(t.getValue(), this.pos)
    objExp = this.ObjectPath(objExp)

    // 只能给对象属性或者变量赋值
    if (objExp.type !== ExpressionType.Property && objExp.type !== ExpressionType.Identity) {
      this.pos = firstPos
      this.tokens.length = 0
      this.inString = false
      return this.Exp()
    }

    t = this.GetToken()
    if (t.getType() !== TokenType.Oper || t.getValue() !== '=') {
      this.pos = firstPos
      this.tokens.length = 0
      this.inString = false
      return this.Exp()
    }

    const exp = this.Exp()

    // 如果是属性赋值
    if (objExp.type === ExpressionType.Property) {
      // 从属性Expression中获取对象及属性名
      const name = objExp.value
      objExp = objExp.children[0]
      return Expression.Assign(objExp, exp, name, this.pos)
    } else {
      // Identity 变量赋值
      return Expression.Assign(null, exp, objExp.value, this.pos)
    }
  }

  Exp () {
    const v = this.Logic()

    // 是':'，表示条件，否则直接返回单结果
    let t = this.GetToken()
    if (t.getType() === TokenType.Oper && t.getValue() === ':') {
      // 第一项转换
      const result = this.Logic()

      // 下一个是","，继续读取下一个条件结果串，由于是右结合，只能采用递归
      t = this.GetToken()
      if (t.getType() === TokenType.Oper && t.getValue() === ',') {
        // 第二项转换
        const sExp = this.Exp()
        // 返回
        return Expression.Condition(v, result, sExp, this.SOURCE, this.pos)
      } else {
        throw new Error(this.GetExceptionMessage('必须有默认值!'))
      }
    } else {
      this.tokens.push(t) // Put the token back
      return v
    }
  }

  Logic () {
    let t = this.GetToken()

    // !表达式
    const hasNot = t.getType() === TokenType.Oper && t.getValue() === '!'
    if (!hasNot) {
      this.tokens.push(t) // Put the token back if it's not '!'
    }

    let v = this.Compare()
    if (hasNot) {
      v = Expression.Not(v, this.SOURCE, this.pos)
    }

    t = this.GetToken()
    while (t.getType() === TokenType.Oper && (t.getValue() === '&&' || t.getValue() === '||')) {
      // 第二项转换
      const exp = this.Logic()

      // 执行
      if (t.getValue() === '&&') {
        v = Expression.And(v, exp, this.SOURCE, this.pos)
      } else {
        v = Expression.Or(v, exp, this.SOURCE, this.pos)
      }

      t = this.GetToken()
    }

    this.tokens.push(t) // Put the token back
    return v
  }

  Compare () {
    const left = this.Math()
    const t = this.GetToken()

    if (t.getType() === TokenType.Oper && (
      t.getValue() === '>' || t.getValue() === '>=' ||
      t.getValue() === '<' || t.getValue() === '<=' || t.getValue() === '=>')) {
      if (t.getValue() === '=>') {
        this.tokens.push(t) // Push back the token for lambda handling
        return this.Lambda()
      }

      const rExp = this.Math()

      switch (t.getValue()) {
        case '>':
          return Expression.GreaterThan(left, rExp, this.SOURCE, this.pos)
        case '>=':
          return Expression.GreaterThanOrEqual(left, rExp, this.SOURCE, this.pos)
        case '<':
          return Expression.LessThan(left, rExp, this.SOURCE, this.pos)
        case '<=':
          return Expression.LessThanOrEqual(left, rExp, this.SOURCE, this.pos)
      }
    } else if (t.getType() === TokenType.Oper && (
      t.getValue() === '==' || t.getValue() === '!=')) {
      const rExp = this.Math()

      // 相等比较
      if (t.getValue() === '==') {
        return Expression.Equal(left, rExp, this.SOURCE, this.pos)
      } else {
        return Expression.NotEqual(left, rExp, this.SOURCE, this.pos)
      }
    }

    // 返回当前的表达式结果
    this.tokens.push(t) // Push the token back
    return left
  }

  Math () {
    let v = this.Mul()
    let t = this.GetToken()

    while (t.getType() === TokenType.Oper && (t.getValue() === '+' || t.getValue() === '-')) {
      // 转换操作数2为数字
      const r = this.Mul()

      // 开始运算
      if (t.getValue() === '+') {
        v = Expression.Add(v, r, this.SOURCE, this.pos)
      } else {
        v = Expression.Subtract(v, r, this.SOURCE, this.pos)
      }

      t = this.GetToken()
    }

    this.tokens.push(t) // Push the token back
    return v
  }

  Lambda () {
    let t = this.GetToken()
    if (t.getValue() !== '=>' || t.getValue() == null) {
      throw new Error(this.GetExceptionMessage('lambda必须以=>开始'))
    }

    t = this.GetToken()
    if (t.getValue() !== '{' || t.getValue() == null) {
      throw new Error(this.GetExceptionMessage('lambda必须以{开始'))
    }

    // 取lambda中的表达式
    const exp = this.CommaExp()

    t = this.GetToken()
    if (t.getValue() !== '}' || t.getValue() == null) {
      throw new Error(this.GetExceptionMessage('lambda必须以}结束'))
    }

    return Expression.Lambda(exp, this.pos)
  }

  Mul () {
    let v = this.UnarySub()
    let t = this.GetToken()

    while (t.getType() === TokenType.Oper && (t.getValue() === '*' || t.getValue() === '/' || t.getValue() === '%')) {
      // Convert the second operand to a number
      const r = this.UnarySub()

      // Perform the operation
      if (t.getValue() === '*') {
        v = Expression.Multiply(v, r, this.SOURCE, this.pos)
      } else if (t.getValue() === '/') {
        v = Expression.Divide(v, r, this.SOURCE, this.pos)
      } else {
        v = Expression.Modulo(v, r, this.SOURCE, this.pos)
      }

      t = this.GetToken()
    }

    this.tokens.push(t)
    return v
  }

  UnarySub () {
    const t = this.GetToken()
    if (t.getType() === TokenType.Oper && t.getValue() === '-') {
      const r = this.Item()
      return Expression.Subtract(Expression.Constant(0, this.pos), r, this.SOURCE, this.pos)
    }
    this.tokens.push(t) // Push the token back into the queue
    return this.Item()
  }

  Item () {
    const objName = ''
    // 获取对象表达式
    let objExp = this.ItemHead(objName)
    // 获取对象路径表达式
    objExp = this.ObjectPath(objExp)
    return objExp
  }

  ItemHead (name) {
    let t = this.GetToken()

    if (t.getType() === TokenType.Oper && t.getValue() === '(') {
      const result = this.CommaExp()
      t = this.GetToken()
      if (t.getType() !== TokenType.Oper || t.getValue() !== ')') {
        throw new Error(this.GetExceptionMessage('括号不匹配'))
      }
      return result
    } else if (t.getType() === TokenType.Oper && t.getValue() === '{') {
      // JSON object
      return this.Json()
    } else if (t.getType() === TokenType.Oper && t.getValue() === '[') {
      // JSON array
      return this.JsonArray()
    } else if (t.getType() === TokenType.Oper && t.getValue() === '"') {
      // String concatenation sequence
      return this.StringUnion()
    } else if (t.getType() === TokenType.Int || t.getType() === TokenType.Double || t.getType() === TokenType.Bool) {
      return Expression.Constant(t.getValue(), this.pos)
    } else if (t.getType() === TokenType.Identy) {
      const objName = t.getValue()

      switch (objName) {
        case 'return':
          return Expression.Return(this.Exp(), this.pos)
        case 'try':
          this.tokens.push(t) // Revert the token back to the queue
          return this.Try()
        case 'throw':
          return Expression.Throw(this.Exp(), this.pos)
        case 'validate':
          return this.Validate()
        case 'assert':
          return Expression.Assert(this.Exp(), this.pos)
        case 'break':
          return Expression.Break(this.pos, undefined)
        case 'continue':
          return Expression.Continue(this.pos, undefined)
      }

      // Return the object name
      name += objName
      // Process the object name
      return this.ObjectName(objName)
    } else if (t.getType() === TokenType.Null) {
      return Expression.Constant(null, this.pos)
    }

    throw new Error(this.GetExceptionMessage('单词类型错误：' + t.getType()))
  }

  /**
   * TryCatch := try {expression} (catch(Exception类 名){表达式})* finally{表达式}?
   */
  Try () {
    let t = this.GetToken()
    if (t.getValue() !== 'try') {
      throw new Error(this.GetExceptionMessage('try-catch必须以try开始'))
    }

    t = this.GetToken()
    if (t.getValue() !== '{') {
      throw new Error(this.GetExceptionMessage('try块必须以{开始'))
    }

    // Get the expression inside the try block
    const tryExp = this.CommaExp()

    t = this.GetToken()
    if (t.getValue() !== '}') {
      throw new Error(this.GetExceptionMessage('try块必须以}结束'))
    }

    // Handle the catch part
    const catches = this.Catch()

    // Handle finally
    let finallyExp
    t = this.GetToken()
    if (t.getValue()) {
      if (t.getValue() === 'finally') {
        t = this.GetToken()
        if (t.getValue() !== '{') {
          throw new Error(this.GetExceptionMessage('finally块必须以{开始'))
        }

        // Get the expression inside the finally block
        finallyExp = this.CommaExp()
        t = this.GetToken()
        if (t.getValue() !== '}') {
          throw new Error(this.GetExceptionMessage('finally块必须以}结束'))
        }
        t = this.GetToken()
      }
    }

    this.tokens.push(t)

    // Return the exception handling expression
    return Expression.Try(tryExp, catches, this.pos, finallyExp)
  }

  /**
   * catch部分处理
   */
  Catch () {
    const result = []
    let t = this.GetToken()

    while (t.getValue() === 'catch') {
      t = this.GetToken()
      if (t.getValue() !== '(') {
        throw new Error(this.GetExceptionMessage('catch块参数必须以(开始'))
      }

      // Get the class name
      t = this.GetToken()
      const className = t.getValue()

      // Get the variable name
      t = this.GetToken()
      const varName = t.getValue()

      t = this.GetToken()
      if (t.getValue() !== ')') {
        throw new Error(this.GetExceptionMessage('catch块参数必须以)结束'))
      }

      // Get the opening curly brace
      t = this.GetToken()
      if (t.getValue() !== '{') {
        throw new Error(this.GetExceptionMessage('catch块必须以{开始'))
      }

      // Get the expression inside the catch block
      const catchExp = this.CommaExp()
      result.push(Expression.Catch(className, varName, catchExp, this.pos))

      // Get the closing curly brace
      t = this.GetToken()
      if (t.getValue() !== '}') {
        throw new Error(this.GetExceptionMessage('catch块必须以}结束'))
      }

      // Get the next token
      t = this.GetToken()
    }

    this.tokens.push(t)
    return result
  }

  /**
   * Validate block handling
   */
  Validate () {
    const t = this.GetToken()
    if (t.getValue() !== '{') {
      throw new Error(this.GetExceptionMessage('validate只接收Json对象'))
    }

    // Get the expression in the validate block
    const exp = this.Json()

    // Return Validate processing expression
    return Expression.Validate(exp, this.pos)
  }

  /**
   * JSON Object ::= {} | {propertyName: propertyValue, propertyName: propertyValue}
   */
  Json () {
    const children = []

    let t = this.GetToken()
    // Empty object, return directly
    if (t.getType() === TokenType.Oper && t.getValue() === '}') {
      return Expression.Json(children, this.pos)
    }

    this.tokens.push(t)
    children.push(this.Attr())
    t = this.GetToken()

    // If it's a comma, continue checking the next property
    while (t.getType() === TokenType.Oper && t.getValue() === ',') {
      const nextT = this.GetToken()
      if (nextT.getType() === TokenType.Oper && nextT.getValue() === '}') {
        t = nextT
        break
      }
      this.tokens.push(nextT)
      children.push(this.Attr())
      t = this.GetToken()
    }

    if (t.getType() !== TokenType.Oper || t.getValue() !== '}') {
      throw new Error(this.GetExceptionMessage('JSON对象构建错误'))
    }

    return Expression.Json(children, this.pos)
  }

  /**
   * JSON Array ::= []
   */
  JsonArray () {
    let t = this.GetToken()
    // Empty array: use Array node so each evaluation returns a new [], not a shared Constant
    if (t.getType() === TokenType.Oper && t.getValue() === ']') {
      return Expression.Array([], this.pos)
    }

    // Loop to get array contents
    this.tokens.push(t)
    const ps = this.Params()
    t = this.GetToken()
    if (t.getType() !== TokenType.Oper || t.getValue() !== ']') {
      throw new Error(this.GetExceptionMessage('JSON集合构建错误'))
    }

    return Expression.Array(ps, this.pos)
  }

  /**
   * Attribute-Value Pair ::= propertyName: propertyValue
   */
  Attr () {
    const name = this.GetToken()
    let value
    if (name.getType() === TokenType.Identy) {
      value = name.getValue()
    } else if (name.getType() === TokenType.Oper && name.getValue() === '"') {
      const expression = this.StringUnion().children[1]
      value = expression.value
    } else {
      throw new Error(this.GetExceptionMessage('JSON对象的key必须是属性名或字符串'))
    }

    const t = this.GetToken()
    if (t.getType() !== TokenType.Oper || t.getValue() !== ':') {
      throw new Error(this.GetExceptionMessage('JSON构建错误'))
    }

    const exp = this.Exp()
    return Expression.Attr(value, exp, this.pos)
  }

  /**
   * Parse string concatenation sequence
   */
  StringUnion () {
    let exp = Expression.Constant('', this.pos)
    let t = this.GetToken()
    // Object sequence
    while ((t.getType() === TokenType.Oper && t.getValue() === '{') || t.getType() === TokenType.String) {
      // If it's a string, return the concatenated result
      if (t.getType() === TokenType.String) {
        exp = Expression.Concat(exp, Expression.Constant(t.getValue(), this.pos), this.SOURCE, this.pos)
      } else {
        // Handle content inside the {}
        const objExp = this.Exp()
        t = this.GetToken()
        if (t.getType() !== TokenType.Oper || t.getValue() !== '}') {
          throw new Error(this.GetExceptionMessage("缺少'}'"))
        }
        // String concatenation
        exp = Expression.Concat(exp, objExp, this.SOURCE, this.pos)
      }
      t = this.GetToken()
    }
    this.tokens.push(t)
    return exp
  }

  /**
   * Get object and object expression by name
   */
  ObjectName (objName) {
    return Expression.Identity(objName, this.pos)
  }

  /**
   * Object path parsing, handles method calls, properties, and array indices
   */
  ObjectPath (inExp) {
    let objExp = this.ArrayIndex(inExp)
    let t = this.GetToken()
    while (t.getType() === TokenType.Oper && t.getValue() === '.') {
      const nameToken = this.GetToken()
      const n = this.GetToken()
      if (n.getType() === TokenType.Oper && n.getValue() === '(') {
        const name = nameToken.getValue()
        if (name === 'each') {
          objExp = this.For(objExp)
        } else {
          objExp = this.MethodCall(name, objExp)
        }
      } else {
        this.tokens.push(n)
        const pi = nameToken.getValue()
        objExp = Expression.Property(objExp, pi, this.pos)
        objExp = this.ArrayIndex(objExp)
      }
      t = this.GetToken()
    }
    this.tokens.push(t)

    return objExp
  }

  /**
   * Array index parsing with optional condition
   */
  ArrayIndex (objExp) {
    let n = this.GetToken()
    if (n.getType() === TokenType.Oper && n.getValue() === '[') {
      const exp = this.Exp()
      n = this.GetToken()
      if (n.getType() !== TokenType.Oper || n.getValue() !== ']') {
        throw new Error(this.GetExceptionMessage("缺少']'"))
      }
      return Expression.ArrayIndex(objExp, exp, this.SOURCE, this.pos)
    }
    this.tokens.push(n)
    return objExp
  }

  /**
   * For loop handling
   */
  For (obj) {
    const exp = this.CommaExp()
    const t = this.GetToken()
    if (t.getType() !== TokenType.Oper || t.getValue() !== ')') {
      throw new Error(this.GetExceptionMessage('函数调用括号不匹配'))
    }
    return Expression.For(obj, exp, this.SOURCE, this.pos)
  }

  /**
   * Method call handling with parameters
   */
  MethodCall (name, obj) {
    let t = this.GetToken()
    if (t.getType() === TokenType.Oper && t.getValue() === ')') {
      const ps = []
      return Expression.Call(obj, name, ps, this.pos)
    }

    this.tokens.push(t)
    const ps = this.Params()
    t = this.GetToken()
    if (t.getType() !== TokenType.Oper || t.getValue() !== ')') {
      throw new Error(this.GetExceptionMessage('函数调用括号不匹配'))
    }

    return Expression.Call(obj, name, ps, this.pos)
  }

  /**
   * Function parameter list handling
   */
  Params () {
    const ps = []
    let name = this.paramName()
    let exp = this.Exp()
    this.procParam(ps, exp, name)
    let t = this.GetToken()
    while (t.getType() === TokenType.Oper && t.getValue() === ',') {
      name = this.paramName()
      exp = this.Exp()
      this.procParam(ps, exp, name)
      t = this.GetToken()
    }
    this.tokens.push(t)
    return ps
  }

  /**
   * Parse parameter name
   */
  paramName () {
    const t = this.GetToken()
    if (t.getType() === TokenType.Identy) {
      const t1 = this.GetToken()
      if (t1.getValue() === ':') {
        return t.getValue()
      } else {
        this.tokens.push(t)
        this.tokens.push(t1)
        return ''
      }
    } else {
      this.tokens.push(t)
      return ''
    }
  }

  /**
   * Process parameters, especially if it contains the "data" object
   */
  procParam (ps, exp, name) {
    const delegate = this.createDelegate(exp)
    if (delegate.containsKey('data')) {
      ps.push(Expression.Constant(delegate, this.pos))
    } else {
      ps.push(Expression.Param(exp, name, this.pos))
    }
  }

  /**
   * Get exception message with position information
   */
  GetExceptionMessage (msg) {
    const result = this.SOURCE.substring(0, this.pos) + ' <- ' + this.SOURCE.substring(this.pos)
    return `${msg}：\n${result}`
  }

  GetToken () {
    // If there are tokens in the queue, return the last one saved
    if (this.tokens.length > 0) {
      return this.tokens.shift()
    }

    const sPos = this.pos
    let hasKeyword = this.pos < this.SOURCE_LENGTH
    let value = null

    if (hasKeyword) {
      value = this.SOURCE.charAt(this.pos)
    }

    // If it's '{', save the previous state and change to non-string state
    if (hasKeyword && value === '{') {
      this.inStrings.push(this.inString)
      this.inString = false
      const str = this.SOURCE.substring(this.pos, this.pos + 1)
      this.pos += 1
      return new Token(TokenType.Oper, str, sPos)
    }

    // If in string state, process the string
    if (this.inString) {
      const startPos = this.pos
      while (hasKeyword && value !== '{' && value !== '}' && value !== '"') {
        if (value === '\\') {
          const nextStrValue = this.SOURCE.charAt(this.pos + 1)
          if (this.pos + 1 < this.SOURCE_LENGTH && ['{', '}', '"', '\\'].includes(nextStrValue)) {
            this.pos++
          } else {
            break
          }
        }
        this.pos++
        if (this.pos < this.SOURCE_LENGTH) {
          value = this.SOURCE.charAt(this.pos)
        } else {
          hasKeyword = false
        }
      }
      if (!(hasKeyword && value === '{')) {
        this.inString = this.inStrings.pop()
      }
      const t = new Token(TokenType.String, this.SOURCE.substring(startPos, this.pos)
        .replace('\\$', '$')
        .replace('\\{', '{')
        .replace('\\}', '}')
        .replace(/\\"/g, '"')
        .replace('\\\\', '\\'), sPos)

      if (hasKeyword && value === '"') {
        this.pos++
      }
      return t
    }

    // Skip whitespace and comments
    while (hasKeyword && (value === ' ' || value === '\n' || value === '\t' || (this.pos < this.SOURCE_LENGTH - 2 && value === '/' && this.SOURCE.charAt(this.pos + 1) === '/'))) {
      if (value !== ' ' && value !== '\n' && value !== '\t') {
        this.pos += 2
        if (this.pos < this.SOURCE_LENGTH) {
          value = this.SOURCE.charAt(this.pos)
        } else {
          hasKeyword = false
        }
        while (hasKeyword && value !== '\n') {
          this.pos++
          if (this.pos < this.SOURCE_LENGTH) {
            value = this.SOURCE.charAt(this.pos)
          } else {
            hasKeyword = false
          }
        }
      }
      this.pos++
      if (this.pos < this.SOURCE_LENGTH) {
        value = this.SOURCE.charAt(this.pos)
      } else {
        hasKeyword = false
      }
    }

    // If we are at the end of the source, return an "End" token
    if (this.pos === this.SOURCE_LENGTH) {
      return new Token(TokenType.End, null, sPos)
    }

    if (value !== null) {
      // Handle number tokens
      if (value >= '0' && value <= '9') {
        const oldPos = this.pos
        while (hasKeyword && value >= '0' && value <= '9') {
          this.pos++
          if (this.pos < this.SOURCE_LENGTH) {
            value = this.SOURCE.charAt(this.pos)
          } else {
            hasKeyword = false
          }
        }

        if (hasKeyword && value === '.') {
          do {
            this.pos++
            if (this.pos < this.SOURCE_LENGTH) {
              value = this.SOURCE.charAt(this.pos)
            } else {
              hasKeyword = false
            }
          } while (hasKeyword && value >= '0' && value <= '9')
          const str = this.SOURCE.substring(oldPos, this.pos)
          return new Token(TokenType.Double, parseFloat(str), sPos)
        } else {
          const str = this.SOURCE.substring(oldPos, this.pos)
          return new Token(TokenType.Int, parseInt(str, 10), sPos)
        }
      } else if ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || value === '_' || value === '$' || /[\u4e00-\u9fa5]/.test(value)) {
        const oldPos = this.pos
        while (hasKeyword && ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || (value >= '0' && value <= '9') || value === '_' || value === '$' || /[\u4e00-\u9fa5]/.test(value))) {
          this.pos++
          if (this.pos < this.SOURCE_LENGTH) {
            value = this.SOURCE.charAt(this.pos)
          } else {
            hasKeyword = false
          }
        }
        const str = this.SOURCE.substring(oldPos, this.pos)
        switch (str) {
          case 'false':
          case 'true':
            return new Token(TokenType.Bool, Boolean(str), sPos)
          case 'null':
            return new Token(TokenType.Null, null, sPos)
          default:
            return new Token(TokenType.Identy, str, sPos)
        }
      } else if ((value === '&' && this.SOURCE.charAt(this.pos + 1) === '&') || (value === '|' && this.SOURCE.charAt(this.pos + 1) === '|')) {
        const str = this.SOURCE.substring(this.pos, this.pos + 2)
        this.pos += 2
        return new Token(TokenType.Oper, str, sPos)
      } else if (['+', '-', '*', '/', '%', '>', '<', '!'].includes(value)) {
        let str = ''
        if (this.SOURCE.charAt(this.pos + 1) === '=') {
          str = this.SOURCE.substring(this.pos, this.pos + 2)
          this.pos += 2
        } else {
          str = this.SOURCE.substring(this.pos, this.pos + 1)
          this.pos += 1
        }
        return new Token(TokenType.Oper, str, sPos)
      } else if (value === '=') {
        let str
        if (this.SOURCE.charAt(this.pos + 1) === '=' || this.SOURCE.charAt(this.pos + 1) === '>') {
          str = this.SOURCE.substring(this.pos, this.pos + 2)
          this.pos += 2
        } else {
          str = this.SOURCE.substring(this.pos, this.pos + 1)
          this.pos += 1
        }
        return new Token(TokenType.Oper, str, sPos)
      } else if ('()[],;.:@{}"'.includes(value)) {
        if (value === '"') {
          this.inStrings.push(false)
          this.inString = true
        } else if ((value === '{' || value === '}') && this.inStrings.length) {
          if (value === '{') {
            this.inStrings.push(false)
            this.inString = false
          } else {
            this.inString = this.inStrings.pop()
          }
        }
        const str = this.SOURCE.substring(this.pos, this.pos + 1)
        this.pos += 1
        return new Token(TokenType.Oper, str, sPos)
      } else {
        throw new Error(`Invalid token: '${value}'`)
      }
    }

    throw new Error('Unknown error while parsing.')
  }
}
