使用 Yacc 和 Lex 进行词汇和语法分析
Lexical & Grammar Analysis using Yacc and Lex
我对 Yacc 和 Lex 编程还很陌生,但我正在使用 C 程序的分析器来训练自己。
但是,我遇到了一个我未能解决的小问题。
当有像 int a,b;
这样的声明时,我想将 a 和 b 保存在一个简单的数组中。我确实设法做到了,但它节省了更多想要的东西。
它实际上是保存 "a," 或 "b;" 而不是 "a" 和 "b"。
它应该工作,因为 </code>should only return <code>tID
这是一个只识别字符串链的正则表达式。我不明白为什么即使将其定义为标记,它也会采用昏迷。有谁知道如何解决这个问题?
这是相应的 yacc 声明:
Declaration :
tINT Decl1 DeclN
{printf("Declaration %s\n", );}
| tCONST Decl1 DeclN
{printf("Declaration %s\n", );}
;
Decl1 :
tID
{$$ = ;
tabvar[compteur].id=; tabvar[compteur].adresse=compteur;
printf("Added %s at adress %d\n", , compteur);
compteur++;}
| tID tEQ E
{$$ = ;
tabvar[compteur].id=; tabvar[compteur].adresse=compteur;
printf("Added %s at adress %d\n", , compteur);
pile[compteur]=;
compteur++;}
;
DeclN :
/*epsilon*/
| tVIR Decl1 DeclN
以及 Lex 文件的摘录:
separateur [ \t\n\r]
id [A-Za-z][A-Za-z0-9_]*
nb [0-9]+
nbdec [0-9]+\.[0-9]+
nbexp [0-9]+e[0-9]+
"," { return tVIR; }
";" { return tPV; }
"=" { return tEQ; }
{separateur} ;
{id} { yylval.str = yytext; return tID; }
{nb}|{nbdec}|{nbexp} { yylval.nb = atoi(yytext); return tNB; }
%%
int yywrap() {return 1;}
问题在于 yytext
是对 lex 的令牌扫描缓冲区的引用,因此它仅在下一次解析器调用 yylex
之前有效。如果要 return,您需要对 yytext
中的字符串进行 复制。类似于:
{id} { yylval.str = strdup(yytext); return tID; }
可以解决问题,但它也会让您面临内存泄漏的可能性。
此外,通常在编写涉及单个字符标记的 lex/yacc 解析器时,直接将它们用作字符常量 更 更清楚(例如 ','
、';'
和 '='
),而不是在代码中定义命名标记(tVIR
、tPV
和 tEQ
)。
我对 Yacc 和 Lex 编程还很陌生,但我正在使用 C 程序的分析器来训练自己。
但是,我遇到了一个我未能解决的小问题。
当有像 int a,b;
这样的声明时,我想将 a 和 b 保存在一个简单的数组中。我确实设法做到了,但它节省了更多想要的东西。
它实际上是保存 "a," 或 "b;" 而不是 "a" 和 "b"。
它应该工作,因为 </code>should only return <code>tID
这是一个只识别字符串链的正则表达式。我不明白为什么即使将其定义为标记,它也会采用昏迷。有谁知道如何解决这个问题?
这是相应的 yacc 声明:
Declaration :
tINT Decl1 DeclN
{printf("Declaration %s\n", );}
| tCONST Decl1 DeclN
{printf("Declaration %s\n", );}
;
Decl1 :
tID
{$$ = ;
tabvar[compteur].id=; tabvar[compteur].adresse=compteur;
printf("Added %s at adress %d\n", , compteur);
compteur++;}
| tID tEQ E
{$$ = ;
tabvar[compteur].id=; tabvar[compteur].adresse=compteur;
printf("Added %s at adress %d\n", , compteur);
pile[compteur]=;
compteur++;}
;
DeclN :
/*epsilon*/
| tVIR Decl1 DeclN
以及 Lex 文件的摘录:
separateur [ \t\n\r]
id [A-Za-z][A-Za-z0-9_]*
nb [0-9]+
nbdec [0-9]+\.[0-9]+
nbexp [0-9]+e[0-9]+
"," { return tVIR; }
";" { return tPV; }
"=" { return tEQ; }
{separateur} ;
{id} { yylval.str = yytext; return tID; }
{nb}|{nbdec}|{nbexp} { yylval.nb = atoi(yytext); return tNB; }
%%
int yywrap() {return 1;}
问题在于 yytext
是对 lex 的令牌扫描缓冲区的引用,因此它仅在下一次解析器调用 yylex
之前有效。如果要 return,您需要对 yytext
中的字符串进行 复制。类似于:
{id} { yylval.str = strdup(yytext); return tID; }
可以解决问题,但它也会让您面临内存泄漏的可能性。
此外,通常在编写涉及单个字符标记的 lex/yacc 解析器时,直接将它们用作字符常量 更 更清楚(例如 ','
、';'
和 '='
),而不是在代码中定义命名标记(tVIR
、tPV
和 tEQ
)。