野牛意想不到的令牌

时间:2017-05-27 22:33:36

标签: c bison flex-lexer

我正在创建一个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应该是正确的。相反,我有这个消息..你能帮帮我吗?

1 个答案:

答案 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喜欢左递归并找到正确的递归充其量是单调乏味的。当递归写入时,许多语法也更具可读性。)