1

我已经实现了一个解析器,但它不打印任何东西。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();
}
4

2 回答 2

1

该函数yylex()是词法扫描器,而不是语法分析器;解析器是yyparse(). 因此,将您的程序更新为 callyyparse()而不是yylex(),在需要新令牌时yyparse()继续调用:yylex()

while (yyparse() != 0)
    ;

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

在旁注中,我想不出使用main(). 始终使用int main(int argc, char **argv). 如果您确实使用 K&R 表示法,那么您必须从 中返回一个值main(),通常成功时为零,失败时非零。尽管 C99 允许您省略最终的 return from main()(这相当于return 0;的唯一非常特殊的情况main()),但我建议包括它。


后来的笔记

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

中和语法中的各种动作功能并不难。该parser.h文件需要包含类似typedef struct tree tree;. 并且语法必须是idf.y,这样bison -d idf.y才会生成idf.tab.hand 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和设置——你可能需要在词法分析器源文件中添加,因为那里有。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):
$

现在您的问题出在您未显示的功能中。这些都是你要解决的。

于 2012-04-03T15:59:09.757 回答
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语义类型。但是您产生的 lex 规则TOKID并没有将任何内容放入a_variable. 它只是这样做return TOKID;,让该指针包含垃圾。您的词法分析器规则必须分配给yylval.a_variable.

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

于 2012-04-03T22:28:52.127 回答