1

生成的扫描器遇到文件结尾时,它会丢失之前规则中调用yytext[]留下的内容。yymore()这种错误行为只有在YY_INPUT()重新定义时才会发生。

这可能是 flex 中的一个错误,但似乎缺少一些东西——flex 扫描器定义在重新定义YY_INPUT().

我已经在 Ubuntu 12.04.1 和 Windows 7 上使用 flex 2.5.35 进行了测试。在这两个系统上,yytext[]如果要扫描的文本是通过YY_INPUT().

下面是一个flex-test.l用于读取和打印 HTML 注释的示例 flex 扫描器 ( ),即使最后一条注释未终止。当它的输入是通过 提供时它可以正常工作yy_scan_string(),但是当它的输入是由 的显式定义提供时它会失败YY_INPUT()。在示例代码中,#if's 用于在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
}
4

1 回答 1

3

从那时起,这个问题可能一直存在,可能永远存在。基本上,当 flex 从其当前缓冲区中获取 EOF 时,它会处理最后一个令牌,然后重新初始化缓冲区,这实际上将当前令牌扔掉,即使它已经用yymore(). (它实际上用 s 初始化前两个字符NUL,但这足以破坏它。)然后它调用yywrap(),它可以选择提供另一个缓冲区(文件)。

这种行为通常是无害的,因为通常不允许令牌跨越两个不同的输入文件,但有时有这个选项会很好。不过,这还不够好,以至于任何人都费心在 flex 存在的四分之一世纪中修复它。

yytext不幸的后果是你得到一个 之后就不能使用了EOF,因为即使没有更多的输入文件,缓冲区重置已经完成了。(yyleng也不对;它还没有被重置,并且它也随着NUL触发 EOF 的 增加。)

在实现中存在一个 hack,yy_scan_string它将新创建的缓冲区的yy_fill_buffer标志设置为 0,这意味着不应尝试重新填充缓冲区。这可以防止缓冲区重置,但不能保护yyleng,这仍然是不正确的。不过,我会考虑保留yytext纯粹的运气。

如果 flex 正在被积极维护,我建议在 flex 手册中添加一条在规则中未定义的注释,yytext甚至可能考虑修复它以允许跨越输入缓冲区(或记录它没有的事实) .yyleng<<EOF>>yymore()

简而言之,您有两种选择:

1)只需使用yy_scan_stringyy_scan_buffer(带有所有适当的警告),并希望没有人恢复允许您查看规则yytext的hack。<<EOF>>我不知道这种希望是如何面向未来的,但没有什么能迫使你升级。

但你可能会更好:

2)使用自己的缓冲区来保存累积的令牌字符串。

选项(2)实际上并不那么昂贵;如果标记字符串很大,它可能比使用更好,yymore因为 flex 确实不是为处理大型标记而设计的。对于可能相当大的注释,您可能会发现维护自己的缓冲区会快得多,而且更可预测。

于 2013-01-20T05:35:32.207 回答