如何使用Bison打印解析树?

时间:2012-04-03 06:58:11

标签: bison yacc flex-lexer parse-tree

我已经实现了一个解析器,但它没有打印任何东西。如果给定的输入在语法上是错误的,虽然我将它包含在yyerror()例程中,但它不会打印“错误”。此外,如果输入正确,则不会打印Parse树。可能的原因是什么?我已将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;
}

这是.lex文件的外观:

%{
/* need this for the call to atof() below */
#include <math.h>
#include "parser.h"
#include "idf.tab.h"
%}

DIGIT    [0-9]
ID       [a-zA-Z]*
%option noyywrap

%% 


{ID} |  
-?{DIGIT}+"."{DIGIT}* |    
-?{DIGIT}+      { yylval.a_variable = findname(yytext); return TOKID; }

";"           return TOKSEMICOLON;
":"           return TOKCOLON;
","           return TOKCOMMA;
"."           return TOKDOT;
"-"           return TOKMINUS; 
.           return TOKUNRECOG;
%%

int main( int argc, char** argv )
{
++argv, --argc; 
if ( argc > 0 )
yyin = fopen( argv[0], "r" );
else
yyin = stdin;

yyparse();
}

2 个答案:

答案 0 :(得分:1)

函数yylex()是词法扫描程序,而不是语法分析程序;解析器是yyparse()。因此,更新您的计划以致电yyparse()而不是yylex(),让yyparse()在需要新令牌时致电yylex()

while (yyparse() != 0)
    ;

您可能会打印解析树而不是空循环体,或者您可以在语法本身的起始规则调用的函数中进行打印。

另一方面,我无法想出使用main()的K&amp; R声明的充分理由。始终使用int main(int argc, char **argv)。如果你使用K&amp; R表示法,那么你必须从main()返回一个值,通常在成功时为零,在失败时为非零。虽然C99允许您省略main()的最终报酬(在return 0;的唯一例外情况下相当于main()),但我建议您将其包括在内。


后来的注释

让人们轻松测试您所寻求帮助的内容是个好主意。提供足够的资源以使其可编辑。删除足够的源以最小化编译工作。

中和语法中的各种动作功能并不是很难。 parser.h文件需要包含typedef struct tree tree;之类的内容。语法必须为idf.y,以便bison -d idf.y生成idf.tab.hidf.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;}

这很快就向我展示了你没有非常优雅地处理空格或换行。你可能需要这样做的规则(这些规则可能不会返回语法)。

[ \t]         { printf("White space\n"); }

当然,这需要出现在'gobbling dot'规则之前。

有了这个,我就可以运行程序并获得词汇输出:

$ ./idf
abc ;
ID or number: abc
White space
Semi-colon

$

我输入了abc ;,它确定了那些正常。由于语法在动作中没有代码,因此语法本身没有输出。可能值得使用-DYYDEBUG进行编译并在yydebug = 1;函数中设置main() - 您可能需要在词法分析器源文件中添加extern int yydebug;,因为main()在那里。

$ 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):
$

现在您的问题出现在您未显示的功能中。那些是你的解决方案。

答案 1 :(得分:1)

我不太确定你可以将多个规则组合成这样的规则:

{ID} |  -?{DIGIT}+"."{DIGIT}* |    -?{DIGIT}+      return TOKID;
lex是空白敏感的;我认为它应该是这样的:

{ID} |
-?{DIGIT}+"."{DIGIT}* |
-?{DIGIT}+      return TOKID;

|字符被解释为一种特殊的操作,表示“与下一行的操作相同”。在模式中|表示正则表达式分支。但是那里有你所有这些空间。

您的评论匹配器看起来很虚伪:

"{"[\^{}}\n]*"}"     /* eat up one-line comments */   

我认为你想在这里使用一个否定的字符类,但你在^字符上放了一个转义符,它只会导致^包含在字符类中。

有什么意义:

"!"+"-"[\n] return TOKCOMMENT;

一系列!后跟 - 和换行是某种你不会忽略的评论,但是作为代币返回,是吗?

由于词法分析器中的行为损坏,此解析规则无法正常工作:

ID        :     TOKID { $$ = $1; }

表达式$1想要访问yystack[<whatever>].a_variable,因为您将TOKID定义为具有a_variable语义类型。但是,生成TOKID的lex规则不会将任何内容放入a_variable。它只是return TOKID;,让指针包含垃圾。您的词法分析器规则必须分配给yylval.a_variable

Lex和Yacc的自动性远不如你实现的那么自动化。