在Bison错误后如何跳过一行中的其余令牌

时间:2019-05-05 06:00:58

标签: parsing error-handling bison flex-lexer

我正在为使用Flex和Bison来确定语句是否有效的作业编写应用程序。在语句中检测到错误后,我想打印一条错误消息并移至下一行以查看下一条语句,但是我尝试的所有操作均不起作用。

在线研究,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                      ;
.                       ;
%%

我的Bison令牌和规则:

%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 个答案:

答案 0 :(得分:1)

由于您的词法分析器忽略\n,因此告诉解析器跳过标记,直到看到换行符为止,这将导致解析器跳过文件的其余部分。

但是,您可以(几乎)通过使词法分析器识别换行符来实现此目的,但是只能在错误恢复期间进行。 (签入\n的操作,然后忽略它或将其发送。)

但是这有时会产生奇怪的结果,因为产生错误的令牌可能在下一行,在这种情况下,在检测到错误之前,换行符已被忽略。例如,这里的问题是缺少分号:

a = 1
while (a > 0) {
    …

但是该错误仅在读取while之后才能检测到。 (如果下一个标记是+,则解析应继续。)因此,跳到该行的末尾意味着继续在第三行进行解析,从而引入不平衡的括号。

即使如此,这可能也是一个有趣的开始。

答案 1 :(得分:0)

使用'\n'不起作用,因为您的词法分析器从不返回'\n',因此令牌流中将永远没有任何'\n'令牌。基本上,如果词法分析器忽略某些字符,则不能以任何方式在解析器中使用它们,包括进行错误恢复。

因此,您的两个选择是停止忽略换行符(这可能是一个坏主意,因为您必须在语法中允许换行的地方提到它们),或使用其他一些令牌来进行错误恢复。跳过所有内容直到下一个分号可能是一个不错的选择(尽管由于并非所有行都以分号结尾,所以仍然无法产生预期的输出)。