如何知道在yylineno中打印哪一行?

时间:2017-01-08 16:53:55

标签: compiler-construction bison lex

我正在使用Lex和Bison解析器生成器。 我有我的.lex文件,它定义了语法和定义语义的.ypp文件。在我的.ypp中我有这句话:

Statement : Type ID ASSIGN Exp {check_types_match($1.type, $4.type)} SC
  • 类型可以是 int 布尔
  • ID是标识符。
  • ASSIGN是 = 符号。
  • Exp可以是很多东西,其中包括 Exp:true ,它将表达式的类型保存为布尔值。
  • SC是semlicon“; ”。
  • check_types_match检查类型不匹配并打印出错误的行(yylineno),如果有的话。

在这个简单的输入文件中:

int x = true 
;

我知道错误在第2行而不在第1行。如何让它在第1行中打印错误?

1 个答案:

答案 0 :(得分:1)

在您到达第2行的分号之前,语句不会被识别。因此,在调用check_types_match时,yylineno必须指向第2行。

如果要生成具有不同行号的错误消息,您当然需要确定应打印哪行。在这里,您至少有两种可能性,因为错误位于令牌int和令牌true之间。在这种情况下,这两个都在第1行,但如果程序文本是:

int x =
  true;

这些令牌中的一个应该被标记为导致错误似乎是合理的,因此问题减少到找出令牌出现在哪一行上。由于该令牌在减少发生时是古老的历史,因此唯一的方法是记住可能仍然需要的每个令牌的位置,通常每个令牌仍然在解析器堆栈上。

幸运的是,bison有一种简单的方法。如果需要,它将维护与解析器堆栈并行的位置堆栈,然后您只需引用@1即可访问令牌1的位置对象。更好的是,只需在bison文件中的某处使用对位置对象的引用即可说服bison维护此信息。所以你可以将你的行动改为:

Statement : Type ID ASSIGN Exp {check_types_match($1.type, $4.type, @1)} SC

(或@4,如果您认为将错误归咎于Exp更合适。

当然,它从未如此简单。还需要安排bison知道每个传入令牌的位置,并了解如何为新创建的非终端创建位置(例如上例中的Exp)。 )

由于位置对象可以指代令牌序列的位置(如在非终端的情况下),其可以分布在多条线上,因此位置对象指示起点和终点是正常的。 。此外,通常需要行号和列偏移来产生准确的错误消息。因此,默认位置对象具有以下类型:

typedef struct YYLTYPE {
  int first_line;
  int first_column;
  int last_line;
  int last_column;
} YYLTYPE;

默认情况下,计算非终端的位置对象,就像编写了类似

的内容一样
@$.first_line = @1.first_line;
@$.first_column = @1.first_column;
@$.last_line = @N.last_line;
@$.last_column = @N.last_column;

其中N是右侧最后一个语法符号的索引。 (由于bison没有任何符号来表示"语法符号的数量"并且不允许$N结构中的变量,您不能实际上写的是。但这就是想法。)

由于所有这些都非常符合你的要求,因此野牛的方面没有问题。但您还需要首先从flex获取信息。

如果使用依赖于全局变量的flexbison之间的简单接口,则与当前标记对应的位置对象的名称为yylloc(类似于{ {1}})。 yylval可以自动创建flex,但它不会自动将其存储在yylineno中,也没有任何内置机制来跟踪列号,也不处理返回令牌的情况分布在多条线上(例如,对于字符串常量,这可能是可能的)。

使所有基础设施正确有点超出了本问题的范围,因为您只询问行号信息。如果您只需要跟踪行号并且您没有多行令牌,那么将以下内容添加到每个flex规则就足够了:

yylloc

如果您有多行令牌,则可以使用以下代码:

yylloc.first_line = yylloc.last_line = yylineno;

这必须添加到每个令牌操作中,即使是那些不做任何事情的操作(评论和空白)。幸运的是,yylloc.first_line = yylloc.last_line; yylloc.last_line = yylineno; 有一个在每个操作开始时添加的宏,因此您不必使整个Flex文件复杂化。添加以下内容就足够了:

flex

(如果您最终也跟踪列号,则需要对其进行修改。)

您还需要确保将#define YY_USER_ACTION do { \ yylloc.first_line = yylloc.last_line; \ yylloc.last_line = yylineno; \ } while(0) 初始化为yylloc.last_line;否则,您的第一个令牌将从第0行开始。

有关详细信息,请阅读手册:

如果您使用的是可重入/纯扫描程序和解析器,那么您需要参考文档,了解如何在没有全局变量的情况下传递位置对象。请注意,1声明并不总是您想要的(如果您不使用可重入/纯扫描程序和解析器,则绝对不是您想要的。)