module Risc

  # MethodCompiler (old name) is used to generate risc instructions for methods
  # and to instantiate the methods correctly. Most of the init is typed layer stuff,
  # but there is some logic too.

  class MethodCompiler < CallableCompiler

    def initialize( method )
      super(method)
    end

    #include block_compilers constants
    def constants
      block_compilers.inject(@constants.dup){|all, compiler| all += compiler.constants}
    end

    def source_name
      "#{@callable.self_type.name}.#{@callable.name}"
    end

    def get_method
      @callable
    end

    # sometimes the method is used as source (tb reviewed)
    def source
      @callable
    end

    # helper method for builtin mainly
    # the class_name is a symbol, which is resolved to the instance_type of that class
    #
    # return compiler_for_type with the resolved type
    #
    def self.compiler_for_class( class_name , method_name , args , frame )
      raise "create_method #{class_name}.#{class_name.class}" unless class_name.is_a? Symbol
      clazz = Parfait.object_space.get_class_by_name! class_name
      compiler_for_type( clazz.instance_type , method_name , args , frame)
    end

    def add_method_to( target )
      target.add_method( @callable )
    end

    def create_block(arg_type , frame_type)
      @callable.create_block(arg_type ,frame_type)
    end

    # create a method for the given type ( Parfait type object)
    # method_name is a Symbol
    # args a hash that will be converted to a type
    # the created method is set as the current and the given type too
    # return the compiler
    def self.compiler_for_type( type , method_name , args , frame)
      raise "create_method #{type.inspect} is not a Type" unless type.is_a? Parfait::Type
      raise "Args must be Type #{args}" unless args.is_a?(Parfait::Type)
      raise "create_method #{method_name}.#{method_name.class}" unless method_name.is_a? Symbol
      method = type.create_method( method_name , args , frame)
      self.new(method)
    end

    # determine how given name need to be accsessed.
    # For methods the options are args or frame
    def slot_type_for(name)
      if @callable.arguments_type.variable_index(name)
        type = :arguments
      else
        type = :frame
      end
      [type , name]
    end

    def add_block_compiler(compiler)
      @block_compilers << compiler
    end

    # return true or false if the given name is in scope (arg/local)
    def in_scope?(name)
      ret = true if @callable.arguments_type.variable_index(name)
      ret = @callable.frame_type.variable_index(name) unless ret
      ret
    end

  end
end