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