如何在Yacc/Bison中看到我所看到的内容?
我有一堆的规则复杂Yacc的文件,他们中的一些复杂的,例如:如何在Yacc/Bison中看到我所看到的内容?
start: program
program: extern_list class
class: T_CLASS T_ID T_LCB field_dec_list method_dec_list T_RCB
确切的规则,我采取相应的行动并不重要,因为我想做的事情,似乎相当简单:只需使用我为其他目的定义的规则打印出现在源文件中的程序即可。但我很惊讶这样做有多困难。
首先,我尝试在上面的第二条规则中添加printf("%s%s", $1, $2)
。这产生了“ @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。
如果您只是想在解析它时回显某些输出的源代码,那么在词法分析器中最容易做到这一点。你没有说明你用于词法分析器的东西,但是你提到了由lex/flex使用的yytext
,所以我会假设这一点。
当您使用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
)
如果在相应的右手侧的第一个符号是在一个野牛动作的终端,$1
意思是“yylval
通过时,它返回对应于该终端的令牌中的扫描仪产生的值。如果符号是非终端,则它指的是分配给的值期间减少非终端的行动评估。如果没有这样的动作,那么缺省$$ = $1
将被执行,所以它将通过减少非终端中的第一个符号的语义值。
我道歉,如果一切是显而易见的,但你的片段是不足以表明:
什么语义类型对于每一个非终端;
每个终端的语义类型是什么;
在扫描仪操作中将什么值(如果有)分配给
yylval
;什么样的值,如果有的话,被分配到
$$
在野牛行动。
如果其中任何语义类型的都没有,事实上,字符串,那么printf
显然会产生垃圾。 (如果你用-Wall
编译生成的代码,gcc可能会提醒你,尽管如果你使用旧版本的flex/bison,可能会出现虚假警告,但我认为这是总是值得编译与-Wall
并仔细读取所产生的警告。)
在野牛动作使用yytext
是有问题的,因为这将涉及最后令牌扫描,通常先行标记的文字。特别是,在输入结束时,yytext
将是NULL
,这就是您将在输入结束时发生的任何减少中获得的结果。 glibc的printf
的实现非常好,可以打印(null)
,而不是在您将(char*)0
提供为格式为%s
的参数时进行分段转换,但我认为依靠它不是一个好主意。
最后,如果你有一个char*
语义价值,而分配yylval = yytext
(或yylval.sval = yytext;
如果您使用的工会),那么你会遇到另一个问题,这是yytext
点到临时缓冲由扫描仪拥有,并且该缓冲区在您使用该地址时可能会有完全不同的内容。所以如果你想将它传递给解析器,你总是需要复制yytext
。
如果你真的想要做的是看看解析器在做什么,我建议你启用野牛的yydebug
parser-trace feature。它会给你很多有用的信息,而不需要你插入printf到你的野牛动作中。