我正在尝试使用flex和bison编写解析器。但是,无论我如何修改文件,总是会出现错误“第1行中的语法错误”。 这是yyinput的test.vm文件:
$asfdfsdf
sdfsdfs
sdfsdfsd
sdfsdfsd
sfsdfd
这是vtl4.l文件:
%{
#include<stdio.h>
#include<string.h>
#include "context.h"
#include "bool.h"
#include "vtl4.tab.h"
%}
%%
(.|\n)* {yylval.string = yytext;return CONTENT;}
<<EOF>> {return FINAL;}
%%
这是vtl4.y文件:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bool.h"
#include "parser.h"
#include "context.h"
#include "vtl4.tab.h"
extern FILE * yyin;
extern FILE * yyout;
extern int yylex();
extern int yywrap();
%}
%union {
struct simpleNode *ast;
double d;
int i;
bool b;
char* string;
struct symbol *sym;
}
%type <ast> root stmts stmt
%token <string> CONTENT
%token FINAL
%%
root:stmts FINAL {printf("root\n");$$ = process($1);traverse($$);}
;
stmts: {printf("stmts:stmt\n");$$ = 0;}
|stmts stmt {printf("stmts:stmts stmt\n");$$ = add_ybrother($1,$2);}
;
stmt:CONTENT {printf("stmt\n");$$ = text($1);}
;
%%
int main(){
FILE *src;
src = fopen("test.vm","r");
yyin = src;
yyparse();
fclose(src);
return 1;
}
int yywrap(){
return 1;
}
生成文件:
CC=cc
FLEX=vtl4.l
BISON=vtl4.y
parse:vtl4.tab.c lex.yy.c
$(CC) -o out *.c -ll
vtl4.tab.c:$(BISON)
bison -d $(BISON) --report=all
lex.yy.c:$(FLEX)
flex $(FLEX)
当我运行./out时,它会打印出正确的结果,但最后总是说“line:1:error:syntax error”!我不知道为什么?
编辑lex规则
时效果很好<<EOF>> {return FINAL;}
到
<<EOF>> {yyterminate();}
并修改yacc规则
root:stmts FINAL {printf("root\n");$$ = process($1);traverse($$);}
到
root:stmts {printf("root\n");$$ = process($1);traverse($$);}
但我不知道为什么?
答案 0 :(得分:2)
通过使用return FINAL
规则中的<<EOF>>
,标记生成器将继续在文件结尾返回FINAL
。当flex
与bison
结合使用时,您不必(也不应该)使用显式的文件结束标记。只需依靠yylex
在文件结尾处返回的0,前提是yywrap
返回1.这正是yyterminate
为你所做的,这就是为什么这样做的原因细
在这种情况下,语法面临着无法处理的FINAL
令牌的源源不断。当然,你不应该在你的语法中容纳这种无休止的流,因为语法将是“正确的”但永远不会终止。
我假设您知道您的tokenizer将匹配单个CONTENT
令牌中的完整文件,因此即使您的语法支持CONTENT
令牌列表,它也始终只能看到一个。
PS:我通过使用-t
bison
选项找到了问题,它将调试跟踪添加到解析器中,并显示它在第二次出现FINAL
时被阻塞。 / p>
P.S2:Makefile
在*.c
的编译器调用中使用parse
。这非常危险,因为一些随机.c
文件可能会在您的目录中挂起。最好使用$^
来引用规则所依赖的所有文件。
P.S3:由于您已经定义了自己的yywrap
和main
,因此可能会失去-ll
。