我有一个扫描程序,解析器和一个main,我可以通过
创建一个可执行文件 bison -d parser.y; flex scanner.l; gcc main.c parer.tab.c lex.yy.c
当我运行./a.out
时,它会按照我的要求执行操作:如果按下Ctrl+D
,则会检测到EOF
并且main
可以采取相应措施。这意味着:如果yyin
为stdin
,则点击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);
}
我试图根据here或here提出的建议重写解析,但没有取得多大成功。当从文件中读取输入时,主要知道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可能是制作干净版本的好方法。
答案 0 :(得分:2)
如您的链接中所述,flex
具有识别输入文件或流的结尾的语法(例如,来自字符串的输入)。
事实上,flex
实际上有这样的规则在任何时候运作。默认情况下,规则会调用yywrap
。你把它关了(%noyywrap
)。那很好,除了......
遇到“EOF令牌”的默认操作是返回0.
bison(和byacc)生成的解析器需要查看此零令牌。请参阅this answer至END 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()
内的循环将从词法分析器中预读一个标记,并在标记为零值结束时接受(并返回)结果。档案$
。如果没有,则令牌必须是TWORD
或TEOF
之一(其他任何内容都会导致调用yyerror()
并尝试重新同步)。如果令牌是两个有效令牌之一,yyparse()
将再次调用词法分析器以验证下一个令牌是零值的文件结束$
令牌。
如果所有这些都成功,yyparse()
将返回成功。
重新添加操作,您应该看到printf
输出,并根据用于识别(最多一个)令牌的缩减规则获取存储在parseValue
中的值。