yyllocp->first_行在可重入Bison解析器的第二次迭代中返回未初始化的值
我有一个可重入解析器,它从字符串中获取输入,并有一个维护上下文的结构。 使用要分析的不同输入字符串调用函数。该功能的相关代码为:yyllocp->first_行在可重入Bison解析器的第二次迭代中返回未初始化的值,bison,flex-lexer,yacc,lex,Bison,Flex Lexer,Yacc,Lex,我有一个可重入解析器,它从字符串中获取输入,并有一个维护上下文的结构。 使用要分析的不同输入字符串调用函数。该功能的相关代码为: void parseMyString(inputToBeParsed) { //LEXICAL COMPONENT - INITIATE LEX PROCESSING yyscan_t scanner; YY_BUFFER_STATE buffer; yylex_init_extra(&parseSupportStruct, &
void parseMyString(inputToBeParsed) {
//LEXICAL COMPONENT - INITIATE LEX PROCESSING
yyscan_t scanner;
YY_BUFFER_STATE buffer;
yylex_init_extra(&parseSupportStruct, &scanner );
//yylex_init(&scanner);
buffer = yy_scan_buffer(inputToBeParsed, i+2, scanner);
if (buffer == NULL) {
strcpy(errorStrings,"YY_BUFFER_STATE returned NULL pointer\n");
return (-1);
}
//BISON PART - THE ACTUAL PARSER
yyparse(scanner, &parseSupportStruct);
...
yylex_destroy(scanner);
...
}
My.l选项包括:
%option noinput nounput noyywrap 8bit nodefault
%option yylineno
%option reentrant bison-bridge bison-locations
%option extra-type="parseSupportStructType *"
.y中的相关行包括:
%define api.pure full
%locations
%param { yyscan_t scanner }
%parse-param { parseSupportStructType* parseSupportStruct}
%code {
int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, yyscan_t scanner);
void yyerror(YYLTYPE* yyllocp, yyscan_t unused, parseSupportStructType* parseSupportStruct, const char* msg);
char *yyget_text (yyscan_t);
char *strcpy(char *, const char *);
}
%union {
int numval;
char *strval;
double floatval;
}
在我的解析器中,在某些规则中,我尝试访问yyllocp->first\u行。
在第一次调用parseMyString…,我得到了正确的值。
第二次,我得到一些未初始化的值。我是否需要在每次调用parseMyString时初始化yyllocp->第一行?如何和在哪里?
我知道我已经给出了部分修改过的代码来解释这种情况。我们很乐意提供更多细节
使用valgrind,我已尽我所能消除了内存泄漏,但一些第三方库问题超出了我的控制范围 flex或bison中的任何内容都不会保持yylloc的值 Bison解析器(而不是push解析器)将初始化该变量。如果您接受默认的位置类型-也就是说,您没有定义YYLTYPE-yylloc将被初始化为{1,1,1,1}。否则,它将被初始化为零,不管它是什么类型。Bison还生成代码,根据非终端的第一个和最后一个子节点的位置计算非终端的位置。Flex生成的代码根本不涉及location对象 如果您要求启用此功能,flex扫描仪会自动维护yylineno
%option yylineno
Flex通常可以比您更有效地完成这项工作,它可以处理所有的角落案例yyless、yymore、input和REJECT。所以,如果你想追踪行号,我强烈建议让flex来做
但是flex的yylineno支持有一个重要的问题。在可重入扫描仪中,行号存储在每个flex缓冲区中,而不是扫描仪状态对象中。IMHO,这几乎肯定是存储它的正确位置,因为如果您使用多个缓冲区,它们可能表示多个输入流,通常您需要引用其文件中的行号。但yy_scan_buffer不会初始化此字段。因此,yy_scan_字符串和yy_scan_字节也是如此,它们只是围绕yy_scan_缓冲区的包装
因此,如果您正在使用其中一个yy_scan_*接口,您应该在yy_scan_*之后立即调用yyset_lineno来重置yylineno。在您的情况下,这将是:
buffer = yy_scan_buffer(inputToBeParsed, i+2, scanner);
yyset_lineno(1, scanner);
一旦获得了yylineno,就很容易维护yylloc对象。Flex有一个钩子,允许您在执行模式的任何操作之前插入代码,即使该操作为空,该钩子可用于自动维护yylloc。在中,我提供了此技术的一个简单示例,它依赖于flex生成的扫描仪维护yylineno:
#define YY_USER_ACTION \
yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \
if (yylloc->last_line == yylineno) \
yylloc->last_column += yyleng; \
else { \
yylloc->last_line = yylineno; \
yylloc->last_column = yytext + yyleng - strrchr(yytext, '\n'); \
}
正如该答案中的注释所示,上述内容并不全面,但在许多情况下都适用:
此YY_用户_动作宏应适用于任何不使用yyless、yymore、input或REJECT的扫描仪。正确处理这些特性并不太困难,但在这里似乎超出了范围
您无法在操作之前处理yyless、yymore或REJECT,因为在操作之前不可能知道它们是否将被执行,因此在使用这些功能的应用程序中,更健壮的位置跟踪器必须包含修复yylloc的代码:
对于yyless,由于flex扫描仪将修复yyleng和yylineno,因此在yyless调用后,可以重新执行上述设置last_line和last_column的代码
对于REJECT,不能在REJECT之后插入代码。处理它的唯一方法是保留yylloc的备份,并在REJECT宏之前立即将其还原。我强烈建议不要使用拒绝。它的效率极低,几乎总是可以用调用yyless和启动条件的组合来代替
对于yymore,yylloc仍然正确,但下一个操作不得覆盖令牌开始位置。要做到这一点,可能需要维护一个标志来指示是否调用了yymore
对于输入,如果希望读取的字符被视为当前标记的一部分,则可以在调用input后在yylloc中提前结束位置,这需要区分返回换行符、文件结尾指示符或常规字符的输入。或者,如果希望使用输入读取的字符不被视为任何令牌的一部分,则需要放弃使用前一令牌的结束位置作为当前令牌的开始位置的想法,这将需要保留一个分离位置值作为下一令牌的开始位置
我的大部分bison/flex代码都基于您的答案!最后我自己维护了line count,因为我的问题是yylineno在第二次调用函数时以一些未初始化的值开始。第一次迭代
阿西尼克斯:我把我先前的评论整合到了答案中,并添加了更多细节。让我知道这对你是否有意义。我想我一开始不理解你的问题,因为你问的是yylloc,不是yylineno,但是当你提到yylineno没有初始化时,我记得yy_扫描缓冲区没有初始化的问题。很抱歉,一开始就没有更多的关注。我通常不会遇到这个问题,因为我几乎总是使用推式解析器,这需要一种不同的方法来处理位置对象。我尝试过,但在没有缓冲区的情况下调用了错误yyset_lineno。我必须承认,我让我的解析器工作,因为我盲目地接受了你的许多建议,它们只是工作!!我有一个功能性的、可重入的、基于缓冲区的解析器,可以很好地满足您的所有建议。否则,在bison和flex中编写解析器将是一项艰巨的工作@阿西尼克斯:我到家后会再检查一遍。yy_scan_buffer应该已经安装了一个缓冲区,除非它返回NULL。@asinix:所以错误消息实际上是正确的:-很高兴你让它工作了。