如何打印我在 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)
-- 将输入复制到名为 yyout
的 FILE *
(默认为 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 操作中。
我有一个复杂的 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)
-- 将输入复制到名为 yyout
的 FILE *
(默认为 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 操作中。