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

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 <> {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  ID 16 %token  INT 17 %token CREATE 18 %token CMD 19 20 %type  col_definition 21 %type  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”?

简短回答:

 yylval.strval = yytext; 

你不能像那样使用yytext 。 它指向的字符串对词法分析器是私有的,并且一旦flex动作结束就会改变。 你需要做一些事情:

 yylval.strval = strdup(yytext); 

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


更长的答案:

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

所以黄金法则: yytext只有在行动结束之前才有效。 如果要保留它,请复制它,然后确保在不再需要时为该副本释放存储空间。

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