トップ » 技術記事 » RHG 片手に Ruby 1.9 を読む集い(The RHG Strikes Back)に参加した(6) - 第7回 RHG の逆襲

RHG 片手に Ruby 1.9 を読む集い(The RHG Strikes Back)に参加した(6) - 第7回 RHG の逆襲

タグ: Ruby

どうも、あかさたです。8/24(日)に第7回 RHG の逆襲(Ruby 1.9 のソースコードを読む勉強会)が行われました。前回は参加できなかったのですが、今回は参加することができたので、その勉強会の内容をレポートします。

概要

勉強会の趣旨を簡単に説明します。2007/12/25 に Ruby 1.9 が発表されました。Ruby 1.9 は VM の搭載など大規模な変更が加えられています。Ruby のソースコードを読むための手引き書としては、Rubyソースコード完全解説(通称、RHG)という名著がありますが、Ruby 1.9 の変更により、その内容の多くが古くなってしまいました。

Ruby 1.9 は Ruby のソースコードに慣れている人でも戸惑うことが多くなるほどの変更を含んでいたため、それならば興味のある人が集まってソースコード読書会を行おうということになりました。主催者は Yugui さんです。

さて、当日の概要を紹介します。第7回も前回までと同じく新橋(汐留)のミラクル・リナックス株式会社さんの会議室で行われました。参加人数は16名(+ ささださんがSkype参加)、スクール形式に座って発表者が発表しつつ、適宜参加者が自由にツッコミを入れていくというスタイルです。

以下、発表者のリストです。

第7回 RHG の逆襲

今回の内容は RHG で言うところの「第12章 構文木の構築」に対応する部分の勉強を行いました。構文解析の部分は、RHG の時代と比較してもそれほど極端に変わっている場所ではないので、基本的には RHG に沿って読んでいきました。

話は前後しますが、第6回と今回(第7回)の勉強会は構文解析をテーマにしていました。パースして(第六回)、構文木を構築する(第7回)という流れになっています。構文木を作ってしまえば、あとは VM のバイトコード(ワードコード)にコンパイルして評価するという流れになります。これは次回以降です。

構文解析についてもう少し補足します。Ruby の構文解析を理解するには、yacc の構文規則の記述(前回)、スキャナ(前回)、意味解析(文法以外のエラーチェック)、構文木の構築を理解する必要があります。この流れに沿って勉強会の内容を紹介することにしましょう。

構文解析の攻略方法

Ruby は既に十分複雑な言語です。構文規則と意味解析を記述した parse.y が一万行あります。Ruby の文法を表現しているわけですから、全てを読んだとしても Ruby がそのように実装されていることが理解できるだけです。

既に Ruby のソースコードの概略をつかんでいる人が parse.y のメンテナを目指して全てを理解しようとするなら、全てのルールを読むことに意味はあると思います。しかし、まず Ruby のソースコードの概略を知りたいと言うことであれば、簡単な例を考えて範囲を限定して深掘りしていくようにしないと、あまりの長さに途中で挫折してしまうでしょう。

サンプルコード

def fname(args)
  statement
end

上記のようなソースコードがある場合、yacc を知っている人であれば、そのソースコードを追うことは難しくないはずです。まず、def を探します。def は「keyword_def」と定義されています。これを検索していくと、以下のような定義が見えてきます。

メソッドの定義(parse.y)

keyword_def fname
{/* アクションは省略 */}
f_arglist
bodystmt
keyword_end
{/* アクションは省略 */}

アクションとは、yacc では構文規則が認識されたときに、その構文に対応して処理を実行するのですが、その処理を記述する部分をアクションと呼びます。yacc の定義ファイルを理解するには、トークンの定義、構文規則、アクションを理解する必要があります。

アクションには、意味解析と構文木の構築が書かれています。アクションはC言語で記述されているので、そこから先は読んでいくことができます。

意味解析

