Bison:如何在不使用全局变量的情况下访问先前非终端的值?

问题描述

我的大语法中有两条规则:

indexed_component
    : name '(' val_list ')' 
    ;

val_list
    : val          
    | val_list ',' val
    ;

如何访问 val_list 中 'name' 的值?

我知道我可以使用全局变量,但这是高度递归的,所以我必须使用堆栈,我想避免麻烦。

解决方法

您想要的是所谓的继承属性,不幸的是,yacc 和bison 并不真正支持该属性。您可以通过访问带有负索引的属性来“破解”它,但这很容易出错。

如果您想尝试使用 btyacc,它有一些语法糖可以帮助您(至少允许对它们进行类型检查)。你可以把它写成

indexed_component : name '(' val_list($1) ')' ;
val_list($name) : val
         | val_list($name) ',' val ;

然后在 val_list 上的操作中,您可以访问 $name。对于 %union,您需要使用 %type 正确声明:

%union {
    char *str;
    struct list *list;
}

%type <str> name                // name gives a string
%type <list> val_list(<str>)    // val_list takes a string and gives a list

正如我所提到的,这只是 btyacc 中的语法糖——它最终被转换为访问 $0 的内联操作。所以它将被转换为:

indexed_component : name '(' { $<str>$ = $1; } val_list ')' ;
val_list : val
         | val_list ',' val ;

并且在 val_list 操作中使用 $name 将变为 $<str>0;

这种工作方式是,当 $0 规则减少并且规则的 RHS 的值被(名义上)弹出时,val_list 最终成为 yacc 堆栈顶部的任何内容-- 它是出现在 RHS 上的任何规则 val_list 的 RHS 上紧接在 val_list 之前的最后一件事的值。

,

在您的情况下,您可以使用野牛文档中所谓的中间规则操作。下面是它的样子:

int(arg)

只要从词法分析器中获取 {% std::string componentName: %} %union { char* id; } %type <id> name %% indexed_component : name { componentName = $1; } '(' val_list ')' ; val_list : val | val_list ',' val ; ,就会在此处执行中间规则操作。那么 ( 的值将在规则 componentName 的任何语义动作中可用,因为中间规则动作是在之前执行的。

中间规则操作的文档: https://www.gnu.org/software/bison/manual/html_node/Using-Midrule-Actions.html

编辑:

但是该解决方案需要一个您不想要的全局变量。然后我能看到的唯一其他解决方案是构建一个语法树并将名称的值作为 val_list 子树中的继承属性进行传播。