ミラクルリナックスにて。毎回会場をお貸ししてくださるミラクルリナックス社さんとよしおかさんに感謝します。
今回は第10章「パーサ」ということで、第2部に入りました。区切りもいいから途中参加を狙っている人はどうぞーというアナウンスがメーリングリストにあったことや、Yuguiさんの「はじめてのRuby」トークセッションでも紹介されたそうで、初参加の方がたくさんいて人数も30人弱くらいだったかな?最後にちょっと自己紹介でもしましょうかってことになって、全員少しずつ喋る機会があってこれは良かった。東京Rails勉強会ではポジションペーパーを書くことになっていますよね。ポジションペーパーはいまいち何書いていいのかわからなくて、あと自宅は印刷環境が貧弱だとか少々敷居が高く感じられるけれど、勉強会の最初にどういう目線で参加しているのかってことを話す時間があるのはいいかもしれませんね。
内容は、まずさわださんが RHG 10章の内容と照らし合せつつ parse.y を拾い読みする発表*1と、星一さんが 1.8 から 1.9 への文法の変更点と parse.y 上の変更を調べてみたという発表。
以下ほぼメモからそのまま箇条書きで転載。
- parse.y 10000 行あるよ。魔境だ。。。
- Yugui さんによる「3分でわかる yacc 速修」
- さわださん発表
- parser.y のユーザアクション部にみられる /*%%%*/ とか /*% ... %*/ というコメントは、ripper と ruby 本体で文法定義を共有するために、両方のユーザアクションを記述して、前処理で切り替えるようになっているらしい。知らなかった。
- おおざっぱな構成
- stmt
- alias
- if修飾子
- 右辺がかっこなしメソッド呼び出しの代入
- expr 規則
- expr
- and/or
- !/not
- arg 規則
- arg
- primary
- リテラル
- 変数参照
- メソッド呼び出し
- begin ... end
- 制御構造
- クラス定義、メソッド定義
- %parse-param でパーサの情報をグローバル変数じゃなくてヒープにとれるようにしている。このへんはオライリーのyacc/lex入門がいいと思う。ちょっと古いけど。あとは bison の info くらいしか情報がなかった気がする。
- ついでにそれを struct RData でラップして ruby のオブジェクトにしている。そういえば後でゴルフ場オーナーこと id:shinichiro_h さんがこれくらい自分で管理すればいいんじゃね、ということをおっしゃってましたが、 struct parser_param の中に VALUE 型で ruby のオブジェクトを参照しているものがあるので GC の mark フェーズでそれらもマークしてあげないといけないので、それを一番簡単に実現する方法が struct RData で wrap することなんだと思います。確か作成する構文木のノード自体が GC 対象になると思うので(ってこれは 1.8 の時代の話かも)、パーサ用構造体内部を ruby のメモリ管理から完全に独立させるのは面倒そう。
- スキャナの実装について(yylex)
- ファイルのパースの時と文字列(メモリ上)のパースの時で使用する関数が違う。関数テーブルでポインタを切り替えることで多態化しているという kernel の vfs とかでよく見られるあれで多態化している。
- 文字列リテラルの中の式の埋め込み対応のあれこれ。
- ヒアドキュメントもちょっと面倒(RHG10章末尾のとおり)
- heredoc_restore とか
- 星一さん
1.8 *2の時代から既にパーサは魔境だったのでできるだけ避けていました。自分で何度か yacc(bison) や racc を使った今はちょっとはわかるようになっていて安心しました。それでも条件付きのスキャナとかなんかすごく頑張ってて、読めば読めそうだけど非常に面倒そう。あれだけのパーサで、shift/reduce conflict が1つもないとか凄すぎる。
次回は 12章「構文木の構築」。予定は 8/24(日)。
勉強会後は新しいメンバたくさんで懇親会になったようですが、明日は久々の客先で体調を万全にしておきたかったので今日は欠席。また次回お会いしましょう。