YACC左递归中的正确顺序

时间:2015-10-20 11:24:48

标签: yacc

假设我们有以下简单的YACC语法:

start:
    list
    {
        if ($1 != NULL) {
            Reverse(&$1); /*correct order*/
        }
        Generate($1);
    }
    ;

list:
    list item
    {
        $$ = Node($2, $1);
    }
    |
    {
        $$ = NULL;
    }
    ;

有没有办法构造list的二进制抽象语法树(仍然使用左递归),以便不必在start中更正元素的顺序?什么是作案手法?

1 个答案:

答案 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中,我们只需将列表中的一个节点循环向左旋转并打破圆圈。 (代码很复杂,因为列表是空的。如果空列表不可能,或者我们愿意分配额外的头元素并在最后丢弃它,代码可以简化。)

在实践中,我不会使用上面的代码,因为它不易阅读,也没有真正的性能优势。

当然,您可以使用正确的递归来向后减少元素,这有效地使用解析堆栈来保存中间列表。这使用了可能无限量的堆栈空间,反向处理顺序可能会产生其他后果(如果节点处理不起作用);无论如何,它并不比从左到右的方法快。