我正在创建一个bison / flex编译器,我遇到了问题。我添加了%glr-parser
,但问题仍然存在。我有:
有一个代表我的问题的简单例子
.y档案:
%{
#include <stdio.h>
#include <stdlib.h>
extern FILE *yyin;
extern int yylex();
int line=1;
int error=0;
#define YYERROR_VERBOSE
void yyerror(const char *msg)
{
error = 1;
printf("ERROR in line %d : %s.\n", line, msg);
}
%}
%start programme
%token SP
%token CRLF
%token LETTER
%%
programme : id CRLF;
id : LETTER;
%%
int main(int argc, char *argv[])
{
if(argc == 2) yyin = fopen(argv[1], "r");
else if(argc < 2){
printf("No file found.\n");
return 0;
} else printf("Only one file is permitted.\n");
yyparse();
if(error == 0) printf("Finished at %d line.\nNo errors!\n",line);
return 0;
}
.l文件
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "myParser.h"
extern int line;
%}
%%
"\n" {line++; return CRLF;}
" " {return SP;}
[a-zA-Z] {return LETTER;}
%%
.h文件
enum yytokentype {
SP = 259,
LETTER = 260,
CRLF = 261
}
我的程序获取.txt文件:
file_correct.txt包含:A
在我的终端上,我写道:
bison -d bison.y
flex myParser.l
gcc bison.tab.c lex.yy.c -lfl -o a
./a file_correct.txt
- &GT;第1行中的错误:语法错误,意外$ undefined,期待LETTER。
输入A\n
应该是正确的。相反,我有这个消息..你能帮帮我吗?
答案 0 :(得分:2)
您必须使用bison生成的头文件。您可以#include
将它放入您自己的头文件中(虽然这没有什么实际价值),但您不能尝试自己编写它并希望它始终是正确的。
在这种情况下,myparser.h
包含
enum yytokentype {
SP = 259,
LETTER = 260,
CRLF = 261
};
这些是yylex
将返回的tokentype数字,因为在您的flex文件中,您#include <myParser.h>
。
但是,由bison生成的文件bison.tab.h
(并在bison.tab.c
中以文字方式包含)具有不同的值:
enum yytokentype
{
SP = 258,
CRLF = 259,
LETTER = 260
};
碰巧,LETTER
在两个文件中都有相同的代码,但其他两个代码有所不同。特别是,当扫描程序看到换行符时,它将返回261,但解析器将需要一个类型号为259的令牌。当它收到261时,它会抱怨:
ERROR in line 2 : syntax error, unexpected $undefined, expecting CRLF.
(对于野牛,代码261与任何内容都不对应,因此它将其报告为$undefined
。)
这是与您在问题中报告的错误消息不同的错误消息,这可能是由于不同的bison版本在不同的订单中对令牌进行编号,或者它可能只是一个复制粘贴问题。
底线是你应该总是把
#include "bison.tab.h"
到您的.l
文件中(根据您的项目更改名称,但始终使用bison生成的文件),而不是(或除了)您自己的头文件。 (如果你也插入自己的头文件,它当然不应该尝试定义标记值。包含你自己的头文件的原因是为你自己的外部函数声明原型,这些函数正被扫描器动作使用。)
总的来说,很少有空格(空格和换行符)应该传递给解析器的情况。 (异常将是一种语言,其中语句肯定由换行符终止,而不是由冒号终止。即使这样,您也不希望将空格传递给解析器。)处理解析器中的空格会产生更多工作比必要的;混淆语法;并且可能导致不必要的转移 - 减少冲突。
考虑简化的语法生成:
decl: type SP id SEMICOLON
这将符合预期的int a;
。但它不符合以下任何一项:
int a;
int a ;
int a;
以上所有内容都可能出现在有效的程序中,因此用户可能会认为有关空白的挑剔是一个问题。 (并且使语法更加灵活将会非常痛苦。)
此外,您可能会考虑将其置于更广泛的背景下:
program: %empty
| program decl CRLF
但是现在你的解析器会拒绝空行,进一步让你的用户烦恼。它也会拒绝
int a; int b;
这可能会让一些人想知道为什么甚至需要分号。
注意以下错误,改编自编辑历史:
prog: stmt
stmt: decl more
more: %empty | stmt CRLF more
这可能永远不会成功解析程序,因为所有程序文本都以换行符结束,但语法只允许在语句之间使用换行符。因此,文件末尾的换行符会导致语法错误,因为解析器拼命试图找到另一个语句。
(上面的片段大概是在误解的情况下编写的,消除左递归是一个好主意。至少如果你使用的是LR解析器,比如野牛.Bison喜欢左递归并找到正确的递归充其量是单调乏味的。当递归写入时,许多语法也更具可读性。)