作为主题DSL的作业,我需要在
Ruby中编写内联汇编程序.我知道
The Joke Is On Us: How Ruby 1.9 Supports the Goto Statement,但我不能使用它.这是一个非常简单的实现,汇编器有四个寄存器 – ax,bx,cx,dx,保存整数值,我可以在其上做一些操作,比如设置它们的值(mov),比较两个寄存器(cmp),递增a register(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 aka goto方法.我的一个想法是使用一个哈希来保存所有被调用的方法及其参数,或者循环块,包含指令并根据全局变量中保存的条件执行或不执行方法,但我对此无能为力.因此,例如,有没有办法打破块并将每个指令保存在数组/散列中,然后根据其索引或类似的东西执行它.任何帮助表示赞赏.非常感谢你提前.
解决方法
这是一个想法:预先“解析”汇编代码然后执行它.这包括改变一些事情.这将是我的Memory类的实现:
class Memory attr_reader :table def initialize @table = { ax: 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做三件事:
>如果方法名称只是一个随机符号(不是指令名称),我只返回符号.所以现在,cx与:cx相同.
>如果是指令名称,请将其和参数添加到程序数组中
>如果是标签,请将下一条指令的索引添加到该名称下的标签哈希中.