如何为 flex/bison 实现更好的错误消息
How to implement better error messages for flex/bison
我需要为我正在编写的语法的语法错误提供正确的错误消息。我发现我可以为 flex 文件中的换行符定义一个规则(?不确定术语),增加行号计数器,我可以在 yyerror(const char*)
中使用它。但是,我还需要知道错误发生的确切位置,以便获得更好的错误消息。这就是我希望错误消息的样子:
Syntax error on line X:
SOME ERRONEOUS TEXT ON LINE X
_______________^
Expected other text.
如何获取列信息以及错误行上的文本?
提前致谢。
输出意外和预期的令牌
只需使用
#define YYERROR_VERBOSE 1
yyerror 输出已经类似于
syntax error, unexpected '+', expecting NUM or '('
打印行号
要打印当前行号,您可以使用 yylineno。您需要用
声明它
extern int yylineno;
在 .y 文件中。
在 .l flex 文件中您需要添加:
%option yylineno
打印列
要获取列信息,您必须跟踪词法分析器文件中的列。因此,在您读取一个标记后,您可以简单地添加标记的长度(例如,通过使用 strlen(yytext))。对于错误报告,您对令牌开始的列感兴趣,因此您需要设置第二个变量并在读取令牌之前记住列位置。
你可以为它使用一个简单的宏:
#define HANDLE_COLUMN column = next_column; next_column += strlen(yytext)
打印当前输入行
要打印当前输入行,必须自己跟踪。您可以自己从 yyin 中读取行,并通过相应地定义宏 YY_INPUT 在词法分析器中使用此数据。这个很好的答案 解释了它是如何工作的。
作者还展示了如何使用宏 YY_USER_ACTION 确定当前列的示例。
简单示例
一个简单、独立的计算器示例可以处理加法和减法,如下所示
输入 5+3+2+1 输出:
5+3+2+1
=11
错误的输入如“5+2++1”导致输出:
error: syntax error, unexpected '+', expecting NUM or '(' in line 3, column 5
5+2++1
____^
calc.l
%{
#include "y.tab.h"
extern int yylval;
static int next_column = 1;
int column = 1;
#define HANDLE_COLUMN column = next_column; next_column += strlen(yytext)
char *lineptr = NULL;
size_t n = 0;
size_t consumed = 0;
size_t available = 0;
size_t min(size_t a, size_t b);
#define YY_INPUT(buf,result,max_size) {\
if(available <= 0) {\
consumed = 0;\
available = getline(&lineptr, &n, yyin);\
if (available < 0) {\
if (ferror(yyin)) { perror("read error:"); }\
available = 0;\
}\
}\
result = min(available, max_size);\
strncpy(buf, lineptr + consumed, result);\
consumed += result;\
available -= result;\
}
%}
%option noyywrap noinput nounput yylineno
%%
[\t ]+ { HANDLE_COLUMN; }
[0-9]+ { HANDLE_COLUMN; yylval = atoi(yytext); return NUM; }
\n { HANDLE_COLUMN; next_column = 1; return '\n'; }
. { HANDLE_COLUMN; return yytext[0]; }
%%
size_t min(size_t a, size_t b) {
return b < a ? b : a;
}
calc.y
%{
#include <stdio.h>
int yylex(void);
void yyerror(const char *s);
extern int yylineno;
extern int column;
extern char *lineptr;
#define YYERROR_VERBOSE 1
%}
%token NUM
%left '-' '+'
%left '(' ')'
%%
LINE: { $$ = 0; }
| LINE EXPR '\n' { printf("%s=%d\n", lineptr, ); }
| LINE '\n'
;
EXPR: NUM { $$ = ; }
| EXPR '-' EXPR { $$ = - ; }
| EXPR '+' EXPR { $$ = + ; }
| '(' EXPR ')' { $$ = ; }
;
%%
void yyerror(const char *str)
{
fprintf(stderr,"error: %s in line %d, column %d\n", str, yylineno, column);
fprintf(stderr,"%s", lineptr);
for(int i = 0; i < column - 1; i++)
fprintf(stderr,"_");
fprintf(stderr,"^\n");
}
int main()
{
yyparse();
free(lineptr);
}
构建命令
根据您的系统,构建命令类似于以下内容:
flex calc.l
yacc -d calc.y
cc -Wextra -Wall lex.yy.c y.tab.c
我需要为我正在编写的语法的语法错误提供正确的错误消息。我发现我可以为 flex 文件中的换行符定义一个规则(?不确定术语),增加行号计数器,我可以在 yyerror(const char*)
中使用它。但是,我还需要知道错误发生的确切位置,以便获得更好的错误消息。这就是我希望错误消息的样子:
Syntax error on line X:
SOME ERRONEOUS TEXT ON LINE X
_______________^
Expected other text.
如何获取列信息以及错误行上的文本?
提前致谢。
输出意外和预期的令牌
只需使用
#define YYERROR_VERBOSE 1
yyerror 输出已经类似于
syntax error, unexpected '+', expecting NUM or '('
打印行号
要打印当前行号,您可以使用 yylineno。您需要用
声明它extern int yylineno;
在 .y 文件中。
在 .l flex 文件中您需要添加:
%option yylineno
打印列
要获取列信息,您必须跟踪词法分析器文件中的列。因此,在您读取一个标记后,您可以简单地添加标记的长度(例如,通过使用 strlen(yytext))。对于错误报告,您对令牌开始的列感兴趣,因此您需要设置第二个变量并在读取令牌之前记住列位置。
你可以为它使用一个简单的宏:
#define HANDLE_COLUMN column = next_column; next_column += strlen(yytext)
打印当前输入行
要打印当前输入行,必须自己跟踪。您可以自己从 yyin 中读取行,并通过相应地定义宏 YY_INPUT 在词法分析器中使用此数据。这个很好的答案
作者还展示了如何使用宏 YY_USER_ACTION 确定当前列的示例。
简单示例
一个简单、独立的计算器示例可以处理加法和减法,如下所示
输入 5+3+2+1 输出:
5+3+2+1
=11
错误的输入如“5+2++1”导致输出:
error: syntax error, unexpected '+', expecting NUM or '(' in line 3, column 5
5+2++1
____^
calc.l
%{
#include "y.tab.h"
extern int yylval;
static int next_column = 1;
int column = 1;
#define HANDLE_COLUMN column = next_column; next_column += strlen(yytext)
char *lineptr = NULL;
size_t n = 0;
size_t consumed = 0;
size_t available = 0;
size_t min(size_t a, size_t b);
#define YY_INPUT(buf,result,max_size) {\
if(available <= 0) {\
consumed = 0;\
available = getline(&lineptr, &n, yyin);\
if (available < 0) {\
if (ferror(yyin)) { perror("read error:"); }\
available = 0;\
}\
}\
result = min(available, max_size);\
strncpy(buf, lineptr + consumed, result);\
consumed += result;\
available -= result;\
}
%}
%option noyywrap noinput nounput yylineno
%%
[\t ]+ { HANDLE_COLUMN; }
[0-9]+ { HANDLE_COLUMN; yylval = atoi(yytext); return NUM; }
\n { HANDLE_COLUMN; next_column = 1; return '\n'; }
. { HANDLE_COLUMN; return yytext[0]; }
%%
size_t min(size_t a, size_t b) {
return b < a ? b : a;
}
calc.y
%{
#include <stdio.h>
int yylex(void);
void yyerror(const char *s);
extern int yylineno;
extern int column;
extern char *lineptr;
#define YYERROR_VERBOSE 1
%}
%token NUM
%left '-' '+'
%left '(' ')'
%%
LINE: { $$ = 0; }
| LINE EXPR '\n' { printf("%s=%d\n", lineptr, ); }
| LINE '\n'
;
EXPR: NUM { $$ = ; }
| EXPR '-' EXPR { $$ = - ; }
| EXPR '+' EXPR { $$ = + ; }
| '(' EXPR ')' { $$ = ; }
;
%%
void yyerror(const char *str)
{
fprintf(stderr,"error: %s in line %d, column %d\n", str, yylineno, column);
fprintf(stderr,"%s", lineptr);
for(int i = 0; i < column - 1; i++)
fprintf(stderr,"_");
fprintf(stderr,"^\n");
}
int main()
{
yyparse();
free(lineptr);
}
构建命令
根据您的系统,构建命令类似于以下内容:
flex calc.l
yacc -d calc.y
cc -Wextra -Wall lex.yy.c y.tab.c