begin
  require 'yarvcore'
rescue LoadError
  exit
end

require 'rb/insns2vm'

module YARVCore
  class AOTCompiler
    def initialize
      @insns_def = insns_def_new

      def @insns_def.make_header_operands insn
        vars = insn.opes
        n = 0
        ops = []
        
        vars.each_with_index{|(type, var), i|
          if type == '...'
            break
          end
          if type == 'OFFSET'
            # label
            ops << "  #define #{var} INSN_LABEL_#{$operands[i].position}"
          else
            op = $operands[i]
            if expr = immediate_value(type, op)
              # embed immediate value
              ops << "  #{type} #{var} = (#{type})#{expr};"
            else
              # get from constant pool
              ops << "  #{type} #{var} = (#{type})GET_OPERAND(#{i});"
            end
          end
          n   += 1
        }
        @opn = n
        
        ops.reverse.join("\n") + "\n"
      end

      def @insns_def.immediate_value type, obj
        case type
        when 'VALUE'
          case obj
          when Fixnum
            "INT2FIX(#{obj})"
          when nil
            'Qnil'
          when true
            'Qtrue'
          when false
            'Qfalse'
          else
            nil
          end
        when 'long', 'ulong'
          obj.to_s
        when 'GENTRY'
          (obj & ~1).to_s
        else
          nil
        end
      end
    end
    
    # ruby script (file name String) => C source file String
    def compile file
      @constant_pool = []

      parsed = YARVCore::parse(File.read(file), file, 1)
      parsed = parsed.insns[0].operands[1]

      ret = ''
      ret << "/*\n"
      ret << parsed.disasm.map{|line| " * #{line}"}.join
      ret << "*/\n"
      
      idx = 0
      oidx= 0
      
      parsed.insns.each{|insn|
        if insn.kind_of? YARVCore::InstructionSequence::Instruction
          insn.operands.each_with_index{|e, i|
            add_constant_pool e, @insns_def.insns[insn.insn_id].opes[i][0]
          } if $operands = insn.operands
          p insn
          ret << "INSN_LABEL_#{idx}:;\n"
          ret << "{\n"
          ret << "#define OPERAND_IDX #{oidx}\n"
          ret << insn2c(insn.insn_id)     + "\n"
          ret << "#undef OPERAND_IDX\n"
          ret << "}\n"
          
          idx += 1 + (insn.operands ? insn.operands.size : 0)
          oidx+=     (insn.operands ? insn.operands.size : 0)
        end
      }
      ret
    end
    
    def insn2c insn_id
      insn = @insns_def.insns[insn_id]
      @insns_def.make_insn_def insn
    end

    def add_constant_pool op, type
      @constant_pool << [op, type]
    end
    
    def init_const_each idx, obj, type
      case type
      when 'VALUE'
        case obj
        when Fixnum, nil, true, false
          nil # "INT2FIX(#{obj})"
        when String
          "rb_str_new(#{obj.dump}, #{obj.size})"
        when Symbol
          "rb_intern(\"#{obj}\")"
        else
          "unsupported operand literal: #{obj}"
        end
      when 'ID'
        "rb_intern(\"#{obj}\")"
      when 'IC'
        'NEW_INLINE_CACHE_ENTRY()'
      else
        nil
      end
    end
    
    def constant_pool
      ret = "static VALUE constant_pool[#{@constant_pool.size}];\n"
      ret << "#define INIT_CONSTANT_POOL() \\\n"
      i = -1
      ret << @constant_pool.map{|e|
        i+=1
        e, t = *e
        expr = init_const_each(i, e, t)
        "constant_pool[#{i}] = #{expr};" if expr
      }.join("  \\\n")
    end
  end
end

if __FILE__ == $0
  aot_compiler = YARVCore::AOTCompiler.new
  open('compiled.inc', 'w'){|f|
    f.puts aot_compiler.compile(File.join(File.dirname($0), 'aotctest.rb'))
  }
  open('compiled_constantpool.inc', 'w'){|f|
    f.puts aot_compiler.constant_pool
    f.puts
  }
end
