RubyVM::AST に関するメモ書き

hkdnet.hatenablog.com

作ったけど微妙ですわこれ(手のひら返し
使い始めたら、 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}

https://github.com/hkdnet/ruby/compare/b7595f2c2ec14389170808dfb36b1d99c9d0e899...814746725cbb26d139feb9f3a5cc74b22b200dd0?expand=1

RubyVM::AST::Node の children を作成するときにいい感じに switch 文があったので拝借して、必要な情報を足すようにしました。extrainfo とかいう名前は仮置きだからまあ……。そして node type のあまりの多さにおののいています。

ちなみに、この辺の #define によって「 nd_mid って結局 RNode 構造体のどこにあるんだっけ?」という疑問が解消されます。これめちゃくちゃ便利。見つけられてなかったら死んでた。最高!

ruby/node.h at trunk · ruby/ruby · GitHub