如何使用Bison打印解析树?
我实现了一个解析器,但它不打印任何内容。如果给定的输入在语法上是错误的,它不会打印“Error”,尽管我将其包含在如何使用Bison打印解析树?,bison,yacc,flex-lexer,parse-tree,Bison,Yacc,Flex Lexer,Parse Tree,我实现了一个解析器,但它不打印任何内容。如果给定的输入在语法上是错误的,它不会打印“Error”,尽管我将其包含在yyerror()例程中。此外,如果输入正确,则不会打印解析树。可能的原因是什么?我已将我的main()放在.lex文件中,而不是.y文件中。这可能是原因吗? 这是主要的方法: int main( argc, argv ) int argc; char **argv; { ++argv, --argc; if ( argc > 0 ) yyin = f
yyerror()
例程中。此外,如果输入正确,则不会打印解析树。可能的原因是什么?我已将我的main()
放在.lex
文件中,而不是.y
文件中。这可能是原因吗?
这是主要的方法:
int main( argc, argv )
int argc;
char **argv;
{
++argv, --argc;
if ( argc > 0 )
yyin = fopen( argv[0], "r" );
else
yyin = stdin;
yyparse();
}
语法文件是:
%{
#include "parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
%}
%union {
char* a_variable;
tree* a_tree;
}
%start file
%token <a_variable> TOKID TOKSEMICOLON TOLCOLON TOKCOMMA TOKUNRECOG TOKDOT TOKMINUS TOKCOLON
%type <a_tree> field file obj ID
%right TOKMINUS
%%
file : /*empty*/ { return NULL; }
| field file { printtree($1, 1); }
;
field : ID TOKCOLON field {$$ = make_op($1, ':', $3); }
| ID TOKCOMMA field {$$ = make_op($1, ',', $3); }
| obj { $$ = $1; }
;
obj : ID TOKSEMICOLON { $$ = make_op($1, ';', NULL); }
;
ID : TOKID { $$ = $1; }
%%
#include <stdio.h>
yyerror(char *str)
{
fprintf(stderr,"error FAIL: %s\n",str);
}
int yywrap()
{
return 1;
}
函数yylex()
是词法扫描器,而不是语法分析器;解析器是yyparse()
。因此,请将程序更新为调用yyparse()
而不是yylex()
,让yyparse()
在需要新令牌时调用yylex()
:
while (yyparse() != 0)
;
您可以打印解析树来代替空循环体,也可以在从语法本身的开始规则调用的函数中进行打印
另一方面,我想不出使用main()
的K&R声明的好理由。始终使用intmain(intargc,char**argv)
。如果使用K&R表示法,则必须从main()
返回一个值,通常成功时为零,失败时为非零。虽然C99允许您省略main()
(这相当于return 0;
在main()
的唯一非常例外的情况下)的最终返回,但我建议将其包括在内
后记 这是一个好主意,可以让人们更容易地测试您请求帮助的内容。提供足够的源代码使其可编译。删除足够的源代码以最小化编译工作 要中和语法中的各种动作功能并不难。
parser.h
文件需要包含类似typedef结构树的内容代码>。语法需要是idf.y
,这样bison-dif.y
将生成idf.tab.h
和idf.tab.c
我使用词法分析器要做的第一件事就是确保它打印出它正在做的事情。所以,我修改了规则,做了如下事情:
{ID} |
-?{DIGIT}+"."{DIGIT}* |
-?{DIGIT}+ { printf("ID or number: %s\n", yytext); /*yylval.a_variable = findname(yytext);*/ return TOKID; }
";" { printf("Semi-colon\n"); return TOKSEMICOLON;}
":" { printf("Colon\n"); return TOKCOLON;}
这很快就告诉我,你不能很优雅地处理空格或换行符。这样做可能需要规则(而这些规则可能不会回到语法)
当然,这需要出现在“吞噬点”规则之前
这样,我就可以运行程序并获得词法输出:
$ ./idf
abc ;
ID or number: abc
White space
Semi-colon
$
我打了abc代码>在它和它确定了那些OK。由于语法在操作中没有代码,因此语法本身没有输出。它可能值得使用-DYYDEBUG
编译,并设置yydebug=1代码>在main()
函数中-您可能需要添加extern int-yydebug由于存在main()
,因此在词法分析器源文件中存在code>
$ flex scanner.l
$ bison -d idf.y
$ gcc -DYYDEBUG -o idf idf.tab.c lex.yy.c
$ ./idf
Starting parse
Entering state 0
Reading a token: abc ;
ID or number: abc
Next token is token TOKID ()
Shifting token TOKID ()
Entering state 1
Reducing stack by rule 7 (line 32):
$1 = token TOKID ()
-> $$ = nterm ID ()
Stack now 0
Entering state 5
Reading a token: White space
Semi-colon
Next token is token TOKSEMICOLON ()
Shifting token TOKSEMICOLON ()
Entering state 8
Reducing stack by rule 6 (line 29):
$1 = nterm ID ()
$2 = token TOKSEMICOLON ()
-> $$ = nterm obj ()
Stack now 0
Entering state 4
Reducing stack by rule 5 (line 26):
$1 = nterm obj ()
-> $$ = nterm field ()
Stack now 0
Entering state 3
Reading a token:
Now at end of input.
Reducing stack by rule 1 (line 20):
$
现在,您的问题在于尚未显示的函数。这些都是你要解决的。我不太确定你是否可以将多个规则组合成一个这样的规则:
{ID} | -?{DIGIT}+"."{DIGIT}* | -?{DIGIT}+ return TOKID;
{ID} |
-?{DIGIT}+"."{DIGIT}* |
-?{DIGIT}+ return TOKID;
lex对空格敏感;我认为应该是这样的:
{ID} | -?{DIGIT}+"."{DIGIT}* | -?{DIGIT}+ return TOKID;
{ID} |
-?{DIGIT}+"."{DIGIT}* |
-?{DIGIT}+ return TOKID;
|
字符被解释为一种特殊的动作,意思是“与下一行的动作相同”。在模式中,|
表示正则表达式分支。但是你有所有这些空间
您的评论匹配器看起来是假的:
"{"[\^{}}\n]*"}" /* eat up one-line comments */
我认为您想要一个否定的字符类,但是您在^
字符上加了一个转义符,这只会导致^
包含在字符类中
这有什么意义
"!"+"-"[\n] return TOKCOMMENT;
一系列的代码>后跟-和换行符是一种注释,您不会忽略它,而是作为标记返回,什么
此解析规则无法正常工作,因为lexer中的行为已损坏:
ID : TOKID { $$ = $1; }
表达式$1
希望访问yystack[]。a_变量
,因为您将TOKID
定义为具有a_变量
语义类型。但是生成TOKID
的lex规则不会将任何内容放入a_变量中。它只返回TOKID代码>,将该指针保留为包含垃圾。您的lexer规则必须分配给yylval.a_变量
Lex和Yacc的自动化程度远不如你想象的那么高。请(至少)显示你的main()
定义。否则无法回答您的问题。@TimLandscheidt我现在已经包含了我的main()
定义。您的main
中的基本方法是可以的,只是缺少条件检查。如果参数无法打开怎么办?您将yyin
设置为空指针并调用yylex
。您的main
调用yylex
,这是由lex
生成的扫描函数yylex
只读取一个令牌。您必须调用yyparse
!!!是否缺少[Homebook]标记?我已将其更新为yyparse()
,但仍然没有显示任何输出。我已按照您的建议进行了更改。请告诉我现在是否可以。顺便问一下,你为什么要将数字标记到TOKID
?语法文件中有一个TOKFLOAT
;你不应该把它用于{DIGIT}+[.]{DIGIT}+
?我不想让语法看起来很复杂。这就是我将数字标记为TOKID
的原因。我可以删除TOKFLOAT
。