2

弹性代码:

  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”?

4

1 回答 1

7

简短的回答:

yylval.strval = yytext;

你不能这样用yytext。它指向的字符串是词法分析器私有的,并且会在 flex 操作完成后立即更改。您需要执行以下操作:

yylval.strval = strdup(yytext);

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


更长的答案:

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

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

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

于 2012-10-31T20:13:26.343 回答