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