如何使用Bison打印解析树?

如何使用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

我实现了一个解析器,但它不打印任何内容。如果给定的输入在语法上是错误的,它不会打印“Error”,尽管我将其包含在
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-yydebugmain()
,因此在词法分析器源文件中存在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