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



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

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

如何在val_list中访问"名称"的值?

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

你想要的是所谓的继承属性,不幸的是,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;

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

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

{%
std::string componentName:
%}
%union {
char* id;
}
%type <id> name
%%
indexed_component : name { componentName = $1; } '(' 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子树中。

相关内容

最新更新