Bison 如何使用yacc解析器检测错误行号
我们不知道如何在yacc解析器中跟踪错误。 我们正在尝试在lex文件中使用Bison 如何使用yacc解析器检测错误行号,bison,yacc,lex,Bison,Yacc,Lex,我们不知道如何在yacc解析器中跟踪错误。 我们正在尝试在lex文件中使用yylineno,并尝试添加%选项yylineno,但它仍然不起作用,我们无法在yacc中访问这些变量 我们只想使用yacc中的error和行号打印出语法错误 这是我们的.l文件 %{ #include <stdio.h> #include <stdlib.h> #include "y.tab.h" int yylineno=1; %} %option yylineno identifier
yylineno
,并尝试添加%选项yylineno
,但它仍然不起作用,我们无法在yacc中访问这些变量
我们只想使用yacc中的error
和行号打印出语法错误
这是我们的.l
文件
%{
#include <stdio.h>
#include <stdlib.h>
#include "y.tab.h"
int yylineno=1;
%}
%option yylineno
identifier [a-zA-Z_][a-zA-Z0-9_]*
int_constant [0-9]+
delimiter ;
%%
"int" {return INT;}
{int_constant} return INT_CONST;
{identifier} return IDENT;
\= {return ASOP;}
\+ {return PLUS;}
\- {return MINUS;}
\* {return MULT;}
\/ {return DIV;}
\, {return COMMA;}
\( {return OP;} /*OP CP = Opening Closing Parenthesis*/
\) {return CP;}
\[ {return OB;} /*OB CB = Opening Closing Brace*/
\] {return CB;}
\{ {return OCB;} /*OCB CCB = Opening Closing Curly Brace*/
\} {return CCB;}
{delimiter} return DEL;
[ \t]
[\n] {yylineno++;}
%%
%{
#include <stdio.h>
#include <string.h>
#include "y.tab.h"
extern FILE *yyin;
%}
%token INT INT_CONST IDENT ASOP PLUS MINUS MULT DIV DEL COMMA CP CB CCB
%left OP OB OCB
%%
program: program_unit;
program_unit: program_unit component | component
component: var_decl DEL | func_decl DEL | func_defn ;
var_decl: dt list;
dt: INT;
list: list COMMA var | var
| error {printf("before ';' token\n"); yyerrok;}
| error INT_CONST {printf("before numeric constant\n"); yyerrok;};
var: IDENT
|IDENT init;
init: ASOP IDENT init | ASOP expr | ASOP IDENT ;
expr: IDENT op expr | const op expr | const | OP expr CP;
const: INT_CONST;
op: PLUS | MINUS | MULT | DIV;
func_decl: dt mult_func;
mult_func: mult_func COMMA mfunc | sfunc;
mfunc: IDENT OP CP;
sfunc: IDENT OP CP OCB func_body CCB;
func_body: program_unit;
func_defn: dt IDENT OP CP OCB func_body CCB
| IDENT OP CP OCB func_body CCB;
%%
int yyerror(char *s){
extern int yylineno;
fprintf(stderr,"At line %d %s ",s,yylineno);
}
int yywrap(){
return 1;
}
int main(int argc, char *argv[]){
yyin=fopen("test.c","r");
yyparse();
fclose(yyin);
return 0;
}
这些文件有很多问题,但没有一个会阻止bison生成的解析器使用
yylineno
您对yyerror
的定义将导致编译时警告。或者可能是几次警告
首先,正确的签名是:
void yyerror(const char *msg);
可以返回int
,但该值从未使用过;但是,函数的定义只是从结尾处掉下来,因此编译器会抱怨没有返回值。另外,yyerror
通常使用文本字符串参数调用,该参数是不可变的;标准C允许将文本字符串传递给参数类型为non-const的函数,但不建议使用此用法,编译器可能会发出警告。更重要的是,
fprintf(stderr,"At line %d %s ",s,yylineno);
将%d
(整数)格式应用于s
(字符串),将%s
(字符串)格式应用于yylineno
(整数);同样,这会产生编译时警告,如果忽略错误,程序可能会出错
最后(与yylineno
相关),如果您在flex
输入中指定%选项yylineno
(如果您想计算行号,这是一个好主意),那么flex生成的扫描仪将定义和初始化yylineno
,并为您进行计数。因此,在.l
文件中定义yylineno
将触发编译时错误(重新定义yylineno
)。另外,当您显式地递增yylineno
([\n]{++yylineno;}
)时,您会重复计算行数yylineno
将由扫描仪递增,然后由您的操作再次递增。我的建议是:指定%选项yylineno
,然后让flex为您做一切。您只需在bison
文件中将其声明为extern
(就像您所做的那样)。您可以将\n
添加到被忽略的空白字符列表中
一个警告:在bison
中直接使用yylineno
意味着您将没有语法错误的确切位置,因为bison
生成的解析器通常读取一个先行标记,当bison
注意到语法错误时,yylineno
将已经更新到此标记末尾的行号。有时这会产生误导,尤其是在缺少标记导致语法错误的情况下
其他一些问题:
- 更好的样式(IMHO)是使用文字字符标记,而不是在
中定义标记名,并将它们与bison
文件协调。如果只使用文字字符,那么这两个文件更容易保持同步;语法更具可读性;你不需要像这样的评论flex
相反,只需在语法中使用/*OP CP = Opening Closing Parenthesis*/
,在lexer中,您可以执行以下操作:”)
或者,您甚至可以在最后使用默认规则:[][=+*/,(){}-] { return yytext[0]; }
. { return yytext[0]; }
- 与上述内容相关,以及我通常选择第二个选项(默认规则)的原因,您的lexer没有针对所有可能字符的规则,因此将使用flex提供的默认规则。flex提供的默认规则是只将无效字符回显到
。在实际的编译器中,这永远不是您想要的,结果是输入错误(或扫描仪错误)被隐藏起来。最好使用我上面建议的默认规则,并通过使用yyout
来避免flex生成的默认规则来保护自己。使用%option nodefault
,如果存在输入不匹配的可能性,flex将向您发出警告;请不要忽视这个警告%选项nodefault
.l
和.y
文件现在与最近的文章放在一起了。你也可以给我们举个例子,埃文,最简单的一个。非常感谢你。