プログラムを、動かしながら学ぶような時は特に、デバッグ機能があること が重要になります。幸運にもYACCは、多数のフィードバックを返す機能を持っ ています。この機能はいくらかオーバーヘッドを必要とするので、使用にあたっ ては幾つかのスイッチをイネーブルにする必要があります。
文法をコンパイルする時は、YACC のコマンドラインに、--debug や --verbose をつけます。文法ファイルの C ヘッダ部には、以下 を付加します。
int yydebug=1;
これは 'y.output' という、出力されたステートマシンを説明するファイルを 生成します。
生成されたバイナリを実行すると、このファイルは、現在起こっていることに ついて、*非常にたくさんの* 情報を出力してくれます。これには、ステート マシンがどんなステートを保有しているのか、どんなトークンが読み込まれて いるのか等の情報も含まれます。
Peter Jinks は debugging によくあるエラーや、エラーへの対処の仕方などを載せています。
YACC で生成した構文解析器は、内部的には 'ステートマシン' というものを 実行しています。名前が示すように、これは、いくつかのステート(状態)を とり得るマシン(機械)のことです。どのステートからどのステートへ遷移す るかを決定する規則、というのも存在します。全ては筆者が前述した、いわゆ る 'root' 規則が起点となります。
Example 7 の y.output からの出力を引用すると -
state 0
ZONETOK , and go to state 1
$default reduce using rule 1 (commands)
commands go to state 29
command go to state 2
zone_set go to state 3
このステートは、デフォルトでは 'commands' 規則を用いて還元します。これ は、前述した再帰に関する規則であり、個々のコマンド文、セミコロン、更に 続くコマンドから構成される 'commands' を定義しています。
このステートは、解釈できるトークン - この場合 ZONETOK 即ち 'zone' とい う単語 - に到達するまで還元します。それから、ステート 1 へ遷移し、zone コマンドをさらに詳しく処理します。
state 1
zone_set -> ZONETOK . quotedname zonecontent (rule 4)
QUOTE , and go to state 4
quotedname go to state 5
最初の行は現在の場所を示す '.' を含んでいます - ZONETOK は既に見つかっ たので、次に 'quotedname' を探しています。quotedname は QUOTE で始まる ので、ステート 4 に遷移することになります。
以上についてもう少し掘り下げたい方は、Example 7 をデバッグの章で触れた フラグを付けてコンパイルしてみてください。
YACC がコンフリクトについて警告を出す時は、何か問題がある時でしょう。 コンフリクトを解決する作業には、ときに職人芸のような特殊さがあり、あな たの使用している言語について多くのことを教えてくれることでしょう - そ れもあなたが知りたかったこと以上のことを。
問題は、連続するトークンをどう解釈するか、という点を中心に起こります。 以下の二つのコマンドを受け付ける必要がある言語を、定義するとしましょう。
delete heater all
delete heater number1
これをするには、以下の文法定義が必要です。
delete_heaters:
TOKDELETE TOKHEATER mode
{
deleteheaters($3);
}
mode: WORD
delete_a_heater:
TOKDELETE TOKHEATER WORD
{
delete($3);
}
もう問題の臭いがしてきましたね。ステートマシンは 'delete' という単語を 読むことから始めて、次のトークンが何であるかによって、遷移先を決定する 必要があります。次のトークンというのは、ヒーターの削除方法を指定するモー ド、もしくは削除すべきヒーターの名前です。
問題は両方のコマンドにとって、次のトークンが WORD になるということです。 YACC は、この場合どうして良いかわかりません。これが 'reduce/reduce'、 更には 'delete_a_heater' ノードに決して達することがない、という警告に なります。
この場合のコンフリクトは、簡単に解決できますが(最初のコマンド名を' delete heaters all' に変更したり、'all' を独立したトークンとして定義す るなど)、もっと複雑になることもあります。--verbose フラグを 付加してyaccを通すと生成される y.output ファイルは、そんな時に非常に助 けとなります。