为什么这个野牛代码会产生意想不到的输出?

时间:2012-10-31 05:06:54

标签: c bison flex-lexer

flex代码:

  1 %option noyywrap nodefault yylineno case-insensitive
  2 %{
  3 #include "stdio.h"
  4 #include "tp.tab.h"
  5 %}
  6 
  7 %%
  8 "{"             {return '{';}
  9 "}"             {return '}';}
 10 ";"             {return ';';}
 11 "create"        {return CREATE;}
 12 "cmd"           {return CMD;}
 13 "int"           {yylval.intval = 20;return INT;}
 14 [a-zA-Z]+       {yylval.strval = yytext;printf("id:%s\n" , yylval.strval);return ID;}
 15 [ \t\n]
 16 <<EOF>>         {return 0;}
 17 .               {printf("mistery char\n");}
 18 

野牛代码:

  1 %{
  2 #include "stdlib.h"
  3 #include "stdio.h"
  4 #include "stdarg.h"
  5 void yyerror(char *s, ...);
  6 #define YYDEBUG 1
  7 int yydebug = 1;
  8 %}
  9 
 10 %union{
 11     char *strval;
 12     int intval;
 13 }
 14 
 15 %token <strval> ID
 16 %token <intval> INT
 17 %token CREATE
 18 %token CMD
 19 
 20 %type <strval> col_definition
 21 %type <intval> create_type
 22 %start stmt_list
 23 
 24 %%
 25 stmt_list:stmt ';'
 26 | stmt_list stmt ';'
 27 ;
 28 
 29 stmt:create_cmd_stmt         {/*printf("create cmd\n");*/}
 30 ;
 31 
 32 create_cmd_stmt:CREATE CMD ID'{'create_col_list'}'    {printf("%s\n" , $3);}
 33 ;
 34 create_col_list:col_definition
 35 | create_col_list col_definition
 36 ;
 37 
 38 col_definition:create_type ID ';' {printf("%d , %s\n" , $1, $2);}
 39 ;
 40 
 41 create_type:INT {$$ = $1;}
 42 ;
 43 
 44 %%
 45 extern FILE *yyin;
 46 
 47 void
 48 yyerror(char *s, ...)
 49 {
 50     extern yylineno;
 51     va_list ap;
 52     va_start(ap, s);
 53     fprintf(stderr, "%d: error: ", yylineno);
 54     vfprintf(stderr, s, ap);
 55     fprintf(stderr, "\n");
 56 }
 57 
 58 int main(int argc , char *argv[])
 59 {
 60     yyin = fopen(argv[1] , "r");
 61     if(!yyin){
 62         printf("open file %s failed\n" ,argv[1]);
 63         return -1;
 64     }
 65 
 66     if(!yyparse()){
 67         printf("parse work!\n");
 68     }else{
 69         printf("parse failed!\n");
 70     }
 71 
 72     fclose(yyin);
 73     return 0;
 74 }
 75

测试输入文件:

create cmd keeplive
{
    int a;
    int b;
};

测试输出:

root@VM-Ubuntu203001:~/test/tpp# ./a.out t1.tp 
id:keeplive
id:a
20 , a;
id:b
20 , b;
keeplive
{
    int a;
    int b;
}
parse work!

我有两个问题:

1)为什么第38行的动作会打印令牌';'?例如,“20,a;”和“20,b;”

2)为什么第32行的行动打印“keeplive     {         int a;         int b;     “而不仅仅是”keeplive“?

1 个答案:

答案 0 :(得分:6)

简短回答:

yylval.strval = yytext;

你不能那样使用yytext。它指向的字符串对词法分析器是私有的,并且一旦flex动作结束就会改变。您需要执行以下操作:

yylval.strval = strdup(yytext);

然后你需要确保你之后释放内存。


更长的答案:

yytext实际上是指向包含输入的缓冲区的指针。为了使yytext工作好像是一个以NUL结尾的字符串,flex框架在执行操作之前用NUL覆盖令牌后面的字符,然后替换原始字符。行动终止。因此strdup在操作中可以正常工作,但在操作之外(在您的野牛代码中),您现在有一个指向缓冲区部分的指针。之后它会变得更糟,因为flex会将源的下一部分读入同一个缓冲区,现在你的指针是随机垃圾。有几种可能的情况,取决于flex选项,但它们都不漂亮。

因此黄金法则:yytext仅在行动结束前有效。如果您想保留它,请将其复制,然后确保在不再需要时为该副本释放存储空间。

在我写的几乎所有词法分析器中,ID令牌实际上在符号表中找到了标识符(或将其放在那里)并将指针返回到符号表中,这简化了内存管理。但是,您仍然存在基本相同的内存管理问题,例如字符串文字。