1

によって生成されたスキャナーがファイルの終わりに遭遇すると、以前のルールの呼び出しyytext[]によってそこに残された内容が失われます。yymore()この誤った動作は、YY_INPUT()が再定義された場合に発生します。

これはフレックスのバグかもしれませんが、何かが欠けている可能性があります-フレックススキャナー定義が再定義するときに提供する必要がある何かYY_INPUT()

Ubuntu12.04.1とWindows7の両方でflex2.5.35を使用してテストしました。どちらのシステムでも、yytext[]スキャンするテキストがの明示的な定義を介して提供されている場合、スキャナーはEOFでコンテンツを失いますYY_INPUT()

flex-test.l以下は、最後のコメントが終了していない場合でも、HTMLコメントを読み取って印刷することを目的としたサンプルフレックススキャナー( )です。入力がを介して提供される場合は正しく機能し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()ます。(実際には、最初の2文字をNULsで初期化しますが、それを破棄するには十分です。)次にyywrap()、を呼び出します。これには、別のバッファー(ファイル)を提供するオプションがあります。

通常、トークンは2つの異なる入力ファイルにまたがることが許可されていないため、この動作は通常無害ですが、オプションがあると便利な場合があります。しかし、フレックスの存在の四半世紀に誰もがそれを修正するのに苦労したほど良くはありません。

残念なことに、入力ファイルがなくなってもバッファのリセットはすでに行われているため、yytextを取得した後は使用できなくなります。EOFyylengこれも正しくありません。まだリセットされておらずNUL、EOFをトリガーしたものでインクリメントされています。)

yy_scan_string実装には、新しく作成されたバッファのフラグを0に設定するハックがあります。これはyy_fill_buffer、バッファを補充しようとしないことを意味します。これにより、バッファのリセットは防止されますが、保護されませんyyleng。これはまだ正しくありません。yytextでも、純粋な運の維持を考えたいと思います。

フレックスが積極的に維持されている場合は、フレックスマニュアルに、ルールで定義されていないyytextコメントを追加することをお勧めします。また、入力バッファーにまたがることができるように修正することを検討することもできます(またはそうでないことを文書化することもできます)。 。yyleng<<EOF>>yymore()

つまり、2つのオプションがあります。

1)yy_scan_stringまたは(すべての適切な警告を含む)を使用し、ルールyy_scan_bufferで確認できるハックを誰も元に戻さないことを期待します。その希望がどれほど将来にわたって利用できるかはわかりませんが、アップグレードを強制するものは何もありません。yytext<<EOF>>

しかし、あなたはおそらく次の方が良いでしょう:

2)独自のバッファを使用して、蓄積されたトークン文字列を保持します。

オプション(2)は、実際にはそれほど高価ではありません。yymoreトークン文字列が大きい場合、 flexは実際には大きなトークンを処理するように設計されていないため、使用するよりもおそらく優れています。コメントはかなり大きくなる可能性がありますが、独自のバッファを維持する方がはるかに高速であり、予測可能性もはるかに高いことがわかるでしょう。

于 2013-01-20T05:35:32.207 に答える