yyparse()でsyntax errorと言われたときのデバッグ方法


yacc/lex(bison/flex)で生成したパーサ yyparse() は、シンタクスエラーのある入力ファイルを食わせると、デフォルトでは

[syntax error]

というそっけないエラーメッセージを吐いてエラー終了する。これではどこが間違っているのか分からないので、以下の3ステップでエラーになっている理由を探る。

YYERROR_VERBOSE

y.tab.cのコンパイル時に-DYYERROR_VERBOSEを指定する。

$(CC) -c $(CFLAGS) -DYYERROR_VERBOSE $(CINC) y.tab.c

すると、エラーメッセージが少し人間に優しくなる。

[syntax error, unexpected YYYY, expecting XXXX]

これは「次はXXXXのはずなのにYYYYが出てきましたよ」というエラー。
yaccの定義ファイル(*.y)は正しく、入力ファイルが間違っているとき、YYERROR_VERBOSEするだけで問題は解決する。

yacc -v

yaccの定義ファイルにバグがあって、プログラマが期待したようなパーサが生成されていない場合は、エラーメッセージをverboseにしただけでは問題解決できないことが多い。
yacc コマンドに -v オプションを付けると、y.tab.cを出力するディレクトリに、同時にy.outputというファイルを生成する。
ここにはパーサの状態遷移ルールが人間に読めるような形で記述されているので、入力ファイルと付き合わせると「なぜ、yaccはXXXXがある位置にYYYYが来ると考えたのか」が分かる。
y.outputはあまり読みやすいものではないのだが、初期状態が「state 0」であることだけ押さえておけば、何となく読めるような気がしてくる。

YYDEBUG

y.outputを見ても何が間違っているのか分からない場合は、main()に yydebug=1; を追加し、

extern int yydebug;
int main(int argc, char *argv[]) {
	yydebug = 1;
	...		


y.tab.c のコンパイルオプションに YYDEBUG を指定する。

$(CC) -c $(CFLAGS) -DYYERROR_VERBOSE -DYYDEBUG $(CINC) y.tab.c


すると、標準エラー出力にトレース情報を出力しながら動作するので、どのstateからどのstateに飛んで、何でこけたのか分かる。

Entering state 28
Reading a token: Next token is token AAAA ()
Shifting token AAAA ()
Entering state 19
Reading a token: Next token is token BBBB ()
Shifting token BBBB ()
Entering state 78
Reading a token: Next token is token XXXX ()Error: popping token YYYY ()