Я пытаюсь узнать, как правильно исправить ошибку в Bison. Дело в том, что когда ввод верен, все работает до тех пор, пока ввод не будет неправильным. В этом случае он определяет все последующие входные данные как неправильные, даже если они верны. Кто-нибудь знает, как это решить?
%{
int yyerror(char *s);
int yylex(void);
//#define YYDEBUG 1
#include <stdio.h>
int linenum = 0;
%}
%token a_TOK b_TOK c_TOK d_TOK e_TOK f_TOK g_TOK h_TOK newline_TOK any_TOK
%%
X: S
| error newline_TOK { yyerrok; yyclearin; };
S: A B LF X;
A: a_TOK h_TOK | a_TOK b_TOK A c_TOK | a_TOK B c_TOK;
B: C D E;
LF: newline_TOK {
linenum += 1;
printf("Correct derivation - line %d!\n", linenum);
};
C: d_TOK | e_TOK;
D: f_TOK D | f_TOK;
E: B | g_TOK;
%%
int yyerror(char *s)
{
linenum += 1;
printf("Syntax error - line %d\n", linenum);
return 0;
}
int main(void)
{
#if YYDEBUG
yydebug = 1;
#endif
if (!yyparse())
printf("End of input reached\n");
return 0;
}
Есть три проблемы с вашей текущей грамматикой и восстановлением ошибок:
правило X
верхнего уровня с выдачей ошибок не является рекурсивным, поэтому после восстановления ошибки единственным допустимым продолжением является конец ввода. Все, кроме EOF, приведет к другой ошибке (которая будет отброшена до следующей новой строки для восстановления, а затем повторится процесс).
ваше рекурсивное правило S
(косвенно) праворекурсивно, поэтому весь ввод должен быть распознан и помещен в стек, прежде чем S
можно будет уменьшить, занимая строки справа налево. Итак, еще раз, восстановление ошибок может произойти только в конце ввода.
ваше рекурсивное правило S
не имеет базового варианта, поэтому оно никогда не может завершиться без ошибки - ввод без каких-либо ошибок получит синтаксическую ошибку при достижении EOF.
Исправление состоит в том, чтобы сделать ваше правило верхнего уровня леворекурсивным (с базовым регистром) и (пока вы это делаете, хотя это совершенно не обязательно, просто проще) избавиться от правила X
. Так что у тебя есть
S : /* epsilon */
| S A B LF
| S error newline_TOK { yyerrok; yyclearin; }
;
Теперь после устранения ошибки он может анализировать больше A B LF
строк.
Вы можете подумать, что S
в выдаче ошибки является странным, и на самом деле это не обязательно (можно удалить) без каких-либо изменений в поведении, если только вы не хотите добавить действие к правилу S A B LF
, которое потребляет $1
(возможно, чтобы связать все строки анализируются вместе в своего рода список или векторную структуру данных). В этом случае S
в выдаче ошибки необходим для передачи ранее проанализированных строк через восстановление ошибок в строки, проанализированные впоследствии.