使用访客模式的树转换

时间:2009-12-14 04:41:00

标签: java python tree abstract-syntax-tree visitor-pattern

(免责声明:这些示例是在构建编译器的上下文中给出的,但这个问题都是关于访问者模式的,并且不需要任何编译器理论知识。)我将通过Andrew Appel的Java中的现代编译器实现尝试自学编译器理论(所以不,这不是作业),我很难理解他想如何使用Visitor模式将AST转换为IR树。 (注意:我在Python中这样做,所以我也可以学习Python,这就是为什么即将推出的示例不是Java的原因。)据我所知,访问者模式中的访问和接受方法是无效的设计类型,所以,如果我有像

这样的东西
class PlusExp(Exp):
    def __init__(self, exp_left, exp_right):
        self.exp_left = exp_left
        self.exp_right = exp_right

    def accept(self, v):
        v.visit_plus_exp(self)

然后我希望能够编写像

这样的访问者方法
def visit_plus_exp(self, plus_exp):
    return BINOP(BinOp.PLUS, 
                 plus_exp.exp_left.accept(self), 
                 plus_exp.exp_right.accept(self))

将两个子表达式转换为IR,然后将它们与表示加号表达式的BINOP链接起来。当然,这是不可能的,除非我修改所有的接受函数以返回额外的信息,这也是杂乱的,因为有时你只是想要一个不返回任何内容的打印访问者。然而,本文坚持认为访问者是正确的方式,而在Java中,这意味着它可以在没有Python灵活性的情况下完成。我想不出任何不太令人难以置信的解决方案 - 任何人都可以启发我的预期设计吗?

3 个答案:

答案 0 :(得分:9)

SAX解析器是一种访问者。要避免向方法添加返回值,可以使用堆栈:

class Visitor {
    Stack<Node> stack = new Stack<Node>();

//    . . .

    void visitPlus(PlusExp pe) {
        pe.left.accept(this);
        pe.right.accept(this);
        Node b = stack.pop();
        Node a = stack.pop();
        stack.push(new BinOp(BinOp.PLUS, a, b));
    }

答案 1 :(得分:1)

查看THIS编译器的源代码。我认为这个家伙使用过访客模式。

答案 2 :(得分:0)

警告:我没看过那本书。

该方法可以是void-typed,但是在Java(本书是为其编写)中,它也是对象的一部分。因此,visitor方法可以在本地成员变量中构建结构,从而在调用之间保持必要的上下文。

因此,例如,您的打印访问者将附加到StringBuilder,该StringBuilder作为成员变量保存(或者作为创建访问者对象的方法中的最终局部变量 - 这在Java中很常见,在Java中创建小的匿名内部类对象是一种常见的习惯。)

在python中,您可以类似地让访问者方法访问非方法局部变量以维护上下文并构建结构。例如,关闭或一个小物体。

更新 - 从下面的评论中添加了一小段代码

result = new Node();
result.left.add(n1.accept(this)); 
result.right.add(n2.accept(this)); 
return result;

result = new Node(); 
this.nextLoc.add(result); 
this.nextLoc = result.left; 
n1.accept(this); 
this.nextLoc = result.right; 
n2.accept(this); 

第一个更漂亮(虽然仍然是糟糕的注释示例代码),但第二个会让你保留void返回类型,如果你真的需要。

相关问题