[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

6. エラーからの回復

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Error%20Recovery"
"bison/エラーからの回復"へのコメント(無し)

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

入力を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であるということは、新たな構文エラーに対するエラーの報告が、 現在は抑制されていることを示します。


[ << ] [ >> ]           [表紙] [目次] [索引] [検索] [上端 / 下端] [?]