标记化后从Flex返回字符串

时间:2014-09-15 10:03:18

标签: compiler-construction bison flex-lexer lexical-analysis

如果我使用以下代码阅读带有Flex的字符串:

     %x str

     %%
             char string_buf[MAX_STR_CONST];
             char *string_buf_ptr;


     \"      string_buf_ptr = string_buf; BEGIN(str);

     <str>\"        { /* saw closing quote - all done */
             BEGIN(INITIAL);
             *string_buf_ptr = '\0';
             /* return string constant token type and
              * value to parser
              */
             }

     <str>\n        {
             /* error - unterminated string constant */
             /* generate error message */
             }

     <str>\\[0-7]{1,3} {
             /* octal escape sequence */
             int result;

             (void) sscanf( yytext + 1, "%o", &result );

             if ( result > 0xff )
                     /* error, constant is out-of-bounds */

             *string_buf_ptr++ = result;
             }

     <str>\\[0-9]+ {
             /* generate error - bad escape sequence; something
              * like '\48' or '\0777777'
              */
             }

     <str>\\n  *string_buf_ptr++ = '\n';
     <str>\\t  *string_buf_ptr++ = '\t';
     <str>\\r  *string_buf_ptr++ = '\r';
     <str>\\b  *string_buf_ptr++ = '\b';
     <str>\\f  *string_buf_ptr++ = '\f';

     <str>\\(.|\n)  *string_buf_ptr++ = yytext[1];

     <str>[^\\\n\"]+        {
             char *yptr = yytext;

             while ( *yptr )
                     *string_buf_ptr++ = *yptr++;
             }

取自herehere。现在Flex只返回yytextyytext中的一个字符,但我的字符串存储在'string_buf_ptr'中。如何在扫描仪中检索它?正如已经指出here,将yytext修改为当前令牌可能会导致并发症。那么将此字符串返回到仅执行此操作的简单扫描程序是什么:

ntoken = yylex();

while(ntoken) {
   prinf("%s\n", yytext);
   ntoken = yylex();
}

2 个答案:

答案 0 :(得分:1)

这个问题有很多解决方案,但唯一真正安全的是分配一个新的字符串缓冲区(通常带malloc),填入它,并将其传递给调用者。

如果调用者是一个由bison生成的解析器,它会期望令牌的“语义值”在变量yylval中,这很可能是一个联合。在传统的flex / bison扫描器/解析器安排中,yylval将是全球性的。如果你在程序中只有一个扫描仪和一个解析器,那就行得很好 - 事实上,在这个版本中已经构建了大量的语言原型 - 但从现代编程的角度来看它有点难看。

幸运的是,bisonflex也进化了,你可以通过告诉flex建立一个可重入的词法分析器和bison来构建一个纯粹的解析器来摆脱全局变量。此外,您可以向bison_bridge提供flex选项,这样就可以为yylex创建正确的调用原型,而无需您做更多的工作。

每次分配新字符串缓冲区的一个缺点是您需要在解析器中释放它。但好处是,在下次调用yylex时,返回的字符串缓冲区不会被覆盖,这样就可以与bison一起使用。 (bison和许多其他解析器生成器假设可以提前读取一个(或多个)标记。)在这种情况下,您不能依赖词法分析器的任何静态状态,因为到{{ 1}}减少发生,词法分析器已经被再次调用并丢弃了之前的状态。

另一个缺点是您需要将缓冲区维持在正确的大小。幸运的是,即使您没有使用C ++字符串也很容易,这是我通常向初学者推荐的策略。这是一个非常简单的缓冲管理策略,除了在名称以W开头的某些平台上之外,其效率令人惊讶:

bison

依赖于realloc将分配调整为指数级增长并且如果实际上有足够的空间则快速无效。许多size_t bf = 0; char* bfsz = 0; #define PUTCHAR(ch) do { \ char* newbf = realloc(bf, ++bfsz); \ if (!newbf) { \ fputs("Out of memory!\n", stderr); \ exit(2); \ } \ bf = newbuf; \ bf[bfsz] = ch; \ bf[bfsz+1] = 0; \ } while(0) 实现只是以这种方式工作。如果realloc没有内联,那么该代码会执行大量额外调用,这会使其慢下来。但它对于快速黑客来说已经足够好了,而且你以后总能改进它。

答案 1 :(得分:0)

如果将string_buf_ptr声明为全局,则无需进一步修改即可访问所有代码。可能你想要包括它,例如在“myglobals.h”中就像

一样
extern char *string_buf_ptr;

并在flex文件中包含该头文件(以及代码需要访问string_buf_ptr的任何其他代码文件)。然后,在调用main()之前,例如声明它。

char string_buf_ptr[1024];

一种可能更好的方法是将内存缓冲区传递给flex而不使用全局变量。您可以使用yyextra执行此操作(有关详细信息,请参阅the Flex manual)。基本方法是:

创建

等结构
struct mystruct {
  char string_buf_ptr[1024]; /* or you can malloc this before calling flex */
};

然后在你打电话给yylex之前,你可以这样做:

main() {
  ...
  struct mystruct lex_data;
  memset(&lex_data, 0, sizeof(lex_data));
  yylex_init_extra(&lex_data, &yyscanner_pointer);
  ...
  yylex(yyscanner_pointer);
}

然后,在你的lex代码中,改变对string_buf_ptr的引用,而不是指向

((struct mystruct *)yyextra)->string_buf_ptr

如果其中任何一种方法不起作用,请随意发表评论。