当前位置:  开发笔记 > 编程语言 > 正文

flex如何准确支持野牛位置?

如何解决《flex如何准确支持野牛位置?》经验,为你挑选了5个好方法。

我正在尝试使用flex和bison来创建一个过滤器,因为我想从复杂的语言中获取某些语法元素.我的计划是使用flex + bison来识别语法,并转出感兴趣的元素的位置.(然后使用脚本根据转储的位置获取文本.)

我发现flex可以支持一个叫做bison-locations的野牛功能,但它是如何工作的.我在flex文档中尝试了这个例子,似乎yylloc不是由flex自动设置的,我总是得到(1,0)-(1,0).可以自动灵活计算每个令牌的位置吗?如果没有,我定义了什么接口函数来实现?有什么例子吗?

有关工具的更好解决方案吗?

最诚挚的问候,凯文

编辑:

现在,yylex的界面转向:

int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param );

bison手册没有说明lexer应如何实现正确设置yylloc_param.对我来说,很难手动跟踪每个令牌的列号.



1> Shlomi Louba..:

yylex声明可能已更改,因为您使用了可重入或纯解析器.似乎网络上的许多文档表明,如果您希望野牛位置有效,则需要它,但这不是必需的.

我也需要行号,并发现Bison文档在这方面令人困惑.简单的解决方案(使用全局var yylloc):在您的Bison文件中添加%locations指令:

%{
...
%}
%locations
...
%%
...

在你的词法分析器中:

%{
...
#include "yourprser.tab.h"  /* This is where it gets the definition for yylloc from */
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%}
%option yylineno
...
%%
...

YY_USER_ACTION宏在每个令牌操作之前被"调用"并更新yylloc.现在您可以像这样使用@N/@ $规则:

statement : error ';'   { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); }

,或使用yylloc全局变量:

void yyerror(char *s)
{
  fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}



2> ekeyser..:

我喜欢Shlomi的回答.

此外,我也在寻找更新列位置.找到了http://oreilly.com/linux/excerpts/9780596155971/error-reporting-recovery.html,在阅读了Shlomi的答案后更有意义.

不幸的是,yylloc的页面上有一个拼写错误.我把它简化了一下.

在你的解析器中添加:

%locations

在你的词法分析器中:

%{

#include "parser.tab.h"

int yycolumn = 1;

#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; \
    yylloc.first_column = yycolumn; yylloc.last_column = yycolumn + yyleng - 1; \
    yycolumn += yyleng; \
    yylval.str = strdup(yytext);

%}

%option yylineno

列位置可能会发生一些事情,它不会严格跟踪列,而只是不断增加.如果它让任何人感到困惑,那只是我的无知和道歉.我目前正在使用列来保存文件字符数,在我的情况下,这比列位置更有利.

希望有所帮助.



3> Brent Royal-..:

bison不会自动flex更新yylloc,但实际上并不难做到 - 如果你知道这个诀窍.

实现yylloc支持的技巧是,即使yyparse()声明yylloc,它也永远不会改变它.这意味着如果您yylloc在对词法分析器的一次调用中进行修改,则在下次调用时会在其中找到相同的值.因此,yylloc将包含最后一个标记的位置.由于最后一个标记的结束与当前标记的开始相同,因此您可以使用旧yylloc值来帮助您确定新值.

换句话说,yylex()不应该计算 yylloc ; 它应该更新 yylloc.

要进行更新yylloc,我们必须首先将last_值复制到first_,然后更新last_值以反映刚刚匹配的令牌的长度.(这不是strlen()令牌;它是行和列的长度.)我们可以在YY_USER_ACTION宏中执行此操作,该宏在执行任何词法分析器操作之前调用; 确保如果规则匹配但不返回值(例如,跳过空格或注释的规则),则跳过该非令牌的位置,而不是包含在实际令牌的开头,或者丢失的方式使位置跟踪不准确.

这是一个针对可重入解析器的版本; 您可以通过交换->运算符来修改非重入解析器.:

#define YY_USER_ACTION \
    yylloc->first_line = yylloc->last_line; \
    yylloc->first_column = yylloc->last_column; \
    for(int i = 0; yytext[i] != '\0'; i++) { \
        if(yytext[i] == '\n') { \
            yylloc->last_line++; \
            yylloc->last_column = 0; \
        } \
        else { \
            yylloc->last_column++; \
        } \
    }

如果您愿意,可以将该代码放在函数中并使宏调用函数,但这两种技术是等效的.



4> Jonathan Lef..:

看一下Bison手册的第3.6节- 它似乎详细地介绍了位置.结合您在Flex手册中找到的内容,这可能就足够了.



5> hugomg..:

如果您只关心保留行号,Shomi的答案是最简单的解决方案.但是,如果您还需要列号,则需要跟踪它们.

一种方法是yycolumn = 1在换行符的每个地方添加规则(如David Elson的回答所示)但是如果你不想跟踪换行符可能出现的所有地方(空白,评论等等)另一种方法是yytext在每个动作开始时检查缓冲区:

static void update_loc(){
  static int curr_line = 1;
  static int curr_col  = 1;

  yylloc.first_line   = curr_line;
  yylloc.first_column = curr_col;

  {char * s; for(s = yytext; *s != '\0'; s++){
    if(*s == '\n'){
      curr_line++;
      curr_col = 1;
    }else{
      curr_col++;
    }
  }}

  yylloc.last_line   = curr_line;
  yylloc.last_column = curr_col-1;
}

#define YY_USER_ACTION update_loc();

最后,需要注意的一点是,一旦您开始手动跟踪列号,您也可以在同一个地方跟踪行号,而不必使用Flex的yylineno选项.

推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有