9

(免責事項: これらの例は、コンパイラを構築するという文脈で与えられていますが、この質問はすべてビジター パターンに関するものであり、コンパイラ理論の知識は必要ありません。)コンパイラの理論を独学で学んでいますが (いや、これは宿題ではありません)、彼がビジター パターンを使用して AST を IR ツリーに変換する方法を理解するのに苦労しています。(注: Python でこれを行っているので、Python も学習できます。これが、次の例が Java ではない理由です。) 私が理解しているように、Visitor パターンの visit メソッドと accept メソッドは設計により void 型になっています。だから私が何かを持っているなら

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))

これにより、2 つの子式が IR に変換され、プラス式を表す BINOP にリンクされます。もちろん、追加情報を返すようにすべての受け入れ関数を変更しない限り、これは不可能です。また、何も返さない印刷ビジターが必要な場合があるため、これも面倒です。それでも、このテキストでは、ビジターが正しい方法であると主張しています。Java では、Python の柔軟性がなくても実行できることを意味します。信じられないほどハックではない解決策は考えられません-意図した設計について誰かが私に教えてくれますか?

4

3 に答える 3

11

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));
    }
于 2009-12-14T10:11:11.277 に答える
1

このコンパイラのソースコードを見てください。男はビジターパターンを使用したと思います。

于 2009-12-14T05:18:06.550 に答える
0

注意:私はその本を読んでいません。

メソッドは void 型かもしれませんが、Java (この本が書かれた対象) ではオブジェクトの一部でもあります。そのため、ビジター メソッドはローカル メンバー変数に構造を構築できるため、呼び出し間で必要なコンテキストを維持できます。

したがって、たとえば、印刷ビジターは、メンバー変数として (またはビジター オブジェクトを作成したメソッドの最後のローカル変数として) 保持されている StringBuilder に追加されます。これは 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 の戻り値の型を保持できます。

于 2009-12-14T05:02:54.867 に答える