次のページ 前のページ 目次へ

7. デバッグ

プログラムを、動かしながら学ぶような時は特に、デバッグ機能があること が重要になります。幸運にもYACCは、多数のフィードバックを返す機能を持っ ています。この機能はいくらかオーバーヘッドを必要とするので、使用にあたっ ては幾つかのスイッチをイネーブルにする必要があります。

文法をコンパイルする時は、YACC のコマンドラインに、--debug や --verbose をつけます。文法ファイルの C ヘッダ部には、以下 を付加します。

int yydebug=1;

これは 'y.output' という、出力されたステートマシンを説明するファイルを 生成します。

生成されたバイナリを実行すると、このファイルは、現在起こっていることに ついて、*非常にたくさんの* 情報を出力してくれます。これには、ステート マシンがどんなステートを保有しているのか、どんなトークンが読み込まれて いるのか等の情報も含まれます。

Peter Jinks は debugging によくあるエラーや、エラーへの対処の仕方などを載せています。

7.1 ステートマシン

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 をデバッグの章で触れた フラグを付けてコンパイルしてみてください。

7.2 コンフリクト: 'shift/reduce', 'reduce/reduce'

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 ファイルは、そんな時に非常に助 けとなります。


次のページ 前のページ 目次へ