从stdin到文件的Flex / Bison EOF传播

时间:2013-12-08 20:12:46

标签: c bison lex flex-lexer

我有一个扫描程序,解析器和一个main,我可以通过

创建一个可执行文件

bison -d parser.y; flex scanner.l; gcc main.c parer.tab.c lex.yy.c

当我运行./a.out时,它会按照我的要求执行操作:如果按下Ctrl+D,则会检测到EOF并且main可以采取相应措施。这意味着:如果yyinstdin,则点击Return结束该行的解析,主循环等待下一个输入行。按Ctrl+D结束在主循环中使用break解析输入并退出。如果输入来自文件,e,g,testFile该文件可以包含1个要解析的表达式,直到EOF。在文件场景中,新行应该像空格和制表符一样被吃掉。当输入来自stdin时,所有这些应该像解释器一样,当输入来自文件时,它就像脚本评估器一样。此类测试文件的示例内容为:test\n。这里没有检测到EOF。我很难理解为什么会这样。换句话说,我希望问题here的扩展能够另外处理输入文件

parser.y:

%{
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

/* stuff from flex that bison needs to know about: */
int yylex();
int yyparse();
FILE *yyin;

static int parseValue;

void yyerror(const char *s);
%}

%token TWORD
%token TEOF
%token TJUNK

%start input 

%%
input: word                         {   printf("W"); parseValue =  1;   }   
    | eof                           {   printf("eof"); parseValue = -11;}
    | /* empty */                   {   printf("_"); parseValue = -1;   }   
    | error                         {   printf("E"); parseValue = -2;   }   
    ;

eof: TEOF
    ;

word: TWORD
    ;
%%

void yyerror(const char *s) {
    printf("nope...");
}

int getWord( FILE *file) {
    int err;

    if (file) {
        yyin = file;
    } else /* error */ {
        printf("file not valid");
        return -3; 
    }   

    err = yyparse();
    if (!err) {
        return parseValue;
    } else /* error */ {
        printf("parse error");
        return -4;
    }
}

scanner.l:

%{
#include <stdio.h>
#include "parser.tab.h"
#define YYSTYPE int

int yylex();
%}

/* avoid: implicit declaration of function ‘fileno’ */
/*%option always-interactive*/

%option noyywrap
/* to avoid warning: ‘yyunput’ defined but not used */
%option nounput
/* to avoid warning: ‘input’ defined but not used */
%option noinput

%%
<<EOF>>                     {   return TEOF;    }
[ \t]                       {   }
[\n]                        {   if (yyin == stdin) return 0;   }
[a-zA-Z][a-zA-Z0-9]*        {   return TWORD; }
.                           {   return TJUNK;   }
%%

main.c中:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>

int main(int argc, char *argv[]) {

    int result = 0;
    FILE *fOut = stdout, *fIn = stdin;

    /* skip over program name */
    ++argv, --argc;
    if ( argc > 0 ) { 
        fIn = fopen( argv[0], "r" );
    }   

    while (true) {
        fprintf(fOut, "\nTEST : ", result);

        result = getWord(fIn);

        if (result == -11) {
            printf(" %i ", result); printf("--> EOF");
            break;
        }   
        if (result < 0) {
            printf(" %i ", result); printf("--> <0");
            /*continue;*/
            break;
        }   

        fprintf(fOut, " => %i", result);
    }   

    fprintf(fOut, "\n\n done \n ");
    exit(EXIT_SUCCESS);
}

我试图根据herehere提出的建议重写解析,但没有取得多大成功。当从文件中读取输入时,主要知道EOF的正确方法是什么?

更新 一个建议是该问题可能是由return 0;上的\n引起的。作为快速测试,如果yyin == stin我只返回0,但调用./a.out testFile仍然无法捕获EOF更新2: 我通过使用yywrap让这个工作。我摆脱了所有TEOF的东西。扫描仪有一个部分:

extern int eof;

最后:

int yywrap() {
    eof = 1;
    return 1;
}

在解析器中有一个:

int eof = 0;

并在文件中进一步向下:

err = yyparse();
if (err != 0) return -4;
else if (eof) return -11;
else return parseValue;

如果有人能给我一个更优雅的解决方案,我仍然会很感激。 This可能是制作干净版本的好方法。

1 个答案:

答案 0 :(得分:2)

如您的链接中所述,flex具有识别输入文件或流的结尾的语法(例如,来自字符串的输入)。

事实上,flex实际上有这样的规则在任何时候运作。默认情况下,规则会调用yywrap。你把它关了(%noyywrap)。那很好,除了......

遇到“EOF令牌”的默认操作是返回0.

bison(和byacc)生成的解析器需要查看此零令牌。请参阅this answerEND OF FILE token with flex and bison (only works without it)

您的词法分析器在遇到换行符时返回0令牌。那会造成各种麻烦。毫无疑问,这会导致你从文件中读到的内容。


编辑:好的,不管怎样,并且应用了更新,让我们考虑你的语法。

请记住,野牛添加了一个寻找零令牌的特殊制作。让我们用$表示(正如人们通常做的那样,或者有时是$end)。所以你的整个语法(没有任何动作和“错误”被删除,因为它也很特殊)是:

$all : input $;

input: word | eof | /* empty */;

word: TWORD;

eof: TEOF;

这意味着你的语法接受的唯一句子是:

TWORD $

或:

TEOF $

或:

$

因此,当您调用yyparse()时,yyparse()内的循环将从词法分析器中预读一个标记,并在标记为零值结束时接受(并返回)结果。档案$。如果没有,则令牌必须是TWORDTEOF之一(其他任何内容都会导致调用yyerror()并尝试重新同步)。如果令牌是两个有效令牌之一,yyparse()将再次调用词法分析器以验证下一个令牌是零值的文件结束$令牌。

如果所有这些都成功,yyparse()将返回成功。

重新添加操作,您应该看到printf输出,并根据用于识别(最多一个)令牌的缩减规则获取存储在parseValue中的值。