# 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_INPUT
。YY_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()
来获取行号信息,代码如下:
%{ | |
/** | |
* 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 输入出现错误时给出上述的错误信息了。