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

Bison: how to access of value of previous non-terminal without using globals?

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

indexed_component
    : name '(' val_list ')' 
    ;

val_list
    : val          
    | val_list ',' val
    ;

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

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

在您的情况下,您可以使用 bison 文档中所谓的中间规则操作。它可能是这样的:

{%
std::string componentName:
%}
%union {
    char* id;
}
%type <id> name
%%
indexed_component : name { componentName = ; } '(' val_list ')' ;

val_list : val
| val_list ',' val ;

只要从词法分析器中获取 (,中间规则操作就会在此处执行。然后 componentName 的值将在规则 val_list 的任何语义操作中可用,因为中间规则操作在之前执行。

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

编辑:

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

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

如果您想尝试使用 btyacc,它有一些语法糖来帮助解决这个问题(至少允许对它们进行类型检查)。你会写成

indexed_component : name '(' val_list() ')' ;
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>$ = ; } val_list ')' ;
val_list : val
         | val_list ',' val ;

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

它的工作方式是 [=19=]val_list 规则被减少并且规则的 RHS 值是(名义上) 弹出 - 它是紧接在 RHS 上 val_list 之前的最后一件事的值 val_list 出现在.

的 RHS 上