【问题标题】:Why yacc can not parse a second line of a grammar rule even when it parses correctly the first one?为什么即使正确解析了第一行,yacc 也无法解析语法规则的第二行?
【发布时间】:2020-07-14 18:11:28
【问题描述】:

我想将行解析为这一行:

20191201 170003296,1.102290,1.102470,0

为此,我编写了以下基本的 yacc/lex 语法/词法分析器:

grammar.y

%{

#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 '\n' { printf("MATCH %lf %lf %ld\n", $3, $5, $7); }
                        ;

%%

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

        yyparse();

        return 0;

}

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(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));


}

/* YYERROR */
void yyerror(const char* msg) {
        fprintf(stderr, "yyerror %s\n", msg);
        exit(1);
}

ma​​kefile

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

当我测试程序时,我得到以下行为,第一行被正确解析,但第二行导致 yyerror:

% ./readfile 
20191201 170003296,1.102290,1.102470,0
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
20191201 170004413,1.102320,1.102470,0
void read_date_YYYYMMDD_HHMMSSmmm(void)
#20191201 170004413#
Sun Dec  1 17:00:04 2019 CET
yyerror syntax error

为什么第二行会失败?

【问题讨论】:

  • yacc 解析的不是行而是整个文件,但是您已经描述过您的语言将永远有一个lastbid_lastask...
  • 我的目标是为每个输入行执行语法,在此示例中,语法中只有一个规则,但想法是会有更多规则,以便程序能够解析一次预定义的一组 CSV 文件格式。关于如何在 yacc 中完成的任何提示(我的意思是逐行阅读并解析)
  • 我在语法规则中添加了return语句。没有它,程序在后续解析中失败,现在我正在努力检测何时没有更多输入(CONTROL+D)
  • 不要将 return 语句放入 yacc 规则中。与词法分析器不同,解析器并不期望这样,它可能最终导致内部变量处于不一致的状态。如果您希望规则立即接受输入,请使用 YYACCEPT(如果您希望解析失败并返回非零返回码,请使用 YYABORT。)

标签: c yacc lex c89


【解决方案1】:

你的语法正好匹配一行。

如果要匹配多行,在开头添加:

input: lastbid_lastask
     | input lastbid_lastask

或者,如果您愿意接受空文件(为什么不呢?)

input: /* empty */
     | input lastbid_lastask

【讨论】:

  • 修复了多行,现在我意识到它是书中使用的结构。但是如何处理 CONTROL+D 以反映没有更多输入?从技术上讲,这将是另一个问题,但与此高度相关
  • 你不需要做任何事情。这一切都会自动发生。 (如果您对 ctrl+d 有疑问,请描述它是什么。)
  • 好的,我将把它包含在另一个问题中,因为我确实有 CONTROL+D 的问题。
猜你喜欢
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2015-04-19
  • 1970-01-01
  • 2022-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多