重复词法分析器,错误分段错误

时间:2016-09-29 12:41:50

标签: segmentation-fault bison interpreter flex-lexer lex

我正在翻译,但我遇到了一些问题。

在我的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

1 个答案:

答案 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的解析器生成器一起工作,它总是读取前瞻标记。)并且这可能最终如何使用嵌套块并不明显,您可能希望在某些时候实现它们