「CRuby Quest」クエスト~ParserとASTの旅
こんにちは、H2開発グループの今野です。
RubyKaigi 2026で購入した『CRuby Quest 〜Rubyのぼうけんのしょ〜』(youchan 著)という本を読んでいるので、今回はその話をしようと思います。後続の「ISeq編」「VM編」と併せて全三部作になる予定です。
『CRuby Quest』はどんな本か
この本は一般の書店で販売されているような出版社が発行する書籍ではなく、いわゆる技術系同人誌として頒布されているものです。もし「自分もチャレンジしてみたい!」と思った方は以下の販売ページから購入できます。
CRuby Quest 〜 Rubyのぼうけんのしょ 〜 - youchan - BOOTH
ゲームソフトのパッケージ風にデザインされた表紙に冒険心をくすぐられてワクワクしますね。裏表紙や章扉も含めて遊び心があふれるデザインになっているので、これはぜひ実物を見てほしいです!
まずはC言語の準備運動から
本の冒頭では、まずC言語を読むために必要な知識が説明されています。書名にある「CRuby」の”C”はC言語のCで、その名のとおり、私たちが普段 ruby コマンドで動かしている処理系はC言語で書かれています。AST編以降はそのCRubyのコードをたくさん読むことになるので、C言語に不慣れな方は、本文中にコードが出てきたらこの冒頭のパートに立ち返りながら読むとよいかもしれません。
とは言え、少なくともAST編まで読み進めた時点では適宜注目すべき行や関数が説明されているので、あまり気負いすぎずに読めると感じています。
Parser編:ミニパーサーを写経する
冒険のスタート地点となるParser編は、実際にRubyでミニパーサーを実装しながら、パーサーが「トークナイザー+AST(構文木)の出力」という2つの仕事をしていること、そしてそれらがざっくり何をしているかを理解するパートです。
これはぜひ手元にコードを書いて実行する環境を用意して、写経しながら読んでほしいところです。…と言いますか、むしろ写経しながら読まないと頭の中だけで理解するのはかなり大変だなと感じました(それこそパーサーを実際に作っている人でもなければ…)。出来上がったミニパーサーに渡すコード片を少しずつ変えて、どのような結果が返ってくるのかを見比べてみるのは結構楽しかったです。
120行のミニパーサーと、その「何十倍」の実際のパーサーの話
写経しつつミニパーサーを完成させると、だいたい120行程度のボリュームになります。この120行に、パーサーがどのような仕事をしているのかという要点がぎゅっとまとめられているのがとてもありがたいです。
その一方で本の中には「フルのRubyの実装を書こうと思ったら(中略)この何十倍ものコードを書く必要があります」という記述があります。何十倍と言われると気が遠くなりそうですが、裏を返せば数千行のオーダーには収まるということでもあり、そこは正直少し意外に感じました。実際に写経していると、一見同じように見える処理をちょっとずつ変えながら延々と積み上げていく作業が続きます。地道というか、泥臭いというか……この何十倍、と考えると、むしろ数千行で収まる方に驚いた、というのがコードを読んだ率直な感想でした。
そしてこの泥臭さは、AIがコードを書く時代だからこそ少し考えさせられるところでもありました。今のところは、AIが「いい感じにやってくれる」と言えるのはあくまで高レベル(=人間に近いレイヤー)の話です。その「いい感じに解釈された何か」をバイトコードのレベルまで持っていくには、どこかの時点で誰かが(それが必ずしも人間かはともかく)この手の泥臭い仕事を引き受けなければいけません。パーサーのコードを読むことは、その素朴な事実をあらためて確認する作業でもありました。
またミニパーサーの仕様定義の段で「規則を単純化するために(Rubyの文法では本来許容されているが)○○という制約をつけます」という趣旨の記述を読むにつけ、Rubyの柔軟な文法が本当に大変な努力のもと作られているんだなとも感じます。
余談:文字コードと戦っていた時代の話
文字を1つずつ読み進めていくパーサーのコードを写経していて、ふと思い出したことがあります。Rubyはメソッド名や変数名にマルチバイト文字が使えて、なんなら絵文字も通ります。頭では分かっていても、これにはいまだに少し驚いてしまいます。
とは言えUTF-8を扱える言語なら今どき当たり前かなぁと予想していましたが、調べてみると実はそうでもないようです。記事としてまとめるにあたって技術的な裏取りを手伝ってもらったAIからは「もちろんRubyだけの特徴ではないけど、絵文字1文字でもメソッド名として普通に成立するのはわりとキャラが立っている方では?」という趣旨のことを熱弁されたのが印象的でした。Rubyはマルチバイト文字について「文字として成立していれば識別子に使ってよい」という判定をしている1ので、絵文字もOK!となるわけですね。
そもそも私がこれに驚いてしまうのは、コメント部分を含むソースコードに日本語を書かない(書けない)ことに、まあまあ実用的な事情があった時代を知っているからです。Webアプリケーションの文脈で言えば、ひとつのシステムの中で「画面(ビューテンプレート)の文字コード」「ソースコードの文字コード」「メールの文字コード」がそれぞれ全部違う、ということが普通にありました。入力された文字列にちょっとしたチェックや操作をかけたいという要望を受けるだけでも、それはそれは大変だったわけです。何ならユニットテストの自動化が一般的になる前の時代の話ですからね…
インターネットの世界がUTF-8でほぼ統一された今、そのあたりを意識する機会はめっきり減りました。とはいえ、こうやって文字に着目したプログラムを書いていると、当時のことをうっすら思い出したりもします。
AST編:処理系の目線でコードを見る
続くAST編はRuby 3.4以降でデフォルトのパーサーになっているprismと従来のparse.yとが生成するASTを見比べつつ、ASTがどのようなものであるかを掘り下げていくパートとなっています。
私はRuby Goldの勉強をしていた頃、「このコード、なんでこういう動きをしてるの?」と思うたびに、Rubyの処理系が出力するS式を見ながら演算子の優先順位を確認していた覚えがあります(ということで、正確にはASTそのものを見ていたわけではないのですが…)。ASTをどのように出力するかは、本の中にも書いてあります。
実際に処理系のデバッグにも使われているだけあって、ASTを見れば処理系がどのようにプログラムを解釈するかが人間の目でも比較的把握しやすい構造になっています。またprismのASTは他の処理系や外部ツールで使われることも想定されているようで、これはエコシステムがより便利になっていくことにも繋がるので楽しみですね。
そんなASTをもとに著者のyouchanさん曰く「魔王城の鍵」となるISeqが生成されるわけですが、ここから先は次回をお楽しみに…!
それでは、次回ISeq編でまたお会いしましょう。よい冒険を!
-
具体的にprismの処理としてはこのあたりの話になります。https://github.com/ruby/ruby/blob/553f1675f3a9cece340b90f374a4245dccac2272/prism/prism.c#L1663-L1724 ↩
KCS キャロットBlog