【发布时间】:2020-07-14 14:37:50
【问题描述】:
我正在准备一个 yacc/lex 测试程序。词法分析器旨在以特定格式 (YYYYMMDD HHMM) 读取整数 (long)、浮点数 (double) 和日期时间。
lexer.l
%{
#include <time.h>
#include "grammar.h"
void read_float_number(void);
void read_integer_number(void);
void read_date_YYYYMMDD_HHMM(void);
void yyerror(const char* msg);
%}
%%
/* SKIP BLANKS AND TABS */
[\t ] { ; }
/* YYYYMMDD HHMM DATE */
[12][09][0-9][0-9][0-1][0-9][0-3][0-9][ ][0-2][0-9][0-5][0-9] { read_date_YYYYMMDD_HHMM(); return DATETIME; }
/* FLOAT NUMBER */
[0-9]+\.[0-9]+ { read_float_number(); return FLOAT_NUMBER; }
/* INTEGER NUMBER */
[0-9]+ { read_integer_number(); return INTEGER_NUMBER; }
%%
/* READ FLOAT NUMBER */
void read_float_number(void) {
sscanf(yytext, "%lf", &yylval.float_number);
}
/* READ INTEGER NUMBER */
void read_integer_number(void) {
sscanf(yytext, "%ld", &yylval.integer_number);
}
/* READ YYYYMMDD HHMM DATE */
void read_date_YYYYMMDD_HHMM(void) {
/* DATETIME STRUCT TM */
struct tm dt;
char buffer[80];
/* READ VALUES */
sscanf(yytext, "%4d%2d%2d %2d%2d", &dt.tm_year, &dt.tm_mon, &dt.tm_mday, &dt.tm_hour, &dt.tm_min);
/* 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 DATETIME */
strftime(buffer, 80, "%c %z %Z\n", &dt);
printf("%s\n", buffer);
/* COPY STRUCT TM TO YACC RETURN VALUE */
memcpy(&dt, &yylval.datetime, sizeof(dt));
}
/* YYERROR */
void yyerror(const char* msg) {
fprintf(stderr, "yyerror %s\n", msg);
exit(1);
}
grammar.y
语法是为了解析这类行(DATETIME,FLOAT,FLOAT,INTEGER):
20191201 17000,1.102290,1.102470,0
%{
#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
%%
lastbid_lastask: DATETIME ',' FLOAT_NUMBER ',' FLOAT_NUMBER ',' INTEGER_NUMBER { printf("MATCH %lf %lf %ld\n", $3, $5, $7); }
;
%%
int main(int argc, char *argv[]) {
yyparse();
return 0;
}
构建一切的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
我运行 readfile 但在解析 DATETIME lex 后似乎调用 yyerror:
% ./readfile
20191201 170003296,1.102290,1.102470,0
Mon Feb 17 22:20:00 2020 +0100 CET
yyerror syntax error
数字也一样:
% ./readfile
45.45
yyerror syntax error
% ./readfile
45
yyerror syntax error
但不适用于任意文本:
% ./readfile
abc
abc
为什么 lex 会调用 yyerror? lex 解析代码中缺少什么?
【问题讨论】:
-
Lex 永远不会调用 yyerror(除非您在操作中明确调用)。只有解析器调用
yyerror(因此将定义放在.y文件中更有意义)。 -
我想当你编译
grammar.c时缺少原型警告被-std=c89抑制了。那不应该是必要的;除了上古版本,flex 和 bison 还可以生成现代 C 代码,甚至在 FreeBSD 上也是如此。 -
我不确定 FreeBSD yacc 是否真的是野牛。阅读手册页我认为不是,它似乎是 Berkeley Yacc,因为它描述了与 Bison 的一些潜在差异。根据手册,Lex 在 FreeBSD 中似乎是灵活的。
-
事实上,FreeBSD 使用了 Berkeley YACC 的衍生版本,最初由 Robert Corbett 编写,目前由 Thomas Dickey 维护。但是它生成的代码应该使用 C99 编译器(甚至 C11 编译器)编译而不会出现警告,尽管它被设计为与 C89 兼容。
-
使用 -std=c89 的唯一原因是我目前正在重读《UNIX 编程环境》和《C 编程语言》和《Yacc 和 Lex -O'Reilly-》