Как пропустить остальные токены в строке после ошибки в Bison - PullRequest
0 голосов
/ 05 мая 2019

Я пишу заявку на назначение, которая использует Flex и Bison, чтобы определить, является ли утверждение действительным или нет.Обнаружив ошибку в утверждении, я хочу напечатать сообщение об ошибке и перейти к следующей строке, чтобы посмотреть следующее утверждение, но все, что я пытаюсь, не работает.

Исследуя онлайн, Бизон имеетвстроенный токен ошибки, который можно использовать для обработки ошибок.Используя ошибку '\ n' {yyerrok;}, я смогу добиться того, чего хочу, но это не работает.

Мой код Flex:

%{
  #include <cstdio>
  #include <iostream>
  using namespace std;

  #include "exp.tab.h"  // to get the token types from Bison

%}
%%

--.*                    ;
[a-zA-Z][a-zA-Z0-9]*    {yylval.print = strdup(yytext); return ID;}
;\s*                    {return EOL;}
[-+*/%]                 {yylval.print = strdup(yytext); return OP;}
=                       {return EQU;}
\(                      {return OPEN;}
\)                      {return CLOSE;}
[0-9]                   ;
\n                      ;
\r                      ;
.                       ;
%%

Мой бизонтокены и правила:

%union{

    char *print;

}

%token EQU
%token <print> ID
%token EOL
%token <print> OP
%token OPEN
%token CLOSE

%%

lines: line
    |   lines line
;

line: ass {cout << " VALID" << endl;}
    |   exp {cout << " VALID" << endl;}
    |   error '\n' {yyerrok;}
;

ass: id EQU {cout << " ="; } exp EOL {cout << ";";}
;

exp: term
    |   exp op term 
;

term: id 
    |   OPEN {cout << "(";} exp op term CLOSE {cout << ")";}
;

id: ID {cout << $1; }

op: OP {cout << $1; }


%%

Мой yyerror () просто печатает "Ошибка".

Мой ввод для анализа:

-- Good (valid) statements:

first = one1 + two2 - three3 / four4 ;
second = one1 * (two2 * three3) ;
one1 * i8766e98e + bignum
second = (one1 * two2) * three3 ;
third = ONE + twenty - three3 ;
third = old * thirty2 / b567 ;

-- Bad (invalid) statements:

first = = one1 + two2 - three3 / four4 ;
first = one1 + - two2 - three3 / four4 ;
first = one1 + two2 - three3 / four4
first = one1 + two2 ? three3 / four4 ;
second = 4 + ( one1 * two2 ) * ( three3 + four4 ;
third = one1 + 24 - three3 ;
one1 +- delta
sixty6 / min = fourth ;

Я ожидаю, что выводвыведите ошибку и перейдите к следующей строке

first =one1+two2-three3/four4; VALID
second =one1*(two2*three3); VALID
one1*i8766e98e+bignum VALID
second =(one1*two2)*three3; VALID
third =ONE+twenty-three3; VALID
third =old*thirty2/b567; VALID
first = Error
first = one1 + Error
first = one1 + two2 - three3 / four4 Error
first = one1 + two2 Error
.
.
.

Но когда я ее запускаю, она просто останавливается при первой ошибке Ошибка печати

first =one1+two2-three3/four4; VALID
second =one1*(two2*three3); VALID
one1*i8766e98e+bignum VALID
second =(one1*two2)*three3; VALID
third =ONE+twenty-three3; VALID
third =old*thirty2/b567; VALID
first = Error

Любая помощь будет принята, но в основном яЯ хочу знать, почему не работает правило ошибки \ n, и что я могу сделать, чтобы это исправить.

Ответы [ 2 ]

1 голос
/ 05 мая 2019

Поскольку ваш лексер игнорирует \n, указание синтаксическому анализатору пропускать токены до тех пор, пока он не увидит новую строку, заставит его пропустить остальную часть файла.

Однако вы можете (почти) сделать эту работу, заставив лексер распознавать символы новой строки, но только во время восстановления после ошибок. (Проверьте в действии значение \n и либо проигнорируйте его, либо отправьте.)

Но это иногда приводит к странным результатам, потому что токен, который выдает ошибку, может находиться на следующей строке, и в этом случае символ новой строки будет уже проигнорирован до того, как ошибка будет обнаружена. Например, здесь проблема заключается в пропущенной точке с запятой:

a = 1
while (a > 0) {
    …

Но эта ошибка будет обнаружена только после прочтения while. (Если следующий токен был, скажем, +, анализ должен продолжаться.) Таким образом, пропуск до конца строки означает продолжение анализа на третьей строке, таким образом вводя несбалансированную скобку.

Несмотря на это, это может быть интересным началом.

0 голосов
/ 05 мая 2019

Использование '\n' не работает, потому что ваш лексер никогда не возвращает '\n', поэтому в потоке токенов никогда не будет никаких токенов '\n'.В основном, если лексер игнорирует определенные символы, вы не можете использовать их в анализаторе никоим образом, в том числе для восстановления после ошибок.

Таким образом, вашими двумя вариантами будет прекращение игнорирования разрывов строк (вероятно, плохая идея, потому что тогдавы должны упоминать их везде в грамматике, где вы хотите разрешить разрывы строк) или использовать какой-то другой токен для восстановления после ошибок.Пропуск всего до следующей точки с запятой, вероятно, будет хорошей альтернативой (хотя это все равно не даст ожидаемого результата, поскольку не все строки заканчиваются точкой с запятой).

...