如何打印我在 Yacc/Bison 中看到的任何内容?

How can I print whatever I see in Yacc/Bison?

我有一个复杂的 Yacc 文件,里面有一堆规则,其中一些很复杂,例如:

start: program
program: extern_list class
class: T_CLASS T_ID T_LCB field_dec_list method_dec_list T_RCB

确切的规则和我对它们采取的行动并不重要,因为我想做的似乎很简单:只需使用我为其他目的定义的规则打印出源文件中出现的程序.但令我惊讶的是这样做的难度。

首先,我尝试将 printf("%s%s", , ) 添加到上面的第二条规则中。这产生了“��@P��@”。据我了解,解析后的文本也可以作为变量 yytext 使用。我将 printf("%s", yytext) 添加到文件中的每个规则,并将 extern char* yytext; 添加到文件顶部。这根据语言的语法从有效文件中生成了 (null){void)1133331122222210101010--552222202020202222;;;;||||&&&&;;;;;;;;;;}}}}}}}}。最后,我把extern char* yytext;改成extern char yytext[],觉得没什么区别。它所产生的输出差异最好显示为屏幕截图

我在 Xubuntu 14.04 上使用 Bison 3.0.2。

如果您只想在解析时将源回显到某些输出,最简单的方法是在词法分析器中执行此操作。你没有说你的词法分析器使用什么,但你提到 yytext,它被 lex/flex 使用,所以我会假设。

当你使用flex识别令牌时,变量yytext指的是flex用来识别令牌的内部缓冲区。在令牌的操作中,它可以用于获取令牌的文本,但只是暂时的——一旦操作完成并读取下一个令牌,它将不再有效。

因此,如果您有如下弹性规则:

[a-zA-Z_][a-zA-Z_0-9]*    { yylval.str = yytext, return T_ID; }

这可能根本行不通,因为您的程序中会有悬空指针 运行;可能是您看到的随机输出的来源。相反,您需要制作一份副本。如果你还想输出不变的输入,你也可以在这里做:

[a-zA-Z_][a-zA-Z_0-9]*    { yylval.str = strdup(yytext); ECHO; return T_ID; }

这使用了 flex 宏 ECHO,它大致等同于 fputs(yytext, yyout) -- 将输入复制到名为 yyoutFILE *(默认为 stdout)

如果对应右手边的第一个符号是终端,bison动作中的</code>表示“扫描器在返回对应的令牌时产生的<code>yylval的值该终端。如果该符号是非终端,则它指的是在评估减少该非终端的动作期间分配给 $$ 的值。如果没有这样的动作,则默认 $$ = 将被执行,因此它将通过第一个符号的语义值来减少该非终结符。

如果所有这些都很明显,我深表歉意,但您的代码片段不足以显示:

  • 每个非终结符的语义类型是什么;

  • 每个终端的语义类型是什么;

  • 在扫描程序操作中为 yylval 分配了哪些值(如果有);

  • 在 bison 动作中为 $$ 分配了哪些值(如果有)。

如果这些语义类型中的任何一个实际上不是字符串,那么 printf 显然会产生垃圾。 (如果你用 -Wall 编译生成的代码,gcc 可能会警告你。尽管如果你使用旧版本的 flex/bison 可能会发出虚假警告,我认为它是 总是值得用-Wall编译并仔细阅读产生的警告。)

在 bison 操作中使用 yytext 是有问题的,因为它将引用扫描的 last 令牌的文本,通常是前瞻令牌。特别是,在输入结束时,yytext 将是 NULL,这就是您将在输入结束时发生的任何归约中得到的结果。 glibc 的 printf 实现足以打印 (null) 而不是当您将 (char*)0 提供给格式为 %s 的参数时出现段错误,但我认为这不是一个好主意就靠那个了。

最后,如果您确实有一个 char* 语义值,并且您分配了 yylval = yytext(或者 yylval.sval = yytext;,如果您使用的是联合),那么您将 运行进入另一个问题,即 yytext 指向扫描器拥有的临时缓冲区,并且当您开始使用该地址时,该缓冲区可能具有完全不同的内容。因此,如果要将 yytext 传递给解析器,则始终需要复制它。

如果您真正 想做的是查看解析器在做什么,我建议您启用 bison 的 yydebug parser-trace feature。它会给你很多有用的信息,根本不需要你将 printf 插入到你的 bison 操作中。