我最近开始使用Lex作为解释我遇到的问题的一种简单方法,假设我正在尝试使用Flex实现一个词法分析器,它可以打印所有字母以及给定文本中的所有双字母组合,这看起来非常简单,很简单,但是一旦我实现了它,我就会意识到它首先显示了bigrams并且只在单个时显示字母,例如:对于以下文本
QQQZ ,JQR
结果是
Bigram QQ
Bigram QZ
Bigram JQ
Letter R
Done
这是我的lex代码
%{
%}
letter[A-Za-z]
Separ [ \t\n]
%%
{letter} {
printf(" Letter %c\n",yytext[0]);
}
{letter}{2} {
printf(" Bigram %s\n",yytext);
}
%%
main()
{ yylex();
printf("Done");
}
我的问题是如何分开实现这两个分析,知道我的实际问题并不像这个例子那么简单
词法分析器将源文本划分为单独的标记。如果您的问题看起来像那样,那么(f)lex是一个合适的工具。如果你的问题看起来不像那样,那么(f)lex可能不是正确的工具。
对文本进行两次同时分析并不是(f)lex的用例。一种可能性是使用两个独立的折返词法分析器,安排给它们提供相同的输入。然而,对于一个可以在几行C中轻松解决的问题,这将是很多工作。
既然你说你的问题与问题中的简单问题有所不同,我就没有费心去编写简单的C代码或更复杂的代码来生成和运行两个独立的词法分析器,因为不可能知道是否这些解决方案完全相关。
如果你的问题确实与来自同一起始位置的两个(或更多)不同的词汇相匹配,你可以使用两种策略中的一种,两者都非常难看(恕我直言):
void handle_letter(char ch);
void handle_bigram(char* s); /* Expects NUL-terminated string */
void handle_trigram(char* s); /* Expects NUL-terminated string */
REJECT
动作,这会导致当前匹配被丢弃。我们的想法是让您处理匹配,然后拒绝它以处理更短(或替代)的匹配。使用flex时,强烈建议不要使用REJECT
,因为它的效率非常低,并且还会阻止词法分析器调整输入缓冲区的大小,这会随意限制可识别令牌的长度。但是,在这个特定的用例中,它非常简单:
[[:alpha:]][[:alpha:]][[:alpha:]] handle_trigram(yytext); REJECT;
[[:alpha:]][[:alpha:]] handle_bigram(yytext); REJECT;
[[:alpha:]] handle_letter(*yytext);
如果你想尝试这个解决方案,我建议使用flex的调试工具(flex -d ...
)来查看发生了什么。
见debugging options和REJECT documentation。yyless()
来重新处理部分识别的令牌。这比REJECT
更有效率; yyless()
只更改一个指针,因此它对速度没有影响。如果没有REJECT
,我们必须知道所需的所有lexeme处理程序,但这并不是很困难。复杂性是handle_bigram
的接口,它需要一个以NUL结尾的字符串。如果您的处理程序没有强加此要求,则代码会更简单。
[[:alpha:]][[:alpha:]][[:alpha:]] { handle_trigram(yytext);
char tmp = yytext[2];
yytext[2] = 0;
handle_bigram(yytext);
yytext[2] = tmp;
handle_letter(yytext[0]);
yyless(1);
}
[[:alpha:]][[:alpha:]] { handle_bigram(yytext);
handle_letter(yytext[0]);
yyless(1);
}
[[:alpha:]] handle_letter(*yytext);
见yyless() documentation