RubyVM::AST に関するメモ書き
作ったけど微妙ですわこれ(手のひら返し
使い始めたら、 children にアクセスするのに node_type とかそんなに意識したくないんだよなーということに気づきました
いまやろうとしてるのは RubyVM::AST を使って Ruby インタプリタを Ruby で書くことなんですけど。
例えば puts 1
という文字列をみて、「あーノード的にはFCALL呼び出しで、メソッドがこれなのね、はいはい」という気持ちで処理したいわけです。
なんだけども、実はメソッド名(= mid, ここでは puts
) って NODE としては情報持ってないんですよね。
$ ruby -v ruby 2.6.0preview2 (2018-05-31 trunk 63539) [x86_64-darwin17] $ ruby --dump=pa -e 'puts 1' ########################################################### ## Do NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ########################################################### # @ NODE_SCOPE (line: 1, location: (1,0)-(1,6)) # +- nd_tbl: (empty) # +- nd_args: # | (null node) # +- nd_body: # @ NODE_FCALL (line: 1, location: (1,0)-(1,6))* # +- nd_mid: :puts # +- nd_args: # @ NODE_ARRAY (line: 1, location: (1,5)-(1,6)) # +- nd_alen: 1 # +- nd_head: # | @ NODE_LIT (line: 1, location: (1,5)-(1,6)) # | +- nd_lit: 1 # +- nd_next: # (null node)
puts
は (1,0)-(1,4)
のはずなんだけどそうした位置情報を持つ NODE はありません。一方で nd_mid
という情報で :puts
はもっています。ここでいきなりメソッド名がシンボルになっていますが、Ruby は内部的なメソッド名などはシンボルとしてもっているので(たしか)そうなっています。
位置のほうに話をもどすと、引き算をしたりすればたぶんなんとかなるんですが puts 1
とか puts(1)
の差異とか考えたくないし AST っていうなら NODE_FCALL にまつわる mid もくださいよ!という気持ちになったのでした。そうすると c コード書く必要がありますね。
とゆーわけでとりあえず積んでるのがこのへん。これによって extrainfo とかやると Hash で情報が返ってきます。
$ ./miniruby -e 'p RubyVM::AST.parse("puts 1").children[1].extrainfo' {:mid=>:puts}
RubyVM::AST::Node
の children を作成するときにいい感じに switch 文があったので拝借して、必要な情報を足すようにしました。extrainfo とかいう名前は仮置きだからまあ……。そして node type のあまりの多さにおののいています。
ちなみに、この辺の #define
によって「 nd_mid って結局 RNode 構造体のどこにあるんだっけ?」という疑問が解消されます。これめちゃくちゃ便利。見つけられてなかったら死んでた。最高!