Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement goto and label methods in Ruby?

Tags:

ruby

goto

As a homework on the topic DSL, I need to write an inline assembler in Ruby. I am aware of The Joke Is On Us: How Ruby 1.9 Supports the Goto Statement, but I must not use it. It is a very simple implementation with the assembler having four registers - ax, bx, cx, dx, holding integer values, on which I can do some operations such as setting their value (mov), comparing two registers (cmp), incrementing a register (inc), jumping to a particular place (jmp) and a few others of the sorts. The interface will be something like this:

Asm.asm do
  mov cx, 1
  jmp l1
  mov ax, 1
  label l1
  mov dx, 1
end

The jmp method will accept either a label name or the sequential number of one of the other functions. So my question is: in the block:

{
mov cx, 1
jmp l1
mov ax, 1
label l1
mov dx, 1
}

how do I keep track of the current number of a function. My implementation looks roughly like this:

module Asm
  def self.asm(&block)
    memory = Memory.new
    memory.instance_eval(&block)
    memory.table.values
  end

  class Memory
    attr_reader :table

    def initialize
      @table = { ax: 0, bx: 0, cx: 0, dx: 0 }
      ...
    end

    ...

    def mov(destination_register, source)
      ...
    end

    def inc(destination_register, value = 1)
      ...
    end

    def dec(destination_register, value = 1)
      ...
    end

    ...
  end
end

I am stuck in implementing the jmp aka goto method. One idea I had was to use a hash holding all called methods and their arguments or to loop the block, containing the instructions and execute or not execute methods based on conditions saved in global variables, but I couldn't do much with that. So, for example, is there a way to break the block and save each instruction in an array/hash and then execute it based on its index or something similar. Any help is appreciated. Thank you very much in advance.

like image 867
Alexander Popov Avatar asked Jan 14 '14 17:01

Alexander Popov


2 Answers

Here's an idea: pre "parse" your assembly code and then execute it. This consists of changing a few things. Here would be my implementation of the Memory class:

class Memory
  attr_reader :table
  def initialize
    @table = { ax: 0, bx: 0, cx: 0, dx: 0 }
    @program = []
    @labels = {}
  end
  OPS = {
    "mov" => lambda {|dest, src| @table[dest] = (src.is_a?(Symbol) ? @table[src] : src); nil},
    "inc" => lambda {|dest, incval = 1| @table[dest] += incval; nil},
    "jmp" => lambda {|lblname| @labels[lblname]}
    #and so on
  }
  def method_missing(name, *args)
    if(OPS.keys.include?(name.to_s)) 
      @program << [name.to_s, args]
    elsif(name.to_s == "label")
      @labels[args.first] = @program.length
    else
      return name
    end
  end
  def load_program(&block)
    self.instance_exec(&block)
  end
  def run_program
    pc = 0
    until(pc == @program.length)
      instruction = @program[pc]
      retval = self.instance_exec(*instruction[1], &OPS[instruction[0]])
      if(retval)
        pc = retval
      else
        pc += 1
      end
    end
  end
  def asm(&block)
    load_program(&block)
    run_program
    @table
  end
end

Let's go over this step by step. Instead of having a method for each instruction, I use a hash of lambdas. Then, I use method_missing to do three things:

  1. If the method name is just a random symbol (not an instruction name), I just return the symbol. So now, cx is equivilent to :cx.
  2. If it's an instruction name, add it and the arguments to the program array
  3. If it's a label, add the index of the next instruction to the labels hash under that name.

The return value (if not nil) of an instruction lambda is used as the new value of the program counter (so that more jumps like jump if zero can be added).

like image 195
Linuxios Avatar answered Oct 01 '22 19:10

Linuxios


I would have each function call push an object representing the instruction on to an array then at the end of the block you evaluate the entire program knowing where all the instructions are (as you suggest).

For labels the instruction should probably add the label to a Hash mapping from label => instruction index so you can quickly look up the jump target if you get a label.

like image 25
Ron Warholic Avatar answered Oct 01 '22 20:10

Ron Warholic