Ruby構文木のオブジェクト化
普段はソースの奥底に隠れている構文木を、RubyオブジェクトとしてRubyの世界にひっぱりだすコードを書いてみた。こんな感じにつかえる。108つもあるRuby構文木ノードのうち、まだ半分くらいしか解析できてないけど。
irb(main):001:0> require 'rbnode' => true irb(main):002:0> def fact(n) irb(main):003:1> if n == 1 then 1 else n * (fact(n-1)) end irb(main):004:1> end => nil irb(main):005:0> n = RBNode.make(self,:fact) => #<RBNode::NODE_METHOD:0x40580efc @body=#<RBNode::NODE_SCOPE:0x40580e5c @next=#<RBNode::NODE_BLOCK:0x40580ca4 @block_body=[#<RBNode::NODE_ARGS:0x40580d1c @opt=nil, @cnt=1, @rest=-1>, #<RBNode::NODE_IF:0x40580b64 @cond=#<RBNode::NODE_CALL:0x40580ac4 @mid=:==, @args=#<RBNode::NODE_ARRAY:0x405809ac @block_body=[#<RBNode::NODE_LIT:0x4058090c @lit=1>], @alen=1>, @recv=#<RBNode::NODE_LVAR:0x40580a24 @cnt=2, @vid=:n>>, @else=#<RBNode::NODE_CALL:0x40580754 @mid=:*, @args=#<RBNode::NODE_ARRAY:0x4058063c @block_body=[#<RBNode::NODE_FCALL:0x405804fc @mid=:fact, @args=#<RBNode::NODE_ARRAY:0x4058045c @block_body=[#<RBNode::NODE_CALL:0x405803bc @mid=:-, @args=#<RBNode::NODE_ARRAY:0x405802a4 @block_body=[#<RBNode::NODE_LIT:0x40580204 @lit=1>], @alen=1>, @recv=#<RBNode::NODE_LVAR:0x4058031c @cnt=2, @vid=:n>>], @alen=1>>], @alen=1>, @recv=#<RBNode::NODE_LVAR:0x405806b4 @cnt=2, @vid=:n>>, @then=#<RBNode::NODE_LIT:0x40580844 @lit=1>>]>>, @noex=0> irb(main):006:0> pp n <METHOD @body=<SCOPE <BLOCK [<ARGS @opt=nil @cnt=1 @rest=-1>, <IF @cond=<CALL @mid=:== @args=<ARRAY [<LIT @lit=1>]> @recv=<LVAR @cnt=2 @vid=:n>> @else=<CALL @mid=:* @args=<ARRAY [<FCALL @mid=:fact @args=<ARRAY [<CALL @mid=:- @args=<ARRAY [<LIT @lit=1>]> @recv=<LVAR @cnt=2 @vid=:n>>]>>]> @recv=<LVAR @cnt=2 @vid=:n>> @then=<LIT @lit=1>>]>> @noex=0> => nil
木構造がわかりやすくなるようにNEWLINEとか消して、表示もシンプルにしてあるので注意。
[2007-03-11]追記
Coke d:id:sumim:20070307:p1 によるなんちゃって Ruby の実装に使えるかも…
というコメントを頂きましたが、Rubyの構文木を表示するだけなら、Typing Ruby(http://truby.sourceforge.jp/index.j.html)やRHG(http://i.loveruby.net/ja/rhg/)の中で使われているnodedumpが便利ですよ。CokeでRubyが動くと格好いいなあ。