これについては、RHG の第12章の「意味解析」が大変よくまとまっているので、これに付け加えることはほとんどありません。簡単に要約を書いておきましょう。意味解析を大きく分けると以下の二つに分かれます。

  • 文法チェック
  • 純粋な意味解析

BNFで書かれた構文規則があるのに、アクションで文法チェックを行うのはなぜでしょうか。これは、RHGによると構文規則を単純に書くためとわかりやすいエラーメッセージを表示するためだそうです。

もう一つ、純粋な意味解析とはどういうことでしょうか。静的な型付きの言語であれば、型チェックなどが行われます。Rubyは動的型言語ですから、構文チェック時に型チェックは行いません。以下を見てください。

Rubyの意味解析でエラーが出る例

a = retry

Ruby の意味解析は上記のコードに対して「void value expression」というエラーメッセージを出します。これは、retry が値を返さないにもかかわらず、戻り値を代入しようとしているからです。Rubyではこのような意味解析が行われます。(式が値を持つかどうかチェックしている部分は parse.y のvalue_expr_gen 関数です。)

もう一つ、最適化について触れておきましょう。勉強会で指摘があった話です。よくある言語だと、「1+2」のような式は、コンパイル時に「3」に変換してしまうことがあります。高速化のためです。Rubyでは演算子を動的に再定義できてしまうため、「1+2」が「3」になることを保証できません。従って、このような最適化は Ruby では行われません。

簡単な構文木の構築

Ruby の構文木は RNode という構造体で作られる構造体です。RNode の定義は Ruby 1.8 の頃から基本的に変わっていないので、RHG の内容はいまでもかなり有効です。重要なポイントを以下に挙げておきます。

  • Ruby のガーベージコレクタでメモリ管理を行うために、flags という属性がある
  • デバッグ用の情報の取り扱いが変わっている(後述)

RHG にあるサンプルを参考にして、構文木がどのように作られているか簡単に紹介します。メソッド定義だと既に複雑なので、以下のように数値リテラルが並んでいるだけのソースコードを構文木に変換するとどうなるか見てみましょう。

数値リテラルが並んでいるだけのソースコード

0
1

図「ノードツリー(by Kodougu)」

構文解析機から見ると、前述のソースコードは文(stmt)の集合(stmts)ということになります。文の中には数値リテラルがあります。上記の図ではノードは二つの子しか持っていないように書かれていますが、実際には 3 つ子を持つことができます。例としては、RHG の if に関する構文木の説明を読むと良いでしょう。

デバッグ用の情報を追う

構文木の Node にはスタックトレースのようなデバッグ情報を出力するために、行番号やファイル情報が含まれていなくてはなりません。flags の空いている領域に行番号は入っています。RNode には nd_file という属性があり、Ruby 1.8 まではこの属性を使っていましたが今は使われていません。

ファイル名はバイトコード(本当はワードコード)へのコンパイル時に、バイトコードの塊毎にファイル名を割り当てているようです。vm.c などでは rb_iseq_struct 構造体の filename 属性を使っているようです。この辺は、VM のコードを読み始めたらもう少し深掘りするかもしれません。

まとめ

今回は構文解析の意味解析と構文木の構築について勉強しました。他に 12 章に対応する内容として、「ローカル変数」のあたりは Ruby 1.9 でもそれなりに変わっているのですが、本記事では触れきれませんでした。

次回はいよいよ VM に踏み込んでいくはずです。スケジュールが合えば開発者のささださんも参加されるとのことです。予定では、次回勉強会は 2008/9/28(日)です。会場は第一回の第二会場になった東大の秋葉原拠点です。

主催者の皆様、有意義な勉強会をありがとうございました。次回も参加することができたら、本ブログで紹介したいと思います。それでは!

Series Navigation

執筆者紹介

あかさた

あかさた

未踏(2006年度下期)でWeb上で動作するモデリング環境 Kodougu の開発をしてました。こちらでもブログを書いています。

TrackBack URL :