Compiler construction 标记化后从Flex返回字符串

Compiler construction 标记化后从Flex返回字符串,compiler-construction,bison,flex-lexer,lexical-analysis,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 */

如果我使用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++;
             }

如果将string_buf_ptr声明为全局,则所有代码都可以访问它,而无需进一步修改。可能您希望将其包括在“myglobals.h”中,例如

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

char string_buf_ptr[1024];
一种可能更好的方法是将内存缓冲区传递给flex,而不使用全局变量。您可以使用yyextra(有关详细信息,请参阅)。基本方法如下:

main() {
  ...
  struct mystruct lex_data;
  memset(&lex_data, 0, sizeof(lex_data));
  yylex_init_extra(&lex_data, &yyscanner_pointer);
  ...
  yylex(yyscanner_pointer);
}
创建一个结构,例如

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

如果这两种方法中的任何一种都不起作用,请随意评论。

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

如果调用者是bison生成的解析器,它将期望令牌的“语义值”在变量
yylval
中,这很可能是一个并集。在传统的flex/bison扫描器/解析器安排中,
yylval
将是一个全球性的解决方案。如果程序中只有一个扫描器和一个解析器,那么这就很好了——事实上,大量的语言原型都是以这种方式构建的——但从现代编程的角度来看,这有点难看

幸运的是,
bison
flex
也已经进化,您可以通过告诉
flex
构建可重入的lexer和
bison
构建纯解析器来摆脱globals。此外,您可以为
flex
提供
bison_桥
选项,这将使它为
yylex
创建正确的调用原型,而无需您做更多工作

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

另一个缺点是需要将缓冲区保持在正确的大小。幸运的是,即使你不使用C++字符串,这也是很容易的,这是我通常推荐给初学者的策略。下面是一个非常简单的缓冲区管理策略,除了在某些名称以W开头的平台上,它的效率出人意料:

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将分配调整为指数增长,如果实际有足够的空间,则快速不做任何事情。许多realloc实现就是以这种方式工作的。如果
realloc
不是内联的,则该代码会执行大量额外调用,这会使其速度减慢一点。但是对于快速的黑客来说,它已经足够好了,你可以在以后不断改进它