首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >yyllocp->第一行在reEntrant Bison解析器的第二次迭代中返回未初始化的值

yyllocp->第一行在reEntrant Bison解析器的第二次迭代中返回未初始化的值
EN

Stack Overflow用户
提问于 2020-01-15 15:10:19
回答 1查看 249关注 0票数 0

我有一个reEntrant解析器,它从字符串中获取输入,并具有维护上下文的结构。调用一个函数时,要解析不同的输入字符串。该职能的相关代码是:

代码语言:javascript
代码运行次数:0
运行
复制
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);
...
}

我的.l选项是:

代码语言:javascript
代码运行次数:0
运行
复制
 %option noinput nounput noyywrap 8bit nodefault                                 
 %option yylineno
 %option reentrant bison-bridge bison-locations                                  
 %option extra-type="parseSupportStructType *"

来自.y的相关行是:

代码语言:javascript
代码运行次数:0
运行
复制
  %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->_line。在第一次调用parseMyString(.)时,我得到了正确的值。第二次,我得到一些未初始化的值。是否需要在每次调用parseMyString时初始化yyllocp->第一行?我知道我已经给出了部分的、经过修改的代码来解释这种情况。很乐意提供更多的细节。

使用val差制,我已经尽可能地删除了内存泄漏,但是一些第三方库问题超出了我的控制范围。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-01-15 20:12:31

flex或bison中没有任何东西能够维护yylloc的价值。

Bison解析器(推送分析器除外)将初始化该变量。(如果您接受默认的位置类型--也就是说,您不接受#define YYLTYPE -- yylloc将初始化为{1, 1, 1, 1}。否则,无论它是什么类型,它都将是零初始化的。)Bison还生成代码,根据非终端的第一个和最后一个子节点的位置计算非终端的位置。Flex生成的代码根本不涉及location对象。

如果要求启用此功能,则flex扫描器会自动维护yylineno

代码语言:javascript
代码运行次数:0
运行
复制
%option yylineno

Flex通常可以比您更有效地完成这个任务,并且它处理所有的拐角处情况(yylessyymoreinput()REJECT)。因此,如果您想跟踪线路号码,我强烈建议让flex做它。

但是,flex的yylineno支持有一个重要的问题。在可重入扫描器中,行号存储在每个flex缓冲区中,而不是存储在扫描器状态对象中。这几乎肯定是存储它的正确位置,IMHO,因为如果您使用多个缓冲区,它们可能代表多个输入流,通常您会想要引用它的文件中的一行数。但是yy_scan_buffer并没有初始化这个字段。(因此,yy_scan_stringyy_scan_bytes也是如此,它们只是yy_scan_buffer的包装器。)

因此,如果您使用的是yy_scan_*接口之一,则应该在yy_scan_*之后立即调用yyset_lineno来重置yy_scan_*。就你而言,这将是:

代码语言:javascript
代码运行次数:0
运行
复制
buffer = yy_scan_buffer(inputToBeParsed, i+2, scanner);
yyset_lineno(1, scanner);

一旦获得了yylineno,就很容易维护yylloc对象。Flex有一个钩子,它允许您在执行模式的任何操作之前注入代码(即使操作是空的),并且可以使用这个钩子自动维护yylloc。在this answer中,我提供了这个技术的一个简单示例(它依赖于由flex生成的扫描器维护的yylineno ):

代码语言:javascript
代码运行次数:0
运行
复制
#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_USER_ACTION宏应该适用于任何不使用yyless()yymore()input()REJECT的扫描器。正确地处理这些特性并不太困难,但它似乎超出了这里的范围。

您无法在操作之前处理yyless()yymore()REJECT (因为在操作之前不可能知道它们是否会被执行),因此,在使用这些功能的应用程序中,更健壮的位置跟踪器必须包括修复yylloc()的代码。

yylineno.

  • For
  • For yyless(),上述设置last_linelast_column的代码可以在yyless()调用后重新执行,因为flex扫描器将修复yylengyyleng REJECT,因此无法在REJECT之后插入代码。处理它的唯一方法是保留yylloc的备份,并在REJECT宏之前立即恢复它。(我强烈建议不要使用REJECT。它效率极低,几乎总是可以用调用yyless()和start yyless() yymore()的组合来代替,yylloc仍然是正确的,但是下一个操作不能覆盖令牌启动位置。正确的操作可能需要维护一个标志来指示是否调用了yymore()
  • For input(),如果您希望将读取的字符视为当前令牌的一部分,则可以在调用input() (这需要区分返回换行符、文件结束指示符或常规字符)之后,在yylloc中推进结束位置。或者,如果您希望使用input()读取的字符不被视为任何令牌的一部分,则需要放弃使用前一个令牌的结束位置作为当前令牌的起始位置的想法,这将需要保留一个分隔位置值作为下一个令牌的开始位置。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59754253

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档