RPPLとは、Read Parse Print Loopの頭字語である。(今考えた。)
次を~/.pryrc
に追記する。1
$__is_pry_rubyvm_ast_mode = false
def ast!
$__is_pry_rubyvm_ast_mode = !$__is_pry_rubyvm_ast_mode
end
Pry.config.hooks.add_hook(:before_eval, :"RPPL") do |code, _pry|
next if code == "ast!\n"
next unless $__is_pry_rubyvm_ast_mode
x = code.inspect
code.clear
code << "RubyVM::AbstractSyntaxTree.parse(#{x})"
end
そしてpryでast!
を実行する。
するとpryに打ち込んだコードがASTとなって出力されて、便利である。
$ pry
[1] pry(main)> 1 + 1
=> 2
[2] pry(main)> ast!
=> true
[3] pry(main)> 1 + 1
=> (SCOPE@1:0-1:5 tbl: [] args: nil body: (OPCALL@1:0-1:5 (LIT@1:0-1:1 1) :+ (ARRAY@1:4-1:5 (LIT@1:4-1:5 1) nil)))
[4] pry(main)>
https://qiita.com/hanachin_/items/d744e322b4a778574ed7 を参考にしたり、アイディアを得たりしている。
簡単な解説
pryはbefore_eval
フックでコードを実行する前にフックを実行できる。
exec_hook :before_eval, code, self
result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
フックにはevalする予定の文字列が渡される。
そしてこの文字列はevalにも同じオブジェクトが渡されるため、オブジェクトを破壊的に書き換えればpryが実行するコードを変えることができる。
文字列の書き換えは、String#clear
とString#<<
を使っている。clearで文字列を空にして、<<
でパースを行うコードを書き加えている。
雑に実装しただけだしまだ試しただけなので便利なのかもよくわからないが、ひたすらASTを観察したい時には便利な気がする。
ぱっと思いつく課題を挙げれば、取得したASTに対してメソッドを呼び出したい(RubyVM::AbstractSyntaxTree::Node#children
など)という需要は容易に考えられるが、この実装だとその需要を満たすことはできない。
真面目に使うのであれば、その辺を改善していくと良いだろう。
より良い実装やアイディアがあったらぜひ教えてほしい。
-
なおこのコードのライセンスはCC0とする。 https://creativecommons.jp/sciencecommons/aboutcc0/ ↩