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

时间:2009-03-18 01:49:15

标签: bison flex-lexer

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

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

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

最诚挚的问候, 凯文

修改

现在yylex的界面转到:

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

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

8 个答案:

答案 0 :(得分:17)

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 global var:

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

答案 1 :(得分:12)

我喜欢Shlomi的回答。

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

不幸的是,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

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

希望有所帮助。

答案 2 :(得分:10)

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

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

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

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

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

.

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

答案 3 :(得分:7)

看一下3.6 of the Bison manual部分 - 这似乎涵盖了一些细节。结合您在Flex手册中找到的内容,这可能就足够了。

答案 4 :(得分:6)

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

答案 5 :(得分:4)

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

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

    #define YYLEX_PARAM &yylval, &yylloc
    

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

  2. lexer.l 中,我必须使用->代替. yylloc

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

    [\n] { yycolumn = 1; }
    
  4. 对于\r等,显然有点复杂,但至少我让它起作用。

答案 6 :(得分:1)

我认为我设法让它发挥作用(归功于野牛手册ltcalc lexical analyzer的作者)。 默认情况下,bison会创建包含

的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,我们应该将列字段初始化为零,否则会报告错误的列。

答案 7 :(得分:0)

Shlomi回答的补充:

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

所以,在解析器中:

public Observable<List<Item>> items() {
    List<Observable> obs = new ArrayList<>();

    Observable<TrustedItemList> memoryObs = Observable.defer(this::getMemoryItems);

    Observable<TrustedItemList> diskObs = Observable.defer(this::getDiskItems);

    Observable<TrustedItemList> networkObs = Observable.defer(this::getNetworkItems);

    Observable<TrustedItemList> concat = Observable.concat(memoryObs, diskObs, networkObs;


    return concat.takeWhile(this::shouldContinueSearching)
                 .filter(trustedItemList -> trustedItemList.items != null && !trustedItemList.items.isEmpty())
                 .map(trustedItemList -> trustedItemList.items);
}

private boolean shouldContinueSearching(TrustedPoiList data) {
     return data == null || data.items == null || data.items.isEmpty() || !data.canTrustIfNotEmpty; 
}
词法分析器

%define api.pure
%locations
相关问题