我正在创建一个 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
-> 第 1 行中的错误:语法错误,意外的 $undefined,期待 LETTER。
输入A\n
应该是正确的。相反,我收到了这条消息。你能帮帮我吗?
最佳答案
必须使用bison生成的头文件。你可以 #include
将它放入您自己的头文件中(尽管这没有什么实用值(value)),但您不能尝试自己编写它并希望它始终是正确的。
在这种情况下,myparser.h
包含
enum yytokentype {
SP = 259,
LETTER = 260,
CRLF = 261
};
那些是 yylex
的 tokentype 数字将返回,因为在您的 flex 文件中,您 #include <myParser.h>
.
但是,文件 bison.tab.h
,由 bison 生成(并以文本方式包含在 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.
(对于 Bison ,代码 261 不对应任何东西,因此它报告为 $undefined
。)
这与您在问题中报告的错误消息不同,这可能是不同 bison 版本以不同顺序对标记进行编号的结果,或者可能只是复制粘贴问题。
底线是你应该始终把
#include "bison.tab.h"
进入你的.l
文件(根据您的项目更改名称,但始终使用 bison 生成的文件),而不是(或除此之外)您自己的头文件。 (如果您还插入了自己的头文件,当然,它不应该尝试定义标记值。包含您自己的头文件的原因是为您自己的外部函数声明原型(prototype),这些函数正被扫描器操作使用。)
总的来说,应该将空格(空格和换行符)传递给解析器的情况很少。 (异常(exception)情况是语句肯定由换行符而不是分号终止的语言,例如。即便如此,您也不想将空格传递给解析器。)在解析器中处理空格会产生更多超出必要的工作;混淆语法;并可能导致不必要的 shift-reduce 冲突。
考虑简化的语法产生式:
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
这可能永远不会成功解析程序,因为所有程序文本都以换行符结尾,但语法只允许换行符 between 语句。因此文件末尾的换行符会导致语法错误,因为解析器拼命地试图找到另一个语句。
(上面的代码片段可能最初是在误解消除左递归是一个好主意的情况下编写的。事实并非如此,至少如果您使用的是 bison 等 LR 解析器。Bison 喜欢左递归并发现右递归充其量是乏味的。许多语法在以递归方式编写时也更具可读性。)
关于c - Bison 意外 token ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44222194/