【问题标题】:Flex/Bison: yytext skips over a valueFlex/Bison:yytext 跳过一个值
【发布时间】:2018-03-17 01:15:41
【问题描述】:

两天来我一直在绞尽脑汁想弄清楚程序为什么会这样。对于一个类项目,我正在尝试编写一个解析地址并以某种方式输出的程序。在我真正进入程序的输出部分之前,我只是想确保我的 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 文件中被定义为外部变量(此处未发布);
  • 我正在使用-vdy 标志来编译 parse.c 文件

【问题讨论】:

    标签: c linux bison flex-lexer


    【解决方案1】:

    如果您想跟踪解析器的工作,最好启用 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中有解释;如果您不使用 -t 标志,#if 允许您的程序编译。关于标志,我强烈建议您不要使用-y 标志;它用于编译依赖某些过时特性的旧 Yacc 程序。如果您不使用 -y,那么 bison 将使用您的 .y 文件的基本名称以及扩展名为 .tab.c 和 .tab.h 的生成文件。

    现在,您的 bison 文件表明您的某些标记具有语义类型,但您的 flex 操作没有为这些标记设置语义值,并且您的 bison 操作不使用语义值。相反,您只需打印yytext 的值。如果您稍微考虑一下,您应该能够明白为什么它不起作用。 Bison 是一个 lookahead 解析器;它根据当前的解析状态并查看下一个标记(如有必要)做出解析决策。它通过调用词法分析器来查看下一个标记。当您调用词法分析器时,它会更改 yytext 的值。

    Bison(与其他 yacc 实现不同)并不总是偷看下一个标记。但是在您的邮政编码规则中,它别无选择,因为如果不查看它就无法判断下一个令牌是否是-。在这种情况下,它不是破折号。这是一个换行符。所以猜猜yytext 在 zipcode 操作中打印出来时包含什么。

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

    【讨论】:

      【解决方案2】:

      因为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); 
      }
      

      【讨论】:

      • 这种机制的问题是解析器需要知道它什么时候必须释放()yylval,否则你会泄漏内存。这是一个相当大的代价,特别是因为 yytext 旨在用作全局变量(这就是它没有声明为静态的原因,以及为什么你有一种机制来更改它的名称)。
      • @Jabberwock,在一个完整且真实的解析器中,您通常会传递一个指向包含创建 AST 方法的对象的指针,然后您可以在需要时释放所需的一切。当然,还有其他方法不需要复制,例如如果你知道它是固定长度的,那么你可以使用数组结合strcpy()。
      • 使用 bison 创建 AST 对我来说似乎是错误的——是的,AST 比 bison 解析器灵活得多,但是对于简单的解析需求,bison 是完美的。我不明白你关于“其他不需要复制的方法......使用与strcpy结合的数组”的观点。调用 strcpy() 会进行数据复制。 (所有这一切都与我最初的观点是正交的,即 yytext 旨在作为一个全局变量——它是 flex 的 API 的一部分。没有理由增加复杂性并冒着内存泄漏的风险来避免使用它。)
      • @Jabberwock 你说得对,我的意思不是“复制”,我指的是动态内存分配。我的意思是,您可以从野牛解析器构建 AST,而且确实可以。我不明白您所说的“AST 比野牛解析器灵活得多”是什么意思,它们没有可比性。我只是想提一个例子,其中一个上下文,所有字符串都以这样的方式捕获和组织,以便在解析结束时使用它们,如果需要,以后可以释放它们。
      【解决方案3】:

      问题出在这里:

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

      解析器不知道要采用哪个规则--25 或 26--直到它解析下一个标记以查看它是否是 DASHTOKEN。到代码执行时,yytext 已经被覆盖了。

      处理这个问题的最简单的方法是有一个产生式,它接受 INTTOKEN 并返回 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); }
              ;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-08-22
        • 2013-03-09
        • 1970-01-01
        相关资源
        最近更新 更多