如果我使用以下代码阅读带有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++;
}
取自here和here。现在Flex只返回yytext
或yytext
中的一个字符,但我的字符串存储在'string_buf_ptr'中。如何在扫描仪中检索它?正如已经指出here,将yytext
修改为当前令牌可能会导致并发症。那么将此字符串返回到仅执行此操作的简单扫描程序是什么:
ntoken = yylex();
while(ntoken) {
prinf("%s\n", yytext);
ntoken = yylex();
}
答案 0 :(得分:1)
这个问题有很多解决方案,但唯一真正安全的是分配一个新的字符串缓冲区(通常带malloc
),填入它,并将其传递给调用者。
如果调用者是一个由bison生成的解析器,它会期望令牌的“语义值”在变量yylval
中,这很可能是一个联合。在传统的flex / bison扫描器/解析器安排中,yylval
将是全球性的。如果你在程序中只有一个扫描仪和一个解析器,那就行得很好 - 事实上,在这个版本中已经构建了大量的语言原型 - 但从现代编程的角度来看它有点难看。
幸运的是,bison
和flex
也进化了,你可以通过告诉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
如果其中任何一种方法不起作用,请随意发表评论。