我已经实现了一个解析器,但它没有打印任何东西。如果给定的输入在语法上是错误的,虽然我将它包含在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();
}
答案 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.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 ;
,它确定了那些正常。由于语法在动作中没有代码,因此语法本身没有输出。可能值得使用-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的自动性远不如你实现的那么自动化。