error トークンがどうゆう感じの挙動をするのか細かく知りたい
ということで、
bison で生成されたコードを読む
エラーが検出される箇所
yybackup: /* Do appropriate processing given the current state. Read a look-ahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { // ■■■ ここ! ■■■ if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } : : yydefault: yyn = yydefact[yystate]; // ■■■ ここ! ■■■ if (yyn == 0) goto yyerrlab; goto yyreduce;
lookahead トークンを読み込んで、yytable[yypact[yystate] + yytoken] が 0 の場合(つまり、 lookahead トークンを読んでどうアクションすべきかというところで、 reduce (マイナス値)でも shift (プラス値)でもなく、エラー(0)の場合)に、 yyerrlab に飛んでいるというのが分かる。
あと、 yydefault の箇所でも error に行っている
yyerrlab ラベル
yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else // (略) #endif } // (略) /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1;
基本的には、エラー状態に入ったら yyerror を呼び出して、 yyerrlab1 に飛ぶだけ
もともとエラー状態の時に、ここに来てもほとんど何もしない。
yyerrlab1 ラベル
yyerrlab1: // ■ ここでエラーフラグをたてている yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { // ■ この yyn にトークン番号を足すと、アクションか分かる yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { // ■ error トークンのアクションがあるかを調べる yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { // ■ 今の状態に error トークンを shift する(yyn がプラス値だと shift)アクションがあったら break yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); // ■ error トークンを shift するアクションがなければ、一個状態を戻す YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; // ■ 値スタックに最後の yylval を積む *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); // ■ error トークンで shift した場合の状態に遷移する yystate = yyn; goto yynewstate;
yyerrstatus フラグは、いつ下ろされるか(yyerrstatus が 0 になるのはどこか)
yyerrlab ラベルに飛ぶところにもう一度戻ってみると、その直後で以下のようにコードが実行されている。
/* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--;
エラー状態とエラーじゃない状態の違い
エラー状態から一旦回復しないと、エラーが報告されない。見た感じそのくらい(?)
エラー状態を手動で回復する場合は
以下のように yyerrok と書くだけでいい。
statment: error ';' { yyerrok; }
yyerrok は
#define yyerrok (yyerrstatus = 0)
と定義されている。