require_relative "code"
require "support/hash_attributes"
module Vm

  # Because the idea of what one instruction does, does not always map one to one to real machine
  # instructions, and instruction may link to another instruction thus creating an arbitrary list
  # to get the job (the original instruciton) done
  
  # Admittately it would be simpler just to create the (abstract) instructions and let the machine 
  # encode them into what-ever is neccessary, but this approach leaves more possibility to 
  # optimize the actual instruction stream (not just the crystal instruction stream). Makes sense?
  
  # We have basic classes (literally) of instructions
  # - Memory
  # - Stack
  # - Logic
  # - Math
  # - Control/Compare
  # - Move
  # - Call
  
  # Instruction derives from Code, for the assembly api
  
  class Instruction < Code    
    def initialize  options
      @attributes = options
    end
    def opcode
      @attributes[:opcode]
    end
    
    def method_missing name , *args , &block 
      return super unless (args.length <= 1) or block_given?
      set , attribute = name.to_s.split("set_")
      if set == ""
        @attributes[attribute.to_sym] = args[0] || 1
        return self 
      else
        return super
      end
      return @attributes[name.to_sym]
    end
  end
  
  class StackInstruction < Instruction
    def initialize first , options = {}
      @first = first
      super(options)
    end
  end
  class MemoryInstruction < Instruction
    def initialize first , options = {}
      @first = first
      super(options)
    end
  end
  class LogicInstruction < Instruction
    #  result = left op right
    # 
    # Logic instruction are your basic operator implementation. But unlike the (normal) code we write
    #    these Instructions must have "place" to write their results. Ie when you write 4 + 5 in ruby
    #    the result is sort of up in the air, but with Instructions the result must be assigned 
    def initialize result , left , right , options = {}
      @result = result
      @left = left
      @right = right
      super(options)
    end
    # this is used to write code that looks like assignment
    #  So instructions can be created without the result (register) set, and this assigns where 
    # the reuslt after the fact, but usually in the same line
    # Example (with block b, and variables int,a,b):  b.int = a + b
    #  a + b actually creates an add instruction while the b.int=  assigns the result to int
    #  b.add( int , a , b) is an alternative (assmbler style) way of writing the same.   
    def assign left
      @result = left
      self
    end
  end
  class MathInstruction < Instruction
    def initialize first , options = {}
      @first = first
      super(options)
    end
  end
  class CompareInstruction < Instruction
    def initialize left , right , options  = {}
      @left = left
      @right = right
      super(options)
    end
  end
  class MoveInstruction < Instruction
    def initialize to , from , options = {}
      @to = to
      @from = from
      raise inspect unless from
      super(options)
    end
  end
  class CallInstruction < Instruction
    def initialize first , options  = {}
      @first = first
      super(options)
      opcode = @attributes[:opcode].to_s
      if opcode.length == 3 and opcode[0] == "b"
        @attributes[:condition_code] = opcode[1,2].to_sym
        @attributes[:opcode] = :b
      end
      if opcode.length == 6 and opcode[0] == "c"
        @attributes[:condition_code] = opcode[4,2].to_sym
        @attributes[:opcode] = :call
      end
    end
  end
end