C 版本的 flex/bison 错误打印的教程可以参考:How to implement better error message for flex/bison

如果不了解原理的话,建议先参考上述 C 版本的代码理解下错误信息打印的原理。如果已经了解原理,仅仅是为了解决 bug,可以直接照抄下面的代码。

最终效果为出错时打印下列信息:

  1. 错误信息
  2. 错误行号、列号
  3. 出现错误的行内容
error: syntax error in line 35, column 24
type existfile(string)[];
_______________________^

# flex

flex 添加如下额外代码:

Flex
%{
#define YYERROR_VERBOSE 1
int column = 1; 
extern int yylval;
static int next_column = 1;
#define HANDLE_COLUMN column = next_column; next_column += strlen(yytext)
std::string line;
size_t consumed = 0;
size_t available = 0;
char ch;
size_t min(size_t a, size_t b);
#define YY_INPUT(buf, result, max_size) {\
    if (available <= 0) {\
        consumed = 0;\
        if (std::getline(yyin, line)) {\
          if (!yyin.eof()) {\
            line += '\n';\
          }\
          available = line.length();\
        } else {\
            available = 0;\
        }\
    }\
    result = min(available, max_size);\
    std::strncpy(buf, line.c_str() + consumed, result);\
    consumed += result;\
    available -= result;\
}
%}
%option c++ noyywrap noinput nounput yylineno
%%
[\t ]+   { HANDLE_COLUMN; }  // 每个规则都需要添加 HANDLE_COLUMN;
[0-9]+   { HANDLE_COLUMN; yylval = atoi(yytext);  return NUM; } // 添加 HANDLE_COLUMN; 后跟原本规则对应的动作即可
\n       { HANDLE_COLUMN; next_column = 1; } //next_column = 1 因为换行了,“下一个列” 在新行中从 1 开始
.        { HANDLE_COLUMN; return yytext[0]; }
%%
size_t min(size_t a, size_t b) {
    return b < a ? b : a;
}

简单讲解下 YY_INPUT() :

YY_INPUT 是 Flex 中提供的一个宏,它用于自定义输入方式。默认情况下,Flex 通过 fread() 读取输入数据,但如果你希望从特定的来源读取数据(比如从内存缓冲区、网络流或者其他自定义方式),就可以重定义 YY_INPUT 。由于我们需要在 flex 读取时自己维护当前读取的列信息,所以我们需要重定义下 YY_INPUTYY_INPUT 的定义如下:

#define YY_INPUT(buf, result, max_size) \
    { \
        /* 你的自定义输入逻辑 */ \
    }
  • buf :Flex 期望读取数据的缓冲区( char* )。
  • result :读取的字节数,需要在 YY_INPUT 中赋值给它。
  • max_size :缓冲区的最大大小(Flex 允许的最多读取字节数)。

如果不重新定义的话,默认行为如下:

#ifndef YY_INPUT
#define YY_INPUT(buf, result, max_size) \
    if ((result = fread(buf, 1, max_size, yyin)) < 0) \
        YY_FATAL_ERROR("input in flex scanner failed");
#endif

我们这里通过重定义 YY_INPUT 来维护列信息。

# bison

此时,在 flex 中已经可以通过 yylineno 来获取行信息了,而由于 c++ 版本的 lexer 对象对 yylineno 做了封装,所以 parser 代码的处理和 C 版本有些不同,需要用 lexer.lineno() 来获取行号信息,代码如下:

Bison
%{
/**
 * There is a little bug in bison 3.8 that causes a [set but not used] 
 * warning to be emitted on the line below:
 *
 * [build] parser.cc:434:9: warning: variable 'yynerrs_' set but not used [-Wunused-but-set-variable]
 * [build]   434 |     int yynerrs_ = 0;
 * [build]       |         ^
 * 
 * This is a known issue and it has been fixed.
 * Patches: https://mail.gnu.org/archive/html/bison-patches/2022-08/msg00006.html
 */
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#define YYERROR_VERBOSE 1
extern int column;
extern std::string line;
%}
%%
    // some rules
%%
void yy::parser::error(const std::string& msg)
{
  std::cerr << "error: " << msg << " in line " << lexer.lineno() << ", column " << column << std::endl;
  std::cerr << line;
  // Print underscores to mark the error position
  for (int i = 0; i < column - 1; ++i)
    std::cerr << "_";
    
  std::cerr << "^" << std::endl;
}

这里的 lexer 是所使用的 lexer 实例,我这里继承 yyFlexLexer 并实现了自己的 MyLexer ,并实例化为 lexer ,所以 yy::parser::error 中使用 lexer.lineno() 来获取行号。

至此就可以在 flex/bison 输入出现错误时给出上述的错误信息了。


更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Gality 微信支付

微信支付

Gality 支付宝

支付宝