Bison 如何使用yacc解析器检测错误行号

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

我们不知道如何在yacc解析器中跟踪错误。 我们正在尝试在lex文件中使用
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
    。在实际的编译器中,这永远不是您想要的,结果是输入错误(或扫描仪错误)被隐藏起来。最好使用我上面建议的默认规则,并通过使用
    %option nodefault
    来避免flex生成的默认规则来保护自己。使用
    %选项nodefault
    ,如果存在输入不匹配的可能性,flex将向您发出警告;请不要忽视这个警告


请向我们展示您的尝试。否则我们就不能告诉你你做错了什么。@Stephen C.先生,我们的
.l
.y
文件现在与最近的文章放在一起了。你也可以给我们举个例子,埃文,最简单的一个。非常感谢你。