module Vm
  module Assignment

    def on_IvarAssignment( statement )
      value = assignment_value(statement)
      name = check_name(statement.name.name)
      index = @method.for_type.variable_index( name)
      raise "No such ivar #{name}  #{@method.for_type}" unless index
      value_type = @method.for_type.type_at( index )
      raise "Argument Type mismatch #{value.type}!=#{value_type}" unless value.type == value_type
      value_reg = use_reg(:value_type)
      add_slot_to_reg(statement , :message , :receiver , value_reg )
      add_reg_to_slot(statement , value , value_reg , index + 1 ) # one for type
    end

    def on_LocalAssignment( statement )
      do_assignment_for( statement , :local )
    end

    def on_ArgAssignment( statement )
      do_assignment_for( statement , :argument )
    end

    private

    def do_assignment_for( statement , type )
      value = assignment_value(statement)
      name = check_name(statement.name.name)
      index = @method.send( "has_#{type}" , name)
      raise "No such #{type} #{name}  #{@method.inspect}" unless index
      value_type = @method.send("#{type}s_type" , index )
      raise "Argument Type mismatch #{value.type}!=#{value_type}" unless value.type == value_type
      move_reg(statement , "#{type}s".to_sym , value , index)
    end

    def move_reg(statement , type , value , index)
      named_list = use_reg(:NamedList)
      add_slot_to_reg(statement , :message , type , named_list )
      add_reg_to_slot(statement , value , named_list , index + 1 ) # one for type
    end

    def assignment_value(statement)
      reset_regs # statements reset registers, ie have all at their disposal
      value = process(statement.value)
      raise "Not register #{v}" unless value.is_a?(Risc::RiscValue)
      value
    end
    # ensure the name given is not space and raise exception otherwise
    # return the name
    def check_name( name )
      raise "space is a reserved name" if name == :space
      name
    end

  end
end