もうすでに文法ファイルがある場合です。この場合は単にraccコマンドが使えればよく、 文法ファイルの書きかたを覚えたりする必要はありません。
文法ファイルの名前が parse.y と仮定すると、コマンドラインから以下のように 打ちこむことで、パーサを含んだファイルが得られます。
$ racc parse.y生成されるファイルはデフォルトでは "ファイル名.tab.rb" になります。 これは -o オプションで変更できます。
自分でraccの文法ファイルを記述する場合です。 racc は yacc を知っていることを前提にしていますので、もし知らないのなら 先に yacc を勉強しましょう。いきなり racc を使うのは不可能です。(これは断言できます)
yacc は yyparse(関数)を生成しますが、racc は yyparse に相当する do_parse メソッドを持ったパーサクラスを生成します。 生成されるクラスは全て Racc::Parser の下位クラスで、規則ファイル中で指定します。 規則ファイルの文法の詳細は、文法リファレンスを 参照してください。
以下には規則ファイルの文法の概略だけ書いておきます。
まずは、全体の概形です。
class MyParser rule target: exp ; exp: tok { print val[0] } | exp tok { print val[1] }; # コメント tok: A | '+' | '-'; /* これもコメント */ endRubyスクリプトのように class でクラス名を指定し、rule ... end の間に文法を記述します。 トークンは Ruby のローカル変数/定数として有効なものが使えます。 yacc だと終端記号を %token で指定する必要がありますが、racc ではそのような 指定は必要ありません。左辺に来ないトークンはすべて終端記号とみなされます。
アクションは、yacc と同じように規則のあとに { と } で囲んで指定します。
当然ながら、アクションはRubyの文で記述します。
yacc での $$ は Racc ではローカル変数 result で、$1,$2... は配列 val です。
result は val[0]($1) の値に初期化され、アクションを抜けたときの result の値が
左辺値になります。Racc ではアクション中の return はアクションから抜けるだけで、
パーズ自体は終わりません。YYACCEPT のようなものもまだないので、
アクション中からパーズを終了するには catch か raise を使う以外にはありません。
YYACCEPT 相当の機能は将来には実装する予定です。
演算子の優先順位、スタートルールなどの yacc の一般的な機能も用意されています。 ただしこちらも少し文法が違います。
yacc では生成されたコードに直接転写されるコードがありました。Racc でも同じように、 ユーザ指定のコードが書けます。racc ではクラスを生成するので、クラス定義の前/中/後の 三個所があります。Racc ではそれを上から順番に header inner footer と呼んでいます。 0.10.1 までは prepare inner driver でしたがかっこわるいので変更しました。 ただし、互換性のため前のままでも使えるようになっています。
inner では、Racc の yylex に相当するメソッド next_token を定義する必要があります。
このメソッドは、トークンシンボルとその値の二要素を持つ配列を返すようにします。
この配列を Racc が破壊することはありません。また、どこか他のところで使うことも
ありません。
スキャンが終了して、もう送るものがない場合は [false,false] を返してください。
[false, false] を受けとったらそれ以上 next_token は呼びだされません。
LALR パーザは別に文字列処理にだけ使われるものではありませんが、実際問題として パーザを作る場面ではたいてい文字列のスキャナとセットで使うことが多いでしょう。 Ruby ならスキャナくらい楽勝で作れますが、高速なスキャナとなると実は難しかったり します。そこで、高速なスキャナを作成するためのライブラリ が同梱されています。もし使えるようであればどんどん使ってください。
Racc には、error トークンを使ったエラー回復機能もあります。
yyerror() は Racc では Parser#on_errorで、
エラーがおきたトークンとその値、値スタックの 3 引数をとります。
on_error はデフォルトでは例外 ParseError を発生します。
これだけあればだいたい書けると思います。あとは、最初に示した方法でコンパイルし、
Rubyスクリプトを得ます。
うまくいけば問題ないですが、大きいものだと最初からはうまくいかないでしょう。
racc に -g オプションをつけてコンパイルし、@yydebug を true にすると
デバッグ用の出力が得られます。また、-v オプションをつけると、
状態遷移表を読みやすい形で出力したファイル(.output)が得られます。
どちらもデバッグの参考になると思います。
Racc の生成したパーザは動作時にランタイムルーチンが必要になります。
具体的には parser.rb と cparse.so それと amstd/ のスクリプトが少しです。
cparse.so は高速にパーズするための拡張モジュールで、amstd/ はユーティリティ、
parser.rb はそれらすべてのフロントエンドです。
これらのファイルを自分でセットアップするのはなかなか面倒です。
Racc をユーザみんなにインストールしてもらうのも一つの手ですが、
これではユーザにとって不親切です。
そこで、Racc では回避策をふたつ用意しました。
ひとつめは、ランタイムとそのインストーラをまるごと自分のソフトのパッケージに 含めてしまうことです。方法は簡単で、Racc パッケージのトップディレクトリに 入って次のように起動します。
ruby rtpack.rb TARGET/これで、ディレクトリ TARGET/ 以下に必要なファイルがインストールされます。 このとき、一度 TARGET/ 以下をクリアするので気をつけてください。
また、インストール時には、TARGET/ に移動して ruby setup.rb を実行すれば ランタイムをインストールできます。
この方法だと、速度を保ったまま、パーザを配布できるのが利点です。
racc に -E オプションをつけてコンパイルすると、必要なものを全部結合した ファイルが得られます。これだとファイルはひとつだけなので扱いが楽です (この形式のパーザが複数あったとしてもクラスやメソッドが衝突することは ありません)。
この方法だと、特別なことはなにもする必要がないので、非常に扱いが楽です。
ただし、cparse.so が使えませんので、必然的に動作は Ruby スクリプトのみになり
速度は低下します。
ただし、これも例外があって、配布先に既に Racc ランタイムがあるときは
自動的にそちらが使われます。
Copyright (c) 1999,2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>