【问题标题】:Using flex, how can I keep yytext contents when EOF is reached and input is provided via YY_INPUT?使用 flex,当到达 EOF 并通过 YY_INPUT 提供输入时,如何保留 yytext 内容?
【发布时间】:2013-01-03 07:44:24
【问题描述】:

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

这可能是 flex 中的一个错误,但似乎缺少某些东西——flex 扫描器定义在重新定义 YY_INPUT() 时应该提供的其他东西。

我已经在 Ubuntu 12.04.1 和 Windows 7 上使用 flex 2.5.35 进行了测试。在这两个系统上,如果要扫描的文本是通过 @ 的显式定义提供的,则扫描仪会在 EOF 处丢失 yytext[] 内容987654327@.

下面是一个示例 flex 扫描器 (flex-test.l),它旨在读取和打印 HTML cmets,即使最后一条评论未终止。当它的输入是通过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
}

【问题讨论】:

    标签: flex-lexer flex-lexer


    【解决方案1】:

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

    这种行为通常是无害的,因为通常不允许标记跨越两个不同的输入文件,但有时有这个选项会很好。不过,这还不够好,以至于在 flex 存在的 25 年里有人费心去修复它。

    不幸的后果是,在获得EOF 后,您将无法使用yytext,因为即使没有更多输入文件,缓冲区重置也已完成。 (yyleng 也不对;它还没有被重置,并且它也被触发了 EOF 的NUL 增加了。)

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

    如果 flex 得到积极维护,我建议在 flex 手册中添加一条注释,指出 yytextyyleng&lt;&lt;EOF&gt;&gt; 规则中未定义,甚至可以考虑修复 yymore() 以便它允许跨越输入缓冲区(或记录它不跨越的事实)。

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

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

    但你可能会更好:

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

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

    【讨论】:

    • 我已经强烈倾向于解决方案 2。现在您已经说服了我,因为(正如您所说)可能不会很快出现适当的解决方案。感谢您提供有用、知识渊博和有见地的回答。
    猜你喜欢
    • 1970-01-01
    • 2020-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-13
    • 1970-01-01
    • 2015-03-13
    • 2015-08-22
    相关资源
    最近更新 更多