为什么会出现这种情况+;lex basic解析器不处理控件+;D/EOF?
我有一个yacc/lex程序来处理这类行(在本例中,它只处理一种格式,但其思想是它显然会处理更多格式): lex词法分析器(lexer.l):为什么会出现这种情况+;lex basic解析器不处理控件+;D/EOF?,c,yacc,lex,c89,C,Yacc,Lex,C89,我有一个yacc/lex程序来处理这类行(在本例中,它只处理一种格式,但其思想是它显然会处理更多格式): lex词法分析器(lexer.l): %{ #include <time.h> #include "grammar.h" void read_float_number(void); void read_integer_number(void); void read_date_YYYYMMDD_HHMMSSmmm(void); void yyerror(co
%{
#include <time.h>
#include "grammar.h"
void read_float_number(void);
void read_integer_number(void);
void read_date_YYYYMMDD_HHMMSSmmm(void);
void yyerror(const char* msg);
%}
%%
/* YYYYMMDD HHMMSSmmm DATE */
[12][09][0-9][0-9][0-1][0-9][0-3][0-9][ ][0-2][0-9][0-5][0-9][0-5][0-9][0-9][0-9][0-9] { read_date_YYYYMMDD_HHMMSSmmm(); return DATETIME; }
/* FLOAT NUMBER */
[0-9]+\.[0-9]+ { read_float_number(); return FLOAT_NUMBER; }
/* INTEGER NUMBER */
[0-9]+ { read_integer_number(); return INTEGER_NUMBER; }
/* PASS ',' CHARACTER */
, { return ','; }
/* PASS '\n' CHARACTER */
\n { return '\n'; }
/* PASS UNEXPECTED CHARACTER */
. { return yytext[0]; }
%%
/* READ FLOAT NUMBER */
void read_float_number(void) {
printf("void read_float_number(void)\n");
printf("#%s#\n", yytext);
sscanf(yytext, "%lf", &yylval.float_number);
printf("%lf\n", yylval.float_number);
}
/* READ INTEGER NUMBER */
void read_integer_number(void) {
printf("void read_integer_number(void)\n");
printf("#%s#\n", yytext);
sscanf(yytext, "%ld", &yylval.integer_number);
printf("%ld\n", yylval.integer_number);
}
/* READ YYYYMMDD HHMMSSmmm DATE */
void read_date_YYYYMMDD_HHMMSSmmm(void) {
printf("void read_date_YYYYMMDD_HHMMSSmmm(void)\n");
printf("#%s#\n", yytext);
/* DATETIME STRUCT TM */
struct tm dt;
/* READ VALUES */
sscanf(yytext, "%4d%2d%2d %2d%2d%2d", &dt.tm_year, &dt.tm_mon, &dt.tm_mday, &dt.tm_hour, &dt.tm_min, &dt.tm_sec);
/* NORMALIZE VALUES */
dt.tm_year = dt.tm_year - 1900; /* NORMALIZE YEAR */
dt.tm_mon = dt.tm_mon - 1; /* NORMALIZE MONTH */
dt.tm_isdst = -1; /* NO INFORMATION ABOUT DST */
mktime(&dt); /* NORMALIZE STRUCT TM */
/* PRINT DATE TIME */
char buffer[80];
strftime(buffer, 80, "%c %Z", &dt);
printf("%s\n", buffer);
/* COPY STRUCT TM TO YACC RETURN VALUE */
memcpy(&yylval.datetime, &dt, sizeof(dt));
}
%{
#include <time.h>
#include <stdio.h>
%}
%union {
struct tm datetime; /* DATE TIME VALUES */
double float_number; /* 8 BYTES DOUBLE VALUE */
long integer_number; /* 8 BYTES INTEGER VALUE */
}
%token <datetime> DATETIME
%token <float_number> FLOAT_NUMBER
%token <integer_number> INTEGER_NUMBER
%%
input: /* empty */
| input lastbid_lastask
lastbid_lastask: DATETIME ',' FLOAT_NUMBER ',' FLOAT_NUMBER ',' INTEGER_NUMBER '\n' { printf("MATCH %lf %lf %ld\n", $3, $5, $7); }
;
%%
extern FILE *yyin;
int main(int argc, char *argv[]) {
while(!feof(yyin)) {
yyparse();
}
return 0;
}
% cat makefile
CCFLAGS = -std=c89 -c
YFLAGS = -d # Forces generation of y.tab.h
OBJS = lexer.o grammar.o
TARGET = readfile
readfile: $(OBJS)
cc $(OBJS) -std=c89 -ll -o $(TARGET)
grammar.h grammar.o: grammar.y
yacc $(YFLAGS) -ogrammar.c grammar.y
cc $(CCFLAGS) grammar.c
lexer.o: lexer.l grammar.h
lex -olexer.c lexer.l
cc $(CCFLAGS) lexer.c
clean:
rm -f $(OBJS) grammar.[ch] lexer.c
现在我编译了程序,没有错误,但当我尝试执行它时,我得到以下结果:
% cat test.csv | ./readfile
Segmentation fault (core dumped)
% cat test.csv | ./readfile
void read_date_YYYYMMDD_HHMMSSmmm(void)
#20191201 170003296#
Sun Dec 1 17:00:03 2019 CET
void read_float_number(void)
#1.102290#
1.102290
void read_float_number(void)
#1.102470#
1.102470
void read_integer_number(void)
#0#
0
MATCH 1.102290 1.102470 0
void read_date_YYYYMMDD_HHMMSSmmm(void)
#20191201 170004413#
Sun Dec 1 17:00:04 2019 CET
void read_float_number(void)
#1.102320#
1.102320
void read_float_number(void)
#1.102470#
1.102470
void read_integer_number(void)
#0#
0
...
现在,如果我替换:
while(!feof(yyin))
与:
然后我得到这个:
% cat test.csv | ./readfile
Segmentation fault (core dumped)
% cat test.csv | ./readfile
void read_date_YYYYMMDD_HHMMSSmmm(void)
#20191201 170003296#
Sun Dec 1 17:00:03 2019 CET
void read_float_number(void)
#1.102290#
1.102290
void read_float_number(void)
#1.102470#
1.102470
void read_integer_number(void)
#0#
0
MATCH 1.102290 1.102470 0
void read_date_YYYYMMDD_HHMMSSmmm(void)
#20191201 170004413#
Sun Dec 1 17:00:04 2019 CET
void read_float_number(void)
#1.102320#
1.102320
void read_float_number(void)
#1.102470#
1.102470
void read_integer_number(void)
#0#
0
...
所以它是可行的,但程序不会以EOF结束。
虽然我知道核心转储可能意味着很多事情,但我可以做些什么来进一步定位问题并获得正常行为?不要在循环中调用yyparse()
。它将解析整个输入并返回;当它返回时,您知道整个输入已被解析(或遇到语法错误)。不需要进行任何EOF测试
(有一些单独的情况需要打破这一规则,其中大多数都与扫描仪返回输入结束指示符有关,而不是在输入结束时,或者解析器使用yyapt/YYABORT
有序地提前终止解析。换句话说,如果您有需要打破这一规则的情况,你已经知道你必须这么做。)
while(!feof(file)){…}
有一个完整的解释为什么它几乎总是一个bug。(摘要:EOF标志是在读取检测到EOF之后设置的,因此在执行读取之前没有设置EOF的事实证明了什么。while(!feof(file))
idiom很好地保证了在文件末尾您将得到一个意外的EOF——这在“但我刚刚检查了EOF…”的意义上是意外的
不过,我不认为FAQ涵盖了这个特定的问题,这是针对使用(f)lex的程序的。当(f)lex扫描器到达文件末尾时,它会将yyin
设置为NULL。然后,如果yywrap
告诉它没有更多的输入,yylex
返回0,这告诉它的调用者(yyparse
)到达文件结尾。然后yyparse
完成解析并返回。如果然后循环,yyin
为NULL,feof(NULL)
为未定义的行为。这就是程序出错的原因
当您删除feof
测试(但仍然循环)时,您重新输入yyparse
,但这次yyin
设置为NULL
。flex扫描仪认为这意味着“使用默认输入”,即stdin
。如果yyin
以前是某个输入文件,这意味着yyparse
的新调用将尝试从终端获取其输入,这可能不是您所期望的。另一方面,如果是stdin
达到EOF,那么您将处于一个硬循环中,不断地接收g来自stdin
的新EOF信号不在循环中调用yyparse()
。它将解析整个输入并返回;当它返回时,您知道整个输入已被解析(或遇到语法错误)。应该不需要任何EOF测试
(有一些单独的情况需要打破这一规则,其中大多数都与扫描仪返回输入结束指示符有关,而不是在输入结束时,或者解析器使用yyapt/YYABORT
有序地提前终止解析。换句话说,如果您有需要打破这一规则的情况,你已经知道你必须这么做。)
while(!feof(file)){…}
有一个完整的解释为什么它几乎总是一个bug。(摘要:EOF标志是在读取检测到EOF之后设置的,因此在执行读取之前没有设置EOF的事实证明了什么。while(!feof(file))
idiom很好地保证了在文件末尾您将得到一个意外的EOF——这在“但我刚刚检查了EOF…”的意义上是意外的
不过,我不认为FAQ涵盖了这个特定的问题,这是针对使用(f)lex的程序的。当(f)lex扫描器到达文件末尾时,它会将yyin
设置为NULL。然后,如果yywrap
告诉它没有更多的输入,yylex
返回0,这告诉它的调用者(yyparse
)到达文件结尾。然后yyparse
完成解析并返回。如果然后循环,yyin
为NULL,feof(NULL)
为未定义的行为。这就是程序出错的原因
当您删除
feof
测试(但仍然循环)时,您重新输入yyparse
,但这次yyin
设置为NULL
。flex扫描仪认为这意味着“使用默认输入”,即stdin
。如果yyin
以前是某个输入文件,这意味着yyparse
的新调用将尝试从终端获取其输入,这可能不是您所期望的。另一方面,如果是stdin
达到EOF,那么您将处于一个硬循环中,不断地接收g来自stdin的新EOF信号
为什么要调用yyparse()
是否在循环中?我的理解是,您只需调用它一次,它就会解析整个输入,并在达到EOF时返回。移除循环可以处理CONTROL+D。这不是一个借口,只是一个信息性的旁注,循环和yyin文件的用法包含在“Lex和Yacc”的第一个示例中O'ReillySee的书为什么打电话给yyparse()
是否在循环中?我的理解是,您只需调用它一次,它就会解析整个输入,并在达到EOF时返回。移除循环可以处理CONTROL+D。这不是一个借口,只是一个信息性的旁注,循环和yyin文件的用法包含在“Lex和Yacc”的第一个示例中O'ReillySee的书出于某种原因(可能是一种特殊情况),使用,而(!feof(yyin))
包含在O'Reilly的Lex和Yacc书中。删除它并添加语法规则