我正在翻译,但我遇到了一些问题。
在我的lex中:
<INITIAL>\{ {BEGIN(BLOC);}
<BLOC>[^}]*\} {BEGIN(INITIAL);strncpy(yylval.sval, yytext, MAXVARSIZE);
temp = strlen(yylval.sval);
yylval.sval[temp-1] = '\0';
return BLOCK;}
lex在{}
之间返回了一个块,在我的野牛解析器中,我设置了flex缓冲区:
ifs:
IF PAREOPEN condition PARECLOSE BLOCK {if($3 > 0){scan_string($5);}}
;
[...]
void scan_string(const char* str)
{
yy_switch_to_buffer(yy_scan_string(str));
}
int main(int argc, char *argv[]) {
yyin = stdin;
do {
printf("aqui2\n");
yyparse();
} while(!feof(yyin));
}
但是后来的野牛产生了一个分段错误。我想将缓冲区恢复为原来的yyin
。
答案 0 :(得分:1)
我不认为这种方法会奏效。见下文。
你不应该致电yy_switch_to_buffer
; yy_scan_string
会自动执行此操作。此外,为了切换回yyin
,您需要有一个<<EOF>>
规则来检测文件结束指示(或缓冲区结束,在本例中)并切换回{{ 1}}。为了确保保留原始缓冲区,您需要将yyin
保留在某个临时变量中,并在该临时变量上调用YY_CURRENT_BUFFER
;如果你从yy_switch_to_buffer
创建一个新缓冲区,你将失去任何缓冲输入。
管理输入缓冲区堆栈的一种更简单的方法是使用缓冲区堆栈;然后,您可以致电yyin
开始扫描新缓冲区,并yypush_buffer_state
规则中yypop_buffer_state
。但是,<<EOF>>
和yy_scan_string
之间存在奇怪的交互,这要求您首先推送当前缓冲区的副本,然后将其替换为yypush_buffer_state
创建的缓冲区状态。有关示例,请参阅this answer。 (你可能想要read the relevant section of the Flex manual,它有一个完整的例子,虽然它用于嵌套文件,而不是字符串。)
如果没有看到更多代码,很难知道segfault的来源。它可能是您yy_scan_string
处理程序中的错误,您没有显示该错误。它也可能与<<EOF>>
的处理有关;如果那是指向缓冲区的指针(即yylval.sval
),那么它显然没有在任何地方初始化,这可能会在你char*
进入时产生错误。
但在我看来,你很可能已经将一个固定长度的字符数组作为语义值联合的一部分。出于多种原因,这是一个非常糟糕的主意,其中最重要的是它在解析器堆栈中浪费了大量空间:每个条目都包含固定长度的缓冲区。而且,固定长度的缓冲区绝不是一个好主意;你可以轻易地将它们溢出来。
在这种情况下,它根本无法工作,因为该数组将成为bison解析器堆栈条目的一部分,并且只要strncpy
操作终止,该条目就会从堆栈中删除。这将使Flex的缓冲区带有悬空指针,这几乎肯定会产生问题。
所以你可以尝试以下方法:
将联盟中的ifs
成员更改为sval
(这需要对代码进行各种更改)
用以下内容替换flex文件中的char*
模式;:
<BLOC>
更改您的bison解析器中的<INITIAL>\{ { BEGIN(BLOC); }
<BLOC>[^}]*\} { BEGIN(INITIAL);
yylval.sval = malloc(yyleng);
memcpy(yylval.sval, yytext, yyleng - 1);
yylval.sval[yyleng - 1] = '\0';
return BLOCK;
}
操作,使其ifs
成为动态分配的字符串缓冲区(它可以执行此操作,因为free
进行复制)。
添加yy_scan_string
规则并修改<<EOF>>
以使用缓冲区堆栈,如上所述。
即便如此,我也不认为这是一个非常好的策略。只有在解析器没有读取前瞻标记时,才能在bison动作中更改flex缓冲区,这使得它非常脆弱。 (并且它不会与其他类似yacc的解析器生成器一起工作,它总是读取前瞻标记。)并且这可能最终如何使用嵌套块并不明显,您可能希望在某些时候实现它们