我们不知道如何跟踪 yacc 解析器中的错误。yylineno我们尝试在 lex 文件中使用并尝试添加%option yylineno,但它仍然不起作用,我们无法在 yacc 中访问这些变量。
error我们想要的只是使用yacc 和行号打印出语法错误。
这是我们的.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++;}
%%
Run Code Online (Sandbox Code Playgroud)
现在这是我们的.y文件
%{
#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;
}
Run Code Online (Sandbox Code Playgroud)
这些文件存在许多问题,但它们都不会阻止yylineno您的 bison 生成的解析器使用。
您对 的定义将会产生编译时警告yyerror。或者可能有几个警告。
首先,正确的签名是:
void yyerror(const char *msg);
Run Code Online (Sandbox Code Playgroud)
可以返回 anint但该值从未被使用;但是,您对函数的定义刚刚结束,因此编译器会抱怨没有返回任何值。此外,yyerror通常使用不可变的文字字符串参数进行调用;标准 C 允许将文字字符串传递给参数类型为非常量的函数,但不建议使用,编译器可能会发出警告。更重要的是,
fprintf(stderr,"At line %d %s ",s,yylineno);
Run Code Online (Sandbox Code Playgroud)
将%d(整数)格式应用于s(字符串)并将%s(字符串)格式应用于yylineno(整数);同样,这应该会产生编译时警告,如果您忽略该错误,您的程序可能会出现段错误。
最后(与 相关),如果您在输入中yylineno指定(如果您想计算行数,这是一个好主意),那么 Flex 生成的扫描仪将定义并初始化并为您进行计数。因此,您在文件中的定义将触发编译时错误(重新定义)。此外,当您显式递增( ) 时,您最终会重复计算行;将由扫描仪递增,然后由您的操作再次递增。我的建议:指定然后让 flex 为你做一切。您只需按照文件中的方式声明它(就像您所做的那样)。您只需添加到忽略的空白字符列表即可。%option yylinenoflexyylinenoyylineno.lyylinenoyylineno[\n] {++yylineno;}yylineno%option yylinenoexternbison\n
需要注意的是:yylineno直接使用 inbison意味着您不会获得语法错误的确切位置,因为bison生成的解析器通常会读取一个先行标记,并且yylineno此时已经更新为该标记末尾的行号bison注意到语法错误。有时这会产生误导,特别是在由于缺少标记而导致语法错误的情况下。
其他一些问题:
使用文字字符标记的风格(恕我直言)比定义标记名称并将bison其与flex文件协调要好得多。如果您只使用文字字符,那么两个文件更容易保持彼此同步;语法更具可读性;你不需要像这样的评论
/*OP CP = Opening Closing Parenthesis*/
Run Code Online (Sandbox Code Playgroud)
相反,只需')'在语法中使用,在词法分析器中您可以执行以下操作:
[][=+*/,(){}-] { return yytext[0]; }
Run Code Online (Sandbox Code Playgroud)
或者你甚至可以在最后使用默认规则:
. { return yytext[0]; }
Run Code Online (Sandbox Code Playgroud)与上述相关,以及我通常选择第二个选项(默认规则)的原因,您的词法分析器没有针对所有可能字符的规则,因此将使用 flex 提供的默认规则。flex 提供的默认规则是仅将无效字符回显到yyout。这绝不是您在实际编译器中想要的,结果是输入错误(或扫描器错误)被悄悄隐藏。最好使用像我上面建议的那样的默认规则,并通过%option nodefault避免使用 Flex 生成的默认规则来保护自己。使用 时%option nodefault,如果输入可能不匹配,flex 会向您发出警告;请不要忽略此警告。