OpalはどうやってRubyコードをパースするか
本記事はOpal Advent Calendar 2016の13日目の記事です。
11日目のエントリではOpalのString以外クラスを読むと書いたのですが、実際読んでみるとあんまり解説することがない(Stringの回と似たような内容になってしまう)ことに気づいたので、今回はパーサを読むことにしました。
パーサはどこにある?
OpalはRubyからJavaScriptへのコンパイラです。コンパイラなのでパーサはホスト側言語(=Ruby)にあれば良い…と思いきや、Opalにはevalがあります。Kernel#evalが呼ばれた場合、Opalプログラムのパースを「実行時に」行う必要があります。このためパーサはJavaScriptかOpal自身で実装されていると予想できます。
ということを念頭に置きつつ、githubでparserで検索してみます。どうもlib/opal/parser.rbがそれのようです。あれ、lib以下ということはRuby用なのか?と一瞬思いましたが、if RUBY_ENGINE == 'opal'
という行があるので、RubyでもOpalでも動かせるようにしてあるようです。
lib/opal/parser.rb
中身を見ると、Parser::Ruby23というクラスを参照しています。ところがリポジトリにはruby23.rbのようなファイルがありません。調べてみると、masterではparserというgemを使うようになっているようです。
parser gemはRubyのコードをパースするためのRubyGemsです。リポジトリを見ると、Rubyのバージョンごとにruby24.y, ruby23.y, ...といったファイルが見つかります。これをもとにraccでパーサを生成しているわけですね。
生成されたコードはOpalでちゃんと動くのだろうか?と思いましたが、パーサジェネレータが生成するコードは決まった処理の繰り返しなので、むしろ普通のプログラムより動かすのは簡単だったかもしれません。
まとめ
- 通常は、Rubyからparser gemを使ってOpalプログラムのパースを行い、JavaScriptへの変換を行う
- 実行時にevalが呼ばれた場合は、「parser gemに含まれるruby23.rbをJavaScriptに変換したもの」を起動してパースを行う
おまけ: 0.10.x
上記のPull Requestは0-10-stableブランチには適用されていないため、次のminor update(0.11ですかね?)からparser gemが使われるようになるようです。
それ以前は、Opalが独自に.yファイルを持っていました。最新のopal gem 0.10.3ではこれが使われています。
おまけ: evalについて
このへんがソースのようです。