Rubyがコードを解析する様子を覗いてみよう
実用的なTipsから少し離れて、今回はRubyのちょっとした「お楽しみトリック」をご紹介します。
Rubyは実行前にコードを解析している
Rubyはプログラムを実行する前に、必ずそのソースコードを解析(パース)しなければなりません。この処理を担っているのがパーサーであり、その正体は一種のステートマシン(状態機械)です。
そして実は、あまり知られていないコマンドラインフラグを使うことで、このステートマシンが行うすべての動作をログとして出力できます。
-y フラグでパーサーの動きを可視化する
次のようなごく単純なコードを例にしてみましょう。
a = 1 + 2
このファイルを -y フラグ付きで実行すると、以下のような出力が得られます。
$ ruby -y sample.rb
Starting parse
Entering state 0
Reducing stack by rule 1 (line 903):
-> $$ = nterm $@1 ()
Stack now 0
Entering state 2
Reading a token: Next token is token tIDENTIFIER ()
Shifting token tIDENTIFIER ()
Enter state 35
Reading a token: Next token is token '=' ()
Reducing stack by rule 509 (line 4417):
$1 = token tIDENTIFIER ()
-> $$ = nterm user_variable ()
Stack now 0 2
Entering state 113
Next token is token '=' ()
Reducing stack by rule 100 (line 1764):
$1 = nterm user_variable ()
-> $$ = nterm lhs ()
Stack now 0 2
...
140 more lines
ログから読み取れること
この出力では、Rubyパーサーがファイル内のトークン(識別子や演算子などの意味的な最小単位)を1つずつ読み込みながら、次のような操作を繰り返し実行している様子がわかります。
- 読み込んだトークンをスタックに積む
- スタックの内容を文法ルールの一覧と照合する
- トークンがいずれかのルールに一致すれば、状態遷移(reduce)を行う
- 一致しなければ、さらに次のトークンをスタックに追加してもう一度試す
tIDENTIFIER や = といったトークンの読み取り、Reducing stack by rule ... のような還元処理が延々と続いているのが、ログからも見て取れます。
パーサーの正体は parse.y にある
こうした状態遷移や文法ルールはすべて、Rubyのソースツリーにある parse.y というファイルに定義されています。
このファイルは bison というパーサージェネレーターによって処理され、そこからC言語で書かれた実際のパーサーが自動生成される仕組みになっています。
bisonは古くからある yacc の系譜を受け継ぐツールであり、-y フラグの出力もまさにyaccデバッグモード由来のものです。普段ブラックボックスのように感じられる「Rubyがコードを理解する瞬間」が、このように目に見える形で観察できるのは面白いところです。
-
Rubyでの静的分析入門!parser gemでメソッド定義を抽出する方法
ソースコードを解析して、すべてのメソッドがどこで定義され、どんな引数を受け取るのかを把握したいと思ったことはありませんか? どうすれば実現できるのでしょうか? 最初に思いつくのは、正規表現(regexp)を書くことかもしれません。 しかし、もっと良い方法があるとしたらどうでしょう? 答えは「あります」! 静的解析(Static Analysis)とは、ソースコードそのものから情報を抽出するためのテクニックです。 これは、ソースコードをトークンへと変換する(パースする)ことで実現されます。 それでは早速見ていきましょう! parser gemを使う Rubyには標準ライブラリとしてRipper
-
Rubyプログラムのデバッグと修正方法を徹底解説|スタックトレースの読み方からPry・Byebugまで
書いたプログラムが、初回実行で思い通りに動くことってどれくらいありますか? 多くの場合、プログラムは期待どおりには動いてくれないもの。そんなときに頼りになるのがRubyのデバッグという技術です。原因を突き止めるための頼れる相棒といえるでしょう。 次のようなエラーメッセージを見たことはありませんか? undefined method some_method for nil:NilClass これは、nil値がコードの中に紛れ込んでしまったことを意味します。 本記事で紹介するテクニックを身につければ、この問題や似たようなトラブルにも自信を持って対処できるようになりますよ! エラーとスタックトレース