Bison flex如何准确地支持野牛定位?

Bison flex如何准确地支持野牛定位?,bison,flex-lexer,Bison,Flex Lexer,我正试图使用flex和bison创建一个过滤器,因为我想从复杂的语言中获取某些语法元素。我的计划是使用flex+bison来识别语法,并转储感兴趣元素的位置。(然后使用脚本根据转储的位置抓取文本。) 我发现flex可以支持一个名为bison locations的bison特性,但它在实际应用中是如何工作的。我在flex文档中尝试了这个例子,似乎yylloc不是由flex自动设置的,我总是得到(1,0)-(1,0)。flex能否自动计算每个令牌的位置?如果没有,定义了什么接口函数供我实现?有什么例

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

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

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

致以最良好的祝愿, 凯文

编辑:

现在,yylex的接口转向:

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

bison手册没有规定lexer应该如何实现以正确设置yylloc_参数。对我来说,手动跟踪每个令牌的列号是很困难的。

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

yylex声明可能会因为您使用了可重入或纯解析器而改变。似乎网络上的许多文件都建议,如果你想让野牛的位置工作,这是必需的,但这不是必需的

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

%{
...
%}
%locations
...
%%
...
在您的lexer中:

%{
...
#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
...
%%
...
%{

#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
在每次令牌操作和更新yylloc之前,“调用”YY_USER_ACTION宏。 现在您可以像这样使用@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);
}

我喜欢希洛米的回答

此外,我也在寻找更新列位置。在阅读了Shlomi的答案后发现哪个更有意义

不幸的是,yylloc的页面上有一个打字错误。我把它简化了一点

在解析器中添加:

%locations
在您的lexer中:

%{
...
#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
...
%%
...
%{

#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
列位置可能发生了一些变化,它并没有严格地跟踪列,而是不断增加。这只是我的无知,如果有人对此感到困惑,请道歉。我目前使用列来保持文件字符数,在我的情况下,这比列位置更有益

希望这能有所帮助。

所以,我让这个“起作用”,但还有几个额外的步骤(我可能在这里忽略了它们……在此表示歉意):

  • 在parser.y中,我不得不说:

    #define YYLEX_PARAM &yylval, &yylloc
    
    即使使用
    %locations
    bison--locations
    ,也可以让它传递数据

  • lexer.l中,我不得不使用
    ->
    而不是
    来表示
    yylloc

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

    [\n] { yycolumn = 1; }
    

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

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

    一种方法是在换行符出现的地方添加
    yycolumn=1
    规则(如David Elson的回答中所建议的),但如果您不想跟踪换行符可能出现的所有位置(空格、注释等)。另一种方法是在每个操作开始时检查
    yytext
    缓冲区:

    静态无效更新_loc(){
    静态int curr_line=1;
    静态int curr_col=1;
    yylloc.第一行=当前行;
    yylloc.first_列=当前列;
    {char*s;for(s=yytext;*s!='\0';s++){
    如果(*s=='\n'){
    curr_line++;
    curr_col=1;
    }否则{
    curr_col++;
    }
    }}
    yylloc.last_行=当前行;
    yylloc.last_列=当前列-1;
    }
    #定义YY_用户_操作更新_loc();
    

    最后,需要注意的一点是,一旦您开始手动跟踪列号,您还可以在同一位置跟踪行号,而不用麻烦使用Flex的
    yylineno
    选项。

    bison和
    Flex
    都不会自动更新
    yyloc
    ,但是如果你知道诀窍的话,你自己做其实并不难

    实现
    yylloc
    支持的诀窍在于,即使
    yyparse()
    声明
    yylloc
    ,它也不会改变它。这意味着,如果在对lexer的一次调用中修改
    yylloc
    ,那么在下一次调用中,您将在其中找到相同的值。因此,
    yylloc
    将包含最后一个标记的位置。由于最后一个令牌的结束与当前令牌的开始相同,因此可以使用旧的
    yylloc
    值来帮助您确定新值

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

    要更新
    yylloc
    ,我们必须首先将
    last_
    值复制到
    first_
    ,然后更新
    last_
    值以反映刚刚匹配的令牌的长度。(这不是令牌的
    strlen()
    ,而是行和列的长度。)我们可以在执行任何lexer操作之前调用的
    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++; \
            } \
        }
    
    如果您愿意,您可以将代码放在函数中,让宏调用函数,但这两种技术是相同的
    statement : IDENTIFIER '=' expression 
                { printf("%d - %d\n", @1.last_line, @1.last_column); }
    
    %define api.pure
    %locations
    
    #include "yourprser.tab.h"
    #define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
    %option bison-locations
    %option yylineno