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

Repetitive lexical analyser, error segmentation fault

我正在做一个解释器,但我遇到了一些问题。

在我的 lex 中:

<INITIAL>\{                 {BEGIN(BLOC);}
<BLOC>[^}]*\}               {BEGIN(INITIAL);strncpy(yylval.sval, yytext, MAXVARSIZE);
                            temp = strlen(yylval.sval);
                            yylval.sval[temp-1] = '[=10=]';
                            return BLOCK;}

lex 在 {} 之间返回了一个块,在我的 bison 解析器中,我设置了 flex 缓冲区:

ifs:
    IF PAREOPEN condition PARECLOSE BLOCK {if( > 0){scan_string();}}

;
[...]

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));

}

但是 bison 后来产生了分段错误。我想将缓冲区恢复到原来的 yyin

我认为这种方法行不通。见下文。

你不应该打电话给 yy_switch_to_bufferyy_scan_string 会自动执行此操作。此外,为了切换回 yyin,您需要有一个 <<EOF>> 规则来检测文件结束指示(或缓冲区结束,在这种情况下)并切换回 yyin。为了确保保留原始缓冲区,您需要将 YY_CURRENT_BUFFER 保存在某个临时变量中并在该临时变量上调用 yy_switch_to_buffer ;如果您从 yyin 创建一个新缓冲区,您将丢失任何缓冲输入。

管理输入缓冲区堆栈的一种更简单的方法是使用缓冲区堆栈;然后您可以调用 yypush_buffer_state 开始扫描新缓冲区,并在您的 <<EOF>> 规则中调用 yypop_buffer_state。然而,yy_scan_stringyypush_buffer_state 之间有一个奇怪的交互,它要求您首先推送当前缓冲区的副本,然后用 yy_scan_string 创建的缓冲区状态替换它。请参阅 for an example. (You might want to read the relevant section of the Flex manual,它有一个完整的示例,尽管它用于嵌套文件,而不是字符串。)

在不查看更多代码的情况下,很难知道段错误从何而来。这可能是您的 <<EOF>> 处理程序中的错误,您没有显示。它也可能与 yylval.sval 的处理有关;如果这是一个指向缓冲区的指针(即 char*),那么它显然没有在任何地方初始化,当你 strncpy 进入它时可能会产生错误。

但在我看来,您最有可能将固定长度的字符数组作为语义值联合的一部分。出于多种原因,这是一个非常糟糕的主意,其中最重要的是它在解析器堆栈中浪费了很多 space :每个条目都将包含固定长度的缓冲区。此外,固定长度的缓冲区从来都不是一个好主意。你可以很容易地溢出它们。

在这种情况下,它根本无法工作,因为该数组将成为 bison 解析器堆栈条目的一部分,并且一旦 ifs 操作终止,该条目就会从堆栈中删除。这将使 Flex 的缓冲区留下悬空指针,这几乎肯定会产生问题。

因此您可以尝试以下操作:

  • 将union中的sval成员改为char*(这需要你的代码做各种改动)

  • 用类似这样的东西替换 flex 文件中的 <BLOC> 模式;:

    <INITIAL>\{         { BEGIN(BLOC); }
    <BLOC>[^}]*\}       { BEGIN(INITIAL);
                          yylval.sval = malloc(yyleng);
                          memcpy(yylval.sval, yytext, yyleng - 1);
                          yylval.sval[yyleng - 1] = '[=10=]';
                          return BLOCK;
                        }
    
  • 更改 bison 解析器中的 ifs 操作,使其 free 成为动态分配的字符串缓冲区(它可以这样做,因为 yy_scan_string 制作了一个副本).

  • 添加 <<EOF>> 规则并修改 scan_string 以使用缓冲区堆栈,如上。

尽管如此,我认为这不是一个很好的策略。在 bison 操作过程中更改 flex 缓冲区只有在解析器没有读取先行标记的情况下才会起作用,这使得它非常脆弱。 (并且它根本无法与其他类似 yacc 的解析器生成器一起工作,这些生成器总是读取先行标记。)并且这可能最终如何与嵌套块一起工作并不明显,您可能希望在某个时候实现它.