#parsing #bison #flex-lexer #yacc
#c #bison #flex-lexer
Вопрос:
гибкий код:
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:%sn" , yylval.strval);return ID;}
15 [ tn]
16 <<EOF>> {return 0;}
17 . {printf("mistery charn");}
18
код bison:
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 cmdn");*/}
30 ;
31
32 create_cmd_stmt:CREATE CMD ID'{'create_col_list'}' {printf("%sn" , $3);}
33 ;
34 create_col_list:col_definition
35 | create_col_list col_definition
36 ;
37
38 col_definition:create_type ID ';' {printf("%d , %sn" , $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 failedn" ,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:
Короткий ответ:
yylval.strval = yytext;
Вы не можете использовать yytext
таким образом. Строка, на которую он указывает, является частной для лексера и изменится, как только действие flex завершится. Вам нужно сделать что-то вроде:
yylval.strval = strdup(yytext);
и затем вам нужно убедиться, что вы впоследствии освободили память.
Более длинный ответ:
yytext
на самом деле это указатель на буфер, содержащий входные данные. Чтобы заставить yytext работать так, как если бы это была строка, заканчивающаяся нулем, flex
фреймворк перезаписывает символ, следующий за токеном, на NUL
перед выполнением действия, а затем заменяет исходный символ, когда действие завершается. Таким образом, strdup
внутри действия все будет работать нормально, но вне действия (в вашем коде bison) теперь у вас есть указатель на часть буфера, начинающуюся с токена. И это становится хуже позже, так как flex
будет считана следующая часть исходного кода в тот же буфер, и теперь ваш указатель на случайный мусор. Существует несколько возможных сценариев, в зависимости от flex
опций, но ни один из них не является приятным.
Итак, золотое правило: yytext
действует только до окончания действия. Если вы хотите сохранить его, скопируйте его, а затем убедитесь, что вы освободили хранилище для копии, когда она вам больше не понадобится.
Почти во всех лексерах, которые я написал, токен ID фактически находит идентификатор в таблице символов (или помещает его туда) и возвращает указатель в таблицу символов, что упрощает управление памятью. Но у вас все еще есть по существу та же проблема с управлением памятью, например, с символьными строковыми литералами.