23

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

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

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

最好的问候,凯文

编辑:

现在yylex的界面变成了:

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

野牛手册未指定词法分析器应如何实现以正确设置 yylloc_param。对我来说,很难手动跟踪每个令牌的列号。

4

8 回答 8

22

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

我也需要行号,发现 Bison 文档在这方面令人困惑。简单的解决方案(使用全局变量 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);
}
于 2011-04-27T22:53:05.500 回答
19

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++; \
        } \
    }

如果您愿意,您可以改为将该代码放在一个函数中并让宏调用该函数,但这两种技术是等效的。

于 2014-03-02T08:36:41.547 回答
15

我喜欢 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

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

希望有帮助。

于 2011-11-06T02:30:50.947 回答
7

看看Bison 手册的第 3.6节- 这似乎涵盖了一些详细的位置。结合您在 Flex 手册中找到的内容,这可能就足够了。

于 2009-03-18T02:50:45.507 回答
7

如果您只关心保留行号,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选项而烦恼。

于 2013-10-03T00:58:19.287 回答
4

所以,我让这个“工作”,但有几个额外的步骤(我可能在这里忽略了它们......在这种情况下道歉):

  1. parser.y中,我不得不说:

    #define YYLEX_PARAM &yylval, &yylloc
    

    即使使用%locationsand bison --locations,也可以让它传递数据。

  2. lexer.l我不得不使用->而不是.foryylloc

  3. 同样在lexer.l中,我重置了操作中的列:

    [\n] { yycolumn = 1; }
    

显然有点复杂,\r等等,但至少我让它工作了。

于 2012-06-01T22:03:51.073 回答
2

对 Shlomi 的回答的补充:

如果在 bison 中使用 %define api.pure 创建可重入解析器,还需要在 flex 中指定 %option bison-locations。这是因为在可重入解析器中 yylloc 不是全局变量,需要传递给词法分析器。

因此,在解析器中:

%define api.pure
%locations

在词法分析器中:

#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno
于 2015-11-09T16:27:17.710 回答
1

我想我设法使它工作(归功于野牛手册ltcalc 词法分析器的作者)。默认情况下,野牛创建 yylloc 包含

{ first_line, first_column , last_line , last_column }

我们只需要在词法分析器中更新这些值。前任 :

[ \t]     { ++yylloc.last_column; }
[\n]      { yyloc.last_column = 0; return EOL; }
[a-zA-Z]+ { 
            yylloc.last_column += strlen(yytext);
            return IDENTIFIER;
          }

现在在野牛中,检索这些字段:

statement : IDENTIFIER '=' expression 
            { printf("%d - %d\n", @1.last_line, @1.last_column); }

默认情况下这些字段初始化为1,我们应该将列字段初始化为零,否则会报错列。

于 2015-08-13T14:57:29.350 回答