Flex / Bison:yytext跳过一个值

时间:2018-03-17 01:15:41

标签: c linux bison flex-lexer

我已经绞尽脑汁两天试图弄清楚程序为什么会这样。对于一个类项目,我正在尝试编写一个解析地址并以某种方式输出它的程序。在我实际到达程序的输出部分之前,我只是想确保我的Bison-fu实际上是正确的并正确输出一些调试信息。

看起来好像Flex和Bison正如预期的那样很好地相互合作,但出于某种原因,当我开始解析地址的第三行时,yytext只是跳过邮政编码并直接进入新线。

下面是我测试的Flex和Bison文件的精简版本,但仍然输出完整版本的相同内容:

[19:45]<Program4> $ cat scan.l
%option noyywrap
%option nounput
%option noinput

%{
#include <stdlib.h>
#include "y.tab.h"
#include "program4.h"
%}

%%

[\ \t]+                 { /* Eat whitespace */}
[\n]                    { return EOLTOKEN; }
","                     { return COMMATOKEN; }
[0-9]+                  { return INTTOKEN; }
[A-Za-z]+               { return NAMETOKEN; }
[A-Za-z0-9]+            { return IDENTIFIERTOKEN; }

%%

/*This area just occupies space*/
[19:45]<Program4> $ cat parse.y


%{
#include <stdlib.h>
#include <stdio.h>
#include "program4.h"

%}

%union {int num; char id[20]; }
%start locationPart
%expect 0
%token <num> NAMETOKEN
%token <num> EOLTOKEN
%token <num> INTTOKEN
%token <num> COMMATOKEN
%type <id> townName zipCode stateCode

%%

/* Entire block */
locationPart:           townName COMMATOKEN stateCode zipCode EOLTOKEN          
{ printf("Rule 12: LP: TN COMMA SC ZC EOL: %s\n", yytext); }
| /* bad location part */                               
{ printf("Rule 13: LP: Bad location part: %s\n", yytext); }
                    ;

/* Lil tokens */
townName:               NAMETOKEN                                               
{ printf("Rule 23: TN: NAMETOKEN: %s\n", yytext); }
                    ;

stateCode:              NAMETOKEN                                               
{ printf("Rule 24: SC: NAMETOKEN: %s\n", yytext); }
                    ;

zipCode:                INTTOKEN DASHTOKEN INTTOKEN                             
{ printf("Rule 25: ZC: INT DASH INT: %s\n", yytext); }
                    | INTTOKEN                                              
{ printf("Rule 26: ZC: INT: %s\n", yytext); }
                    ;

%% 

int yyerror (char const *s){
  extern int yylineno; //Defined in lex

  fprintf(stderr, "ERROR: %s at symbol \"%s\"\n at line %d.\n", s, yytext, 
yylineno);
  exit(1);
}
[19:45]<Program4> $ cat addresses/zip.txt
Rockford, HI 12345
[19:45]<Program4> $ parser < addresses/zip.txt
Operating in parse mode.

Rule 23: TN: NAMETOKEN: Rockford
Rule 24: SC: NAMETOKEN: HI
Rule 26: ZC: INT:

Rule 12: LP: TN COMMA SC ZC EOL:

Parse successful!
[19:46]<Program4> $

正如您在底部附近看到的那样,它会打印Rule 26: ZC: INT:但无法打印5位数的邮政编码。这就像程序只是跳过数字并存储换行符。任何想法为什么它不会存储和打印邮政编码?

注意:

  • yytext在我的.h文件中定义为extern(未在此处发布);
  • 我使用-vdy标志来编译parse.c文件

3 个答案:

答案 0 :(得分:2)

如果要跟踪解析器的工作方式,最好启用bison的跟踪功能。这真的很容易。只需将-t--debug标志添加到bison命令以生成代码,然后添加一行以实际生成跟踪:

/* This assumes you have #included the parse.tab.h header */
int main(void) {
#if YYDEBUG
   yydebug = 1;
#endif

the Bison manual中对此进行了解释;如果你不在#if标志,-t允许你的程序编译。关于旗帜的问题,我强烈建议你不要使用-y旗帜;它用于编译依赖于某些过时功能的旧Yacc程序。如果您不使用-y,则bison将使用.y文件的基本名称,其中包含.tab.c.tab.h分机,用于生成的文件。

现在,您的bison文件说您的某些令牌具有语义类型,但您的flex操作不会为这些令牌设置语义值,并且您的野兔操作不会使用语义值。相反,您只需打印yytext的值即可。如果你仔细想一想,你应该能够明白为什么它不会起作用。 Bison是 lookahead 解析器;它根据当前的解析状态和查看下一个令牌(如果需要)查看解析决策。它通过调用词法分析器来查看下一个标记。当你调用词法分析器时,它会改变yytext的值。

Bison(与其他yacc实现不同)并不总是窥视下一个令牌。但是在您的邮政编码规则中,它没有其他选择,因为它无法判断下一个令牌是否为-而没有查看它。在这种情况下,它不是破折号;这是一个换行符。因此,当您在zipcode操作中将其打印出来时,请猜出yytext包含的内容。

如果您的标记生成器要将文本保存在id语义值成员中(这就是它的用途),那么您的解析器将能够以$1,{{1}访问语义值,},

答案 1 :(得分:1)

因为yytext是一个全局变量,所以它被覆盖了,您必须将它复制到 lex 脚本中。在纯粹的解析器中,即使它不再是全局的,它仍然被重用并作为参数传递,因此使用它的值就像你正在尝试的那样是不正确的。

此外,请勿在野外使用它,而是使用$n,其中n是规则中令牌的位置。您可能需要将%union指令更改为

%union {
    int number;
    char *name;
};

所以在 flex 文件中,如果要捕获文本,请执行类似

的操作
[A-Za-z]+               { yylval.name = strdup(yytext); return NAMETOKEN; }

请记住,不要在 bison 中使用yytext,这是词法分子使用的内部事物。

然后,因为您已经为邮政编码定义了一种类型

/* Entire block */
locationPart:           townName COMMATOKEN stateCode zipCode EOLTOKEN {
    printf("Rule 12: LP: TN COMMA SC ZC EOL: town:%s, stateCode:%d zip-code:%s\n", $1, $3, $4); 
}

答案 2 :(得分:0)

问题在于:

zipCode:              INTTOKEN DASHTOKEN INTTOKEN     { // case 25 }        
                    | INTTOKEN                        { // case 26 }  
                    ;

解析器不知道要采取的规则--25或26 - 直到它解析下一个令牌以查看它是否为DASHTOKEN。到代码执行时,yytext已被覆盖。

处理此问题的最简单方法是使用INTTOKENs生成并返回malloc()内存中yytext []的内容。类似的东西:

zipCode:              inttoken DASHTOKEN inttoken
                         {
                              printf("Rule 25: zip is %s-%s\n", $1, $3);
                              free($1);
                              free($3);
                         }                    
                    | inttoken
                         {
                              printf("Rule 26: zip is %s\n", $1);
                              free($1);
                         }                    
                    ;

inttoken: INTTOKEN { $$ = strdup(yytext); }
        ;