Flex和Bison:使用特定关键字开始一个句子

我正在使用Flex和Bison开发一个程序。 我的任务可以仅使用Flex(使用开始条件等)完成,但我已经明白使用Bison可能会让我的生活更轻松。 我的任务是设计一个识别编程语言声明部分的程序。 通过下面的代码可以理解它的语法和逻辑。 我的问题是,我希望我的程序能够将代码的每个部分识别为可接受的声明部分,该部分仅以var ”关键字开头! 到现在为止,我还没有成功。 我怎么能成功呢?

我在我的.l(flex)和.y(野牛)文件下面。

exercise4.l

%{ #include  %} %% [ \t\n]+ { /* Ignore all whitespaces */ } ";" { /* Ignore all semicolons */ } ":" { /* Ignore all colons */ } var { printf("A keyword: %s\n",yytext); return VAR; } real { printf("A variable type: %s\n",yytext); return REAL; } boolean { printf("A variable type: %s\n",yytext); return BOOLEAN; } integer { printf("A variable type: %s\n",yytext); return INTEGER; } char { printf("A variable type: %s\n",yytext); return CHAR; } [a-zA-Z][a-zA-Z0-9_]* { printf("A variable name: %s\n",yytext); return VAR_NAME; } . { printf("Unrecognized character!\n"); return yytext[0]; } %% 

exercise4.y

 %{ #include  %} %token VAR VAR_NAME REAL BOOLEAN INTEGER CHAR %% program : VAR typedecls ; typedecls : typedecl | typedecls typedecl ; typedecl : varlist ':' var_type ';' ; varlist : VAR_NAME | varlist ',' VAR_NAME ; var_type : REAL | BOOLEAN | INTEGER | CHAR ; %% main( argc, argv ) int argc; char **argv; { extern FILE *yyin; ++argv, --argc; /* skip over program name */ if ( argc > 0 ) yyin = fopen( argv[0], "r" ); else yyin = stdin; //yylex(); yyparse(); } yyerror(char *s) { printf("\nError\n"); } #include "lex.yy.c" 

您的扫描仪永远不会返回:; 令牌(和抱怨,未被承认)。 但是你的语法包括那些令牌。

所以,

 typedecl : varlist ':' var_type ';' ; 

永远无法匹敌。 它没有看到:它正在期待,因此var_type是意外的。 (它永远不会匹配;如果它那么远。)


我不知道你所遵循的是什么古老的教程,但主要的pre-ansi原型表明它是在上个世纪(而不是在最后几年)。 作为一个粗略的指南,我稍微更新了你的文件。

这是exercise4.l; 最重要的修改是我删除了手工构建的跟踪日志,因为我打算使用flex的内置调试function。 我还添加了一些选项来减少编译器警告,并启用行号跟踪以用于错误消息。 我离开了虫子。

 /* File: exercise4.l */ %{ /* The bison-generated header file includes token declarations */ #include "exercise4.tab.h" %} %option noinput nounput noyywrap nodefault %option yylineno %% [ \t\n]+ { /* Ignore all whitespaces */ } ";" { /* Ignore all semicolons. BUG */ } ":" { /* Ignore all colons. BUG */ } var { return VAR; } real { return REAL; } boolean { return BOOLEAN; } integer { return INTEGER; } char { return CHAR; } [a-zA-Z][a-zA-Z0-9_]* { return VAR_NAME; } . { return yytext[0]; } 

这里是解析器,有一些修复(比如使用1989 C标准中的标准C原型语法):

 /* File: exercise4.y */ %{ /* Used in this file */ #include  /* Forward and external declarations */ extern int yylineno; int yylex(); void yyerror(const char* msg); %} /* These two options make error messages more informative */ %define parse.lac full %error-verbose %token VAR VAR_NAME REAL BOOLEAN INTEGER CHAR %% program : VAR typedecls typedecls : typedecl | typedecls typedecl typedecl : varlist ':' var_type ';' varlist : VAR_NAME | varlist ',' VAR_NAME ; var_type : REAL | BOOLEAN | INTEGER | CHAR ; %% /* Welcome to 1990 */ int main( int argc, char** argv ) { extern FILE *yyin; if ( argc > 1 ) yyin = fopen( argv[1], "r" ); else yyin = stdin; if (!yyin) { /* If you don't check, you'll end up segfaulting when * yylex tries to read from NULL. Checking lets us print * a hopefully meaningful error message. */ perror("Could not open file for reading"); return 1; } return yyparse(); } /* Now that error messages have some content, it's worthwhile * actually using the argument passed to yyerror */ void yyerror(const char* msg) { fprintf(stderr, "At line %d: %s\n", yylineno, msg); } 

现在,我按顺序通过flex,bison和gcc运行文件,以生成可执行文件:

 # The `-d` option to flex causes it to insert debugging traces. That's # a lot less effort and a lot more useful than rolling your own trace # logs. flex -d -o exercise4.scan.c exercise4.l # The `-d` option to bison causes it to output a header file, whose name # is the same as the output file name with `.c` changed to `.h`. That's # the file which we need to `#include` in the scanner, so that token # names are available to scanner actions. Bison also has a debugging # feature, but you need to use `-t` to enable it, plus add a line at # runtime. For now, we'll leave it out. bison -d -o exercise4.tab.c exercise4.y # Now compile and link an executable: gcc -Wall -g -o exercise4 exercise4.tab.c exercise4.scan.c 

通常,三个shell命令将进入Makefile,因此您只需键入make exercise4即可创建可执行文件。 但那里没有什么真正复杂的。

现在,我们可以尝试一下。 由于我没有修复错误,你很容易看到问题所在:

 var a, b: integer; --accepting rule at line 14 ("var") --accepting rule at line 11 (" ") --accepting rule at line 19 ("a") --accepting rule at line 20 (",") --accepting rule at line 11 (" ") --accepting rule at line 19 ("b") --accepting rule at line 13 (":") --accepting rule at line 11 (" ") --accepting rule at line 17 ("integer") At line 1: syntax error, unexpected INTEGER, expecting ':' or ',' 

线条开始--是flex的痕迹。 您可以在识别时看到每个令牌。 因此,当在输入中遇到:时,执行第13行的规则。 该规则是{ /* Ignore all colons */ } 。 这就是发生的事情。

最后的错误消息由yyerror打印。 感谢%errors-verbose指令,它告诉我们它找到了什么( INTEGER )以及它所期待的内容。

希望一切都有所帮助。