構文解析エラーが起きた場合に、構文解析プログラムが止まることは、 通常望ましくありません。 たとえば、コンパイラは入力の残りを構文解析して他のエラーを探せるように エラーから回復するべきですし、電卓は次の式を受け入れるべきです。
入力を1行ごとに処理する単純な対話的構文解析器は、
エラーが発生した場合にyyparse
が1を返し、
入力行の残りを無視し、
yyparse
をもう一度呼び出してもかまいません。
しかし、コンパイラでこのように処理すると、
エラーに先立つ文法的文脈を忘れてしまうので不都合です。
深い関数呼び出しの中で構文エラーが発生した場合に、
コンパイラがエラーに続く行をソースファイルの先頭のように
扱うべきではありません。
特別なトークンerror
を認識するような規則を書くことによって、
構文エラーから回復する方法を定義できます。
error
は定義済みの終端記号で、自分で宣言する必要はなく、
エラー処理のために予約されています。
Bison構文解析器は、構文エラーが起こるたびに、error
トークンを生成します。
現在の文脈でerror
トークンを認識できる規則を書いていれば、
構文解析を継続できます。
例を示します。
stmnts: /* 空文字列 */ | stmnts '\n' | stmnts exp '\n' | stmnts error '\n'
第4の規則は、error
とそれに続く改行が、任意のstmnts
に
続くことを認めます。
exp
の中で構文エラーが発生するとどうなるでしょうか。
エラー回復規則は、厳密に解釈され、stmnts
、error
、改行からなる
列に適用されます。
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.