如何在Ruby中实现goto和label方法?

时间:2014-01-14 17:42:51

标签: ruby goto

作为关于DSL主题的家庭作业,我需要在Ruby中编写内联汇编程序。我知道The Joke Is On Us: How Ruby 1.9 Supports the Goto Statement,但我不能使用它。这是一个非常简单的实现,汇编器有四个寄存器 - axbxcxdx,保存整数值,我可以在其上执行某些操作,如设置它们的值(mov),比较两个寄存器(cmp),递增寄存器(inc),跳转到特定位置(jmp)和其他几个寄存器各种各样。界面将是这样的:

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

jmp方法将接受标签名称或其他功能之一的序列号。所以我的问题是:在块中:

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

如何跟踪当前的功能号码。我的实现大致如下:

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

我无法实施jmp又名goto方法。我的一个想法是使用一个哈希来保存所有被调用的方法及其参数,或者循环块,包含指令并根据全局变量中保存的条件执行或不执行方法,但我对此无能为力。因此,例如,有没有办法打破块并将每个指令保存在数组/散列中,然后根据其索引或类似的东西执行它。任何帮助表示赞赏。非常感谢你提前。

2 个答案:

答案 0 :(得分:7)

这是一个想法:预先“解析”你的汇编代码,然后执行它。这包括改变一些事情。这将是我对Memory类的实现:

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

让我们一步一步来看看。我没有使用每条指令的方法,而是使用lambdas的哈希值。然后,我使用method_missing做三件事:

  1. 如果方法名称只是一个随机符号(不是指令名称),我只返回符号。现在,cx:cx等效。
  2. 如果是指令名称,请将其和参数添加到程序数组
  3. 如果是label,请将下一条指令的索引添加到该名称下的标签哈希中。
  4. 指令lambda的返回值(如果不是nil)用作程序计数器的新值(如果可以添加零,则跳转更多跳转)。

答案 1 :(得分:4)

我会让每个函数调用将一个表示指令的对象推送到一个数组,然后在块结束时,你可以知道整个程序知道所有指令的位置(正如你的建议)。

对于标签,指令可能应该将标签添加到来自label => instruction index的哈希映射中,这样如果你得到标签,就可以快速查找跳转目标。