Compiler construction 标记化后从Flex返回字符串
如果我使用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 */
%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
不是内联的,则该代码会执行大量额外调用,这会使其速度减慢一点。但是对于快速的黑客来说,它已经足够好了,你可以在以后不断改进它