重复词法分析器,错误分段错误
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_buffer
; yy_scan_string
会自动执行此操作。此外,为了切换回 yyin
,您需要有一个 <<EOF>>
规则来检测文件结束指示(或缓冲区结束,在这种情况下)并切换回 yyin
。为了确保保留原始缓冲区,您需要将 YY_CURRENT_BUFFER
保存在某个临时变量中并在该临时变量上调用 yy_switch_to_buffer
;如果您从 yyin
创建一个新缓冲区,您将丢失任何缓冲输入。
管理输入缓冲区堆栈的一种更简单的方法是使用缓冲区堆栈;然后您可以调用 yypush_buffer_state
开始扫描新缓冲区,并在您的 <<EOF>>
规则中调用 yypop_buffer_state
。然而,yy_scan_string
和 yypush_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 的解析器生成器一起工作,这些生成器总是读取先行标记。)并且这可能最终如何与嵌套块一起工作并不明显,您可能希望在某个时候实现它.
我正在做一个解释器,但我遇到了一些问题。
在我的 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_buffer
; yy_scan_string
会自动执行此操作。此外,为了切换回 yyin
,您需要有一个 <<EOF>>
规则来检测文件结束指示(或缓冲区结束,在这种情况下)并切换回 yyin
。为了确保保留原始缓冲区,您需要将 YY_CURRENT_BUFFER
保存在某个临时变量中并在该临时变量上调用 yy_switch_to_buffer
;如果您从 yyin
创建一个新缓冲区,您将丢失任何缓冲输入。
管理输入缓冲区堆栈的一种更简单的方法是使用缓冲区堆栈;然后您可以调用 yypush_buffer_state
开始扫描新缓冲区,并在您的 <<EOF>>
规则中调用 yypop_buffer_state
。然而,yy_scan_string
和 yypush_buffer_state
之间有一个奇怪的交互,它要求您首先推送当前缓冲区的副本,然后用 yy_scan_string
创建的缓冲区状态替换它。请参阅
在不查看更多代码的情况下,很难知道段错误从何而来。这可能是您的 <<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 的解析器生成器一起工作,这些生成器总是读取先行标记。)并且这可能最终如何与嵌套块一起工作并不明显,您可能希望在某个时候实现它.