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