class SyntaxTree::YARV::Decompiler

walking through it to generate equivalent Ruby code.
This class is responsible for taking a compiled instruction sequence and

def decompile(iseq)

def decompile(iseq)
  label = :label_0
  clauses = {}
  clause = []
  iseq.insns.each do |insn|
    case insn
    when InstructionSequence::Label
      unless clause.last.is_a?(Next)
        clause << Assign(block_label.field, node_for(insn.name))
      end
      clauses[label] = clause
      clause = []
      label = insn.name
    when BranchIf
      body = [
        Assign(block_label.field, node_for(insn.label.name)),
        Next(Args([]))
      ]
      clause << UnlessNode(clause.pop, Statements(body), nil)
    when BranchUnless
      body = [
        Assign(block_label.field, node_for(insn.label.name)),
        Next(Args([]))
      ]
      clause << IfNode(clause.pop, Statements(body), nil)
    when Dup
      clause << clause.last
    when DupHash
      assocs =
        insn.object.map do |key, value|
          Assoc(node_for(key), node_for(value))
        end
      clause << HashLiteral(LBrace("{"), assocs)
    when GetGlobal
      clause << VarRef(GVar(insn.name.to_s))
    when GetLocalWC0
      local = iseq.local_table.locals[insn.index]
      clause << VarRef(Ident(local.name.to_s))
    when Jump
      clause << Assign(block_label.field, node_for(insn.label.name))
      clause << Next(Args([]))
    when Leave
      value = Args([clause.pop])
      clause << (iseq.type != :top ? Break(value) : ReturnNode(value))
    when OptAnd, OptDiv, OptEq, OptGE, OptGT, OptLE, OptLT, OptLTLT,
         OptMinus, OptMod, OptMult, OptOr, OptPlus
      left, right = clause.pop(2)
      clause << Binary(left, insn.calldata.method, right)
    when OptAref
      collection, arg = clause.pop(2)
      clause << ARef(collection, Args([arg]))
    when OptAset
      collection, arg, value = clause.pop(3)
      clause << if value.is_a?(Binary) && value.left.is_a?(ARef) &&
           collection === value.left.collection &&
           arg === value.left.index.parts[0]
        OpAssign(
          ARefField(collection, Args([arg])),
          Op("#{value.operator}="),
          value.right
        )
      else
        Assign(ARefField(collection, Args([arg])), value)
      end
    when OptNEq
      left, right = clause.pop(2)
      clause << Binary(left, :"!=", right)
    when OptSendWithoutBlock
      method = insn.calldata.method.to_s
      argc = insn.calldata.argc
      if insn.calldata.flag?(CallData::CALL_FCALL)
        if argc == 0
          clause.pop
          clause << CallNode(nil, nil, Ident(method), Args([]))
        elsif argc == 1 && method.end_with?("=")
          _receiver, argument = clause.pop(2)
          clause << Assign(
            CallNode(nil, nil, Ident(method[0..-2]), nil),
            argument
          )
        else
          _receiver, *arguments = clause.pop(argc + 1)
          clause << CallNode(
            nil,
            nil,
            Ident(method),
            ArgParen(Args(arguments))
          )
        end
      else
        if argc == 0
          clause << CallNode(clause.pop, Period("."), Ident(method), nil)
        elsif argc == 1 && method.end_with?("=")
          receiver, argument = clause.pop(2)
          clause << Assign(
            Field(receiver, Period("."), Ident(method[0..-2])),
            argument
          )
        else
          receiver, *arguments = clause.pop(argc + 1)
          clause << CallNode(
            receiver,
            Period("."),
            Ident(method),
            ArgParen(Args(arguments))
          )
        end
      end
    when Pop
      # skip
    when PutObject
      case insn.object
      when Float
        clause << FloatLiteral(insn.object.inspect)
      when Integer
        clause << Int(insn.object.inspect)
      else
        raise "Unknown object type: #{insn.object.class.name}"
      end
    when PutObjectInt2Fix0
      clause << Int("0")
    when PutObjectInt2Fix1
      clause << Int("1")
    when PutSelf
      clause << VarRef(Kw("self"))
    when SetGlobal
      target = GVar(insn.name.to_s)
      value = clause.pop
      clause << if value.is_a?(Binary) && VarRef(target) === value.left
        OpAssign(VarField(target), Op("#{value.operator}="), value.right)
      else
        Assign(VarField(target), value)
      end
    when SetLocalWC0
      target = Ident(local_name(insn.index, 0))
      value = clause.pop
      clause << if value.is_a?(Binary) && VarRef(target) === value.left
        OpAssign(VarField(target), Op("#{value.operator}="), value.right)
      else
        Assign(VarField(target), value)
      end
    else
      raise "Unknown instruction #{insn}"
    end
  end
  # If there's only one clause, then we don't need a case statement, and
  # we can just disassemble the first clause.
  clauses[label] = clause
  return Statements(clauses.values.first) if clauses.size == 1
  # Here we're going to build up a big case statement that will handle all
  # of the different labels.
  current = nil
  clauses.reverse_each do |current_label, current_clause|
    current =
      When(
        Args([node_for(current_label)]),
        Statements(current_clause),
        current
      )
  end
  switch = Case(Kw("case"), block_label.ref, current)
  # Here we're going to make sure that any locals that were established in
  # the label_0 block are initialized so that scoping rules work
  # correctly.
  stack = []
  locals = [block_label.name]
  clauses[:label_0].each do |node|
    if node.is_a?(Assign) && node.target.is_a?(VarField) &&
         node.target.value.is_a?(Ident)
      value = node.target.value.value
      next if locals.include?(value)
      stack << Assign(node.target, VarRef(Kw("nil")))
      locals << value
    end
  end
  # Finally, we'll set up the initial label and loop the entire case
  # statement.
  stack << Assign(block_label.field, node_for(:label_0))
  stack << MethodAddBlock(
    CallNode(nil, nil, Ident("loop"), Args([])),
    BlockNode(
      Kw("do"),
      nil,
      BodyStmt(Statements([switch]), nil, nil, nil, nil)
    )
  )
  Statements(stack)
end

def initialize(iseq)

def initialize(iseq)
  @iseq = iseq
  @block_label = BlockLabel.new("__block_label")
end

def local_name(index, level)

def local_name(index, level)
  current = iseq
  level.times { current = current.parent_iseq }
  current.local_table.locals[index].name.to_s
end

def node_for(value)

def node_for(value)
  case value
  when Integer
    Int(value.to_s)
  when Symbol
    SymbolLiteral(Ident(value.to_s))
  end
end

def to_ruby

def to_ruby
  Program(decompile(iseq))
end