如何防止 Flex 忽略之前的分析?
How to prevent Flex from ignoring previous analysis?
我最近开始使用 Lex,作为解释我遇到的问题的一种简单方法,假设我正在尝试使用 Flex 实现一个词法分析器,打印给定文本中的所有字母和所有二元语法,这看起来非常容易和简单,但是一旦我实现了它,我就意识到它首先显示双字母,只有当它们是单个时才显示字母,例如:对于以下文本
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 */
由于历史原因,lex 实现了 REJECT
动作,导致当前匹配被丢弃。这个想法是让你处理一个匹配项,然后拒绝它以处理一个更短的(或替代的)匹配项。对于 flex,强烈建议不要使用 REJECT
,因为它效率极低,而且还会阻止词法分析器调整输入缓冲区的大小,这会任意限制可识别标记的长度。但是,在这个特定的用例中,它非常简单:
[[:alpha:]][[:alpha:]][[:alpha:]] handle_trigram(yytext); REJECT;
[[:alpha:]][[:alpha:]] handle_bigram(yytext); REJECT;
[[:alpha:]] handle_letter(*yytext);
如果您想尝试此解决方案,我建议使用 flex 的调试工具 (flex -d ...
) 以查看发生了什么。
虽然代码有点笨拙,但我实际上推荐的解决方案是使用yyless()
重新处理部分已识别的令牌。这比 REJECT
效率高很多; yyless()
只是改变了一个指针,所以对速度没有影响。如果没有 REJECT
,我们必须知道所有需要的词素处理程序,但这并不难。复杂的是 handle_bigram
的接口,它需要 NUL-terminated 字符串。如果您的处理程序没有强加此要求,代码会更简单。
[[: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);
我最近开始使用 Lex,作为解释我遇到的问题的一种简单方法,假设我正在尝试使用 Flex 实现一个词法分析器,打印给定文本中的所有字母和所有二元语法,这看起来非常容易和简单,但是一旦我实现了它,我就意识到它首先显示双字母,只有当它们是单个时才显示字母,例如:对于以下文本
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 */
由于历史原因,lex 实现了
REJECT
动作,导致当前匹配被丢弃。这个想法是让你处理一个匹配项,然后拒绝它以处理一个更短的(或替代的)匹配项。对于 flex,强烈建议不要使用REJECT
,因为它效率极低,而且还会阻止词法分析器调整输入缓冲区的大小,这会任意限制可识别标记的长度。但是,在这个特定的用例中,它非常简单:[[:alpha:]][[:alpha:]][[:alpha:]] handle_trigram(yytext); REJECT; [[:alpha:]][[:alpha:]] handle_bigram(yytext); REJECT; [[:alpha:]] handle_letter(*yytext);
如果您想尝试此解决方案,我建议使用 flex 的调试工具 (
flex -d ...
) 以查看发生了什么。虽然代码有点笨拙,但我实际上推荐的解决方案是使用
yyless()
重新处理部分已识别的令牌。这比REJECT
效率高很多;yyless()
只是改变了一个指针,所以对速度没有影响。如果没有REJECT
,我们必须知道所有需要的词素处理程序,但这并不难。复杂的是handle_bigram
的接口,它需要 NUL-terminated 字符串。如果您的处理程序没有强加此要求,代码会更简单。[[: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);