简单的Ruby直接到机器码的生成器(X86) RPGMaker兼容
2016 年 5 月 7 日
毕竟Bellard大牛的OTCC里面就是到处飞机器码,并且,他是一边解析词法符号一边就直接产生了,真是棒棒的不按套路却又无比正经的写法
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | =begin Author: 晴兰@66RPG UID=131313 LICENSE: CC-BY-SA 3.0 [url]http://creativecommons.org/licenses/by-sa/3.0/deed.zh[/url] Purpose: Simple Machine Code Generator. Limited operation supported =end CallWindowProc = Win32API.new 'user32', 'CallWindowProcW', 'pLLLL', 'L' def prolog(num_of_var = nil)   [0x55, 0x8b, 0xec].pack("C*") + (num_of_var ? [0x83, 0354, num_of_var * 4].pack("C*") : "") end def epilog   [0xc9, 0xc2, 0x10].pack("CCS") end def render(list, num_of_var = nil)  prolog(num_of_var) + render_rec(list) + epilog end def check_var(list)   return -4 * list[1]  if list[0] == :var   raise "lvalue required for #{list.inspect}" end def render_rec(list)     case list.first     when :lit       [0xb8, list[1]].pack("CL")     when :arg       [0x8b, 0105, 4 + list[1] * 4].pack("C*")     when :do       list[1..-1].map{|x| render_rec(x)}.join     when :var       [0x8b, 0105, -4 * list[1] ].pack("C*")     when :assign       id = check_var(list[1])       render_rec(list[2]) + [0x89, 0105, id].pack("C*")     when :+, :-, :&, :^, :|       render_rec(list[2]) + "\x50" + render_rec(list[1]) +        [0132, "+|ab&-^".index(list.first.to_s) << 3 | 3, 0302].pack("C*")     when :<, :>, :'==', :'!=', :>=, :<=       v = list.first.to_s       l = v.length       f = v.ord rescue v[0]       u = "0000G+000000<HF>".index((f + (l > 1 ? 10 : 0)).chr)       render_rec(list[2]) + "\x50" + render_rec(list[1]) +        [0132, 073, 0302, 0x0f, 0x90 + u, 0300, 0x81, 0340, 0xff, 0x00, 0x00, 0x00].pack("C*")     when :nil; ""     when :if       cond, thenpart, elsepart = list[1..-1].map{|x| render_rec(x)}       thenpart += [0xe9, elsepart.length].pack("CL")       cond     += [0x85, 0300, 0x75, 0x05, 0xe9, thenpart.length].pack("CCCCCL")       cond     + thenpart + elsepart     when :for       init, cond, incr, body = list[1..-1].map{|x| render_rec(x)}       addbyte = [0x85, 0300, 0x75, 0x05, 0xe9, body.size + incr.size + 5].pack("CCCCCL")       fbody = body + incr +                [0xe9, -(body.size + incr.size + cond.size + addbyte.size + 5)].pack("CL")       init + cond + addbyte + fbody   end end x = render(  [:do,    [:assign, [:var, 1], [:lit, 0]],     [:for,         [:assign, [:var, 2], [:lit, 1]],        [:<, [:var, 2], [:lit, 101]],        [:assign, [:var, 2], [:+, [:var, 2], [:lit, 1]]],        [:assign, [:var, 1], [:+, [:var, 1], [:var, 2]]]    ],    [:var, 1]  ], 2 ) =begin  int var1 = 0;  for(int var2 = 1; var2 < 101; var2+=1)     var1 += var2;  return var1 =end p CallWindowProc.call x, 0, 0, 0, 0  #=>5050 |