假设我们有以下简单的YACC语法:
start:
list
{
if ($1 != NULL) {
Reverse(&$1); /*correct order*/
}
Generate($1);
}
;
list:
list item
{
$$ = Node($2, $1);
}
|
{
$$ = NULL;
}
;
有没有办法构造list
的二进制抽象语法树(仍然使用左递归),以便不必在start
中更正元素的顺序?什么是作案手法?
答案 0 :(得分:1)
不是。
左递归语法从左到右执行缩减。如果要构建链接列表,则需要在结尾处反转列表,如OP中所示,或者需要保留指向列表末尾的指针,以便可以追加到O中的列表(1)。 (当完全解析列表时,您仍然需要从解析堆栈中丢弃此指针。)
这是第二个策略的一个例子:
start
: list { if ($1) {
$$ = $1->next;
$1->next = NULL;
}
}
list: { $$ = NULL; }
| list node { if ($1) {
$$ = Node($2, $1->next);
$1->next = $$;
}
else {
$$ = Node($2, NULL);
$$->next = $$;
}
}
这里,中间列表是循环的,list
的语义值是它的最后一个元素。实际上,我们维护列表向右旋转一个节点。最后,在start
中,我们只需将列表中的一个节点循环向左旋转并打破圆圈。 (代码很复杂,因为列表是空的。如果空列表不可能,或者我们愿意分配额外的头元素并在最后丢弃它,代码可以简化。)
在实践中,我不会使用上面的代码,因为它不易阅读,也没有真正的性能优势。
当然,您可以使用正确的递归来向后减少元素,这有效地使用解析堆栈来保存中间列表。这使用了可能无限量的堆栈空间,反向处理顺序可能会产生其他后果(如果节点处理不起作用);无论如何,它并不比从左到右的方法快。