Go to the first, previous, next, last section, table of contents.


エラーからの回復

構文解析エラーが起きた場合に、構文解析プログラムが止まることは、 通常望ましくありません。 たとえば、コンパイラは入力の残りを構文解析して他のエラーを探せるように エラーから回復するべきですし、電卓は次の式を受け入れるべきです。

入力を1行ごとに処理する単純な対話的構文解析器は、 エラーが発生した場合にyyparseが1を返し、 入力行の残りを無視し、 yyparseをもう一度呼び出してもかまいません。 しかし、コンパイラでこのように処理すると、 エラーに先立つ文法的文脈を忘れてしまうので不都合です。 深い関数呼び出しの中で構文エラーが発生した場合に、 コンパイラがエラーに続く行をソースファイルの先頭のように 扱うべきではありません。

特別なトークンerrorを認識するような規則を書くことによって、 構文エラーから回復する方法を定義できます。 errorは定義済みの終端記号で、自分で宣言する必要はなく、 エラー処理のために予約されています。 Bison構文解析器は、構文エラーが起こるたびに、errorトークンを生成します。 現在の文脈でerrorトークンを認識できる規則を書いていれば、 構文解析を継続できます。

例を示します。

stmnts:  /* 空文字列 */
        | stmnts '\n'
        | stmnts exp '\n'
        | stmnts error '\n'

第4の規則は、errorとそれに続く改行が、任意のstmntsに 続くことを認めます。

expの中で構文エラーが発生するとどうなるでしょうか。 エラー回復規則は、厳密に解釈され、stmntserror、改行からなる 列に適用されます。 expの中で構文エラーが起きると、おそらく、いくつかの余分なトークンまたは 部分式がスタック上の最後のstmntsの後に存在し、 したがって、次の改行を読む前に読むべきトークンが存在するでしょう。 したがって、この通常の方法では規則を適用できません。

しかし、意味文脈と入力の一部を捨てることによって、 Bisonは強制的にこの場合に規則を適用できます。 まず、errorトークンが受理可能な状態に戻るまで、 状態とオブジェクトがスタックから捨てられます (これは、すでに構文解析された部分式が捨てられ、 最後の完全なstmntsに状態が戻ることを意味します)。 この時点で、errorトークンがシフトされます。 そして、古い先読みトークンのシフトは受理不可能なので、 受理可能なトークンを見つけるまで、 構文解析器はトークンを読んで捨てます。 前述の例では、次の改行がくるまで入力が読み捨てられ、 したがって、第4の規則が適用可能になります。

文法中のエラー規則の選択は、エラー回復の戦略の選択です。 単純で便利な戦略の1つは、エラーを検出したら、 単純に現在の入力行または文を読み飛ばすことです。

stmnt: error ';'  /* エラーがあれば、「;」がくるまで読み飛ばす。 */

すでに構文解析された開き区切りトークン(14)を、閉じ区切りトークンに対応させることは、 エラー処理のうまい方法です。そうしなければ、閉じ区切りトークンが 後で不適切に現れて、別の重大なエラーを引き起こすでしょう。

primary:  '(' expr ')'
        | '(' error ')'
        ...
        ;

エラー回復の戦略は熟慮されるべきです。 戦略が悪いと、1つの構文エラーがしばしば別のエラーを引き起こします。 前述の例では、エラー回復規則は、1個のstmntの中で起きると 仮定されています。 そうでない可能性として、有効なstmntの中に誤ってセミコロンが まぎれ込んでいることを考えてみましょう。 エラー回復規則が最初のエラーを回復した後で、まぎれ込んだセミコロンに続く 入力も無効なstmntなので、もう1つの構文エラーがただちにみつかります。

エラー報告の洪水を避けるために、 最初の構文エラーが起きた直後の他の構文エラーに対しては、 構文解析器はエラー報告を表示しないべきでしょう。 3個の連続する入力トークンのシフトに成功してから、 エラー報告を再開するべきです。

errorトークンを受け付ける規則も、他の規則と同様に アクションを持てることに注意してください。

アクションの中でマクロyyerrokを使って、 ただちにエラー報告を再開できます。 もし、エラー規則のアクションの中でこのマクロを使えば、 エラー報告は抑制されません。 このマクロに引数は不要で、`yyerrok;'は有効なCの文です。

直前の先読みトークンは、エラーの直後に再解析されます。 これが不都合ならば、マクロyyclearinによって先読みトークンを 消してください。すなわち、エラー規則のアクションに `yyclearin;'文を書いてください。

たとえば、構文エラーにおいて、構文解析を再開すべき場所まで入力を進めるような、 エラー処理手続きを考えてみましょう。 字句解析器から返される次の記号は、おそらく正しいでしょう。 以前の先読みトークンは`yyclearin;'によって捨てられるべきです。

マクロYYRECOVERINGは、式を表し、構文解析器がエラーから回復する 途中にあるならば値が1になり、通常の状態ならば値が0になります。 値が1であるということは、新たな構文エラーに対するエラーの報告が、 現在は抑制されていることを示します。


Go to the first, previous, next, last section, table of contents.