【问题标题】:How to print a parse tree using Bison?如何使用 Bison 打印解析树?
【发布时间】:2012-04-03 06:58:10
【问题描述】:

我已经实现了一个解析器,但它不打印任何东西。如果给定的输入在语法上是错误的,它不会打印“错误”,尽管我将它包含在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();
}

【问题讨论】:

  • 请显示(至少)您的main () 定义。否则没有办法回答你的问题。
  • @TimLandscheidt 我现在已经包含了我的main() 定义。
  • main 中的基本方法是可以的,只是缺少条件检查。如果论证无法打开怎么办?您将yyin 设置为空指针并调用yylex。
  • 你的main调用yylex,也就是lex生成的scanner函数。 yylex 只读取一个令牌。您必须致电yyparse。 !!!
  • 这是否缺少 [homework] 标签?

标签: bison yacc flex-lexer parse-tree


【解决方案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[&lt;whatever&gt;].a_variable,因为您将TOKID 定义为具有a_variable 语义类型。但是您产生TOKID 的lex 规则并没有将任何内容放入a_variable。它只是做return TOKID;,让那个指针包含垃圾。您的词法分析器规则必须分配给yylval.a_variable。

Lex 和 Yacc 远没有你想象的那么自动化。

【讨论】:

  • 我按照您的建议进行了更改。如果现在还好,请告诉我。
  • 顺便问一下,你为什么要将数字标记为TOKID?你的语法文件中有一个TOKFLOAT;你不应该将它用于{DIGIT}+[.]{DIGIT}+吗?
  • 我不想让语法看起来很复杂。这就是我将数字标记为TOKID 的原因。我可以删除TOKFLOAT。
【解决方案2】:

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

while (yyparse() != 0)
    ;

您可以打印解析树来代替空循环体,或者您可以在从语法本身的开始规则调用的函数中打印。

顺便说一句,我想不出使用main() 的K&R 声明的充分理由。始终使用int main(int argc, char **argv)。如果您确实使用 K&R 表示法,那么您必须从 main() 返回一个值,通常成功时为零,失败时非零。尽管 C99 允许您省略来自 main() 的最终返回(在 main() 的唯一非常特殊的情况下相当于 return 0;),但我建议包含它。


后记

让人们轻松测试您寻求帮助的内容是个好主意。提供足够的源代码使其可编译。删除足够的源代码以最大程度地减少编译工作。

中和语法中的各种动作函数并不难。 parser.h 文件需要包含类似typedef struct tree tree; 的内容。并且语法必须是idf.y,这样bison -d idf.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;}

这很快就告诉我你不能非常优雅地处理空格或换行符。您可能需要规则才能这样做(并且这些规则可能不会返回到语法)。

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

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

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

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

$

我在上面输入了abc ;,它识别出那些都OK。由于语法在动作中没有留下代码,因此语法本身没有输出。使用-DYYDEBUG 编译并在main() 函数中设置yydebug = 1; 可能值得——您可能需要在词法分析器源文件中添加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):
$

现在你的问题出在你没有展示的功能上。这些都是你要解决的。

【讨论】:

  • 我已将其更新为yyparse(),但仍然没有显示任何输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
  • 2011-05-15
相关资源
最近更新 更多