RubyVM::AST を用いた Ruby 製 Ruby インタプリタを作っている話
Ruby では v2.6 から RubyVM::AST というのが入ることになりまして、Ruby ランタイム上から AST を取得できるようになります。この話は以前に書いたので省略しますが、ASTがとれるということはASTを解釈して実行することもできるわけです。やるしかないですね。
というわけでやったのがこれです。まだ全然途中ですが。
デモ
if 式を解釈できるようす
# spec/snippets/if.rb a = true if a puts "truthy" else puts "falsy" end b = false if b puts "truthy" else puts "falsy" end
$ bin/stray spec/snippets/if.rb truthy falsy
メソッドを定義したりできてるようす
# spec/snippets/method.rb def foo puts :foo end foo def bar(arg) puts arg end bar :bar def blk yield 1 end blk do |arg| puts arg end
$ bin/stray spec/snippets/method.rb foo bar 1
進捗
プロジェクトとしての進捗はこんな感じです。今回は紹介程度に留めて、実装時に留意したことなどは別エントリに書きます。インタプリタの実装をするのは初めてだったのでいろんな壁にぶつかりながらやってます。
できること
- Integer, String, Symbol, Array あたりのリテラル
- Float とかそういうのはちょっと面倒なんでやりません
- ローカル変数代入
- メソッド定義
- オプショナル引数、キーワード引数はまだ
- メソッド呼び出し
- yield
- if 式
- ぼっち演算子
できないこと
- クラス定義
- サボってます
rb_cClass
とかの定義を見てそれに合わせないとダメなのはわかってるんだけどオレオレ実装してしまったので修正が面倒……。- 当然継承とか include とか extend とか using ( refinements )とか
- Kernel モジュール、Objectクラスに定義されてるだいたいのもの
- 実装するのがめんどくさくて
puts
くらいしかやってない
- 実装するのがめんどくさくて
- case 式
===
をちゃんとするのが単純にめんどくさそうでやってない
- lambda とか
ちょっと外れた話
cruby のソースを読んでたらフォーマットが他と違うところを発見したのでPRしました。
Use nd_X shorthand for annotation by hkdnet · Pull Request #1901 · ruby/ruby · GitHub
また、実装していると NODE_DEFN
から nd_mid
を取れないことに気づきました。 NODE_DEFN
は def foo; end
のようなメソッド定義を示すノード種別です。nd_mid
はメソッド名に対応するシンボルです。先程の例だと :foo
という情報です。これがないとなんのメソッドを定義したのかわかりません。 twitter で y.kaneko さん( @spikeolaf
)に質問したら快く修正してくれました。
hkd氏におどされ、なくなくコミット。
— y.kaneko (@spikeolaf) June 28, 2018
新しい機能を実際に使ってみた結果をフィードバックをする人はたぶん少ないので意味がありそうですね。