当flex-lexer生成的扫描程序遇到文件结尾时,它会丢失之前规则中yytext[]
次调用留下的yymore()
内容。如果YY_INPUT()
被重新定义,则会发生这种错误行为。
这可能是flex中的一个错误,但似乎缺少一些东西 - 灵活扫描程序定义在重新定义YY_INPUT()
时应该提供的其他内容。
我已经在Ubuntu 12.04.1和Windows 7上使用flex 2.5.35进行了测试。在两个系统上,如果要通过显式定义提供要扫描的文本,扫描程序会丢失EOF中的yytext[]
内容YY_INPUT()
。
下面是一个示例灵活扫描程序(flex-test.l
),用于读取和打印HTML注释,即使最后一条注释未被终止。当它的输入是通过yy_scan_string()
提供时它可以正常工作,但是当它的输入由YY_INPUT()
的明确定义提供时失败。在示例代码中,#if
用于在yy_scan_string()
和YY_INPUT()
实现之间进行选择。
具体来说,预期的输出:
Begin comment: <!--
More comment: <!--incomplete
EOF comment: <!--incomplete
如果使用构建扫描仪,则会出现
flex --nounistd flex-test.l && gcc -DREDEFINE_YY_INPUT=0 lex.yy.c
但是如果使用
构建扫描仪flex --nounistd flex-test.l && gcc -DREDEFINE_YY_INPUT=1 lex.yy.c
(将 = 0 更改为 = 1 ),然后显示错误输出:
Begin comment: <!--
More comment: <!--incomplete
EOF comment:
请注意最后一行输出中没有任何评论文本。
以下是示例代码:
/* A scanner demonstrating bad interaction between yymore() and <<EOF>>
* when YY_INPUT() is redefined: specifically, yytext[] content is lost. */
%{
#include <stdio.h>
int yywrap(void) { return 1; }
#if REDEFINE_YY_INPUT
#define MIN(a,b) ((a)<(b) ? (a) : (b))
const char *source_chars;
size_t source_length;
#define set_data(s) (source_chars=(s), source_length=strlen(source_chars))
size_t get_data(char *buf, size_t request_size) {
size_t copy_size = MIN(request_size, source_length);
memcpy(buf, source_chars, copy_size);
source_chars += copy_size;
source_length -= copy_size;
return copy_size;
}
#define YY_INPUT(buf,actual,ask) ((actual)=get_data(buf,ask))
#endif
%}
%x COMM
%%
"<!--" printf("Begin comment: %s\n", yytext); yymore(); BEGIN(COMM);
<COMM>[^-]+ printf("More comment: %s\n", yytext); yymore();
<COMM>. printf("More comment: %s\n", yytext); yymore();
<COMM>--+\ *[>] printf("End comment: %s\n", yytext); BEGIN(INITIAL);
<COMM><<EOF>> printf("EOF comment: %s\n", yytext); BEGIN(INITIAL); return 0;
. printf("Other: %s\n", yytext);
<<EOF>> printf("EOF: %s\n", yytext); return 0;
%%
int main(int argc, char **argv) {
char *text = "<!--incomplete";
#if REDEFINE_YY_INPUT
set_data(text);
yylex();
#else
YY_BUFFER_STATE state = yy_scan_string(text);
yylex();
yy_delete_buffer(state);
#endif
}
答案 0 :(得分:2)
此问题一直处于弹性状态,可能是永远的。基本上,当flex从其当前缓冲区获取EOF时,它会处理最后一个令牌,然后重新初始化缓冲区,这会有效地抛出当前令牌,即使它已被yymore()
“保存”。 (它实际上用NUL
s初始化前两个字符,但这足以销毁它。)然后调用yywrap()
,它可以选择提供另一个缓冲区(文件)。
这种行为通常是无害的,因为通常不允许令牌跨越两个不同的输入文件,但有时候选择它会很好。然而,在灵魂存在的四分之一世纪中,任何人都不愿意修复它,这还不够好。
不幸的结果是,在获得yytext
之后,您无法使用EOF
,因为即使没有更多输入文件,缓冲区重置已经完成。 (yyleng
也不正确;它尚未重置,并且还会因触发EOF的NUL
而增加。)
yy_scan_string
的实现中有一个hack,它将新创建的缓冲区的yy_fill_buffer
标志设置为0,这意味着不应该尝试重新填充缓冲区。这可以防止缓冲区重置,但不会保护yyleng
,这仍然是不正确的。不过,我会考虑保留yytext
纯粹的运气。
如果正在积极维护flex,我建议在弹性手册中添加yytext
和yyleng
在<<EOF>>
规则中未定义的评论,甚至可能考虑修复yymore()
以便它允许跨越输入缓冲区(或记录它没有的事实)。
简而言之,您有两种选择:
1)只需使用yy_scan_string
或yy_scan_buffer
(包含所有适当的警告),并希望没有人恢复黑客攻击,以便您查看{{1}中的yytext
}} 规则。我不知道希望如何面向未来,但没有什么可以迫使你升级。
但你可能会更好:
2)使用您自己的缓冲区来保留累积的令牌字符串。
选项(2)实际上并不是那么昂贵;如果令牌字符串很大,则可能比使用<<EOF>>
更好,因为flex实际上并不是为处理大型令牌而设计的。对于可能相当大的评论,您可能会发现维护自己的缓冲区会更快,并且可以更加可预测。