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

我有一个扫描仪,解析器和一个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。 我很难理解为什么会这样。 换句话说,我想在这里扩展问题,以便另外使用输入文件

parser.y:

 %{ #include  #include  #include  /* 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  #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 %% <> { return TEOF; } [ \t] { } [\n] { if (yyin == stdin) return 0; } [a-zA-Z][a-zA-Z0-9]* { return TWORD; } . { return TJUNK; } %% 

main.c中:

 #include  #include  #include  #include  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   %i", result); } fprintf(fOut, "\n\n done \n "); exit(EXIT_SUCCESS); } 

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

更新:一个建议是该问题可能是由于return 0;\n 。 作为一个快速测试,我只返回0如果yyin == stin ./a.out testFile但是调用./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; 

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

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

实际上, flex实际上有这样的规则在任何时候都在运行。 默认情况下,规则调用yywrap 。 你把它关了(用%noyywrap )。 那没关系,除了……

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

bison(和byacc)生成的解析器需要查看此零令牌。 请参阅带有flex和bison的END OF FILE标记的 答案 (仅在没有它的情况下有效) 。

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


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

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

 $all : input $; input: word | eof | /* empty */; word: TWORD; eof: TEOF; 

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

 TWORD $ 

要么:

 TEOF $ 

要么:

 $ 

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

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

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