Ruby 1.9.0でインクリメント演算子もどきを実装
Rubinius で、Ruby にインクリメント演算子モドキを実装 - sumim’s smalltalking-tos (http://d.hatena.ne.jp/sumim/20080214)を読んで、Rubinusいいなー、うらやましいなーと思いました。でも、私の環境はCygwinなんでまだ実行できないので残念です。
Squeakもすごいなー、面白そうだなと思いました。こっちはWindowsでも動くから勉強せねばと思いました。
ふと、Ruby 1.9.0(YARV)でも出来ないかなと思い、作ってみました。現状ではInstructionSequence#loadが無効になっているので、この辺を有効にするパッチを当てる必要があります(http://d.hatena.ne.jp/miura1729/20080101/1199154900)。RubinusやSqueakと違い、環境を操作する方法が分からなかったので、incをsendするバイトコード命令付近を書き換えて強引に代入にしてしまうことで実現しました。sumimさんの文中のマクロを使った実現に近いんじゃないかなと思います。
def patch(iseqt) inst = iseqt[12] prev = nil last = inst[-1] inst = inst.reduce([]) {|res, ele| if ele[0] == :send and ele[1] == :inc then res.push prev.dup setinst = prev[0].to_s.gsub(/get/, 'set').intern res.push [:putobject, 1] res.push [:send, :+, 1, nil, 0, nil] prev[0] = setinst res.push prev res.push [:putobject, 1] prev = nil else if prev then res.push prev end prev = ele end res } iseqt[12] = inst + [last] iseqt end iseq = VM::InstructionSequence.compile_file(ARGV[0]) ARGV.shift iseqt = patch(iseq.to_a) VM::InstructionSequence.load(iseqt).eval
テストプログラムです。
a = 4
a.inc
p a
こんな感じの実行になります。
$ /usr/local/bin/ruby.exe -v ruby 1.9.0 (2007-12-29 revision 0) [i386-cygwin] $ /usr/local/bin/ruby.exe inc.rb test.rb 5