web-dev-qa-db-ja.com

AST=訪問者を使用してトラバースする

私はCのような言語のコンパイラーを書いていて、抽象的な構文ツリーをトラバースするエレガントな方法を探しています。 Visitorパターンを実装しようとしていますが、正しく実行しているとは確信していません。

_struct Visitor {   
    // Expressions
    virtual void visit(AsgnExpression&);
    virtual void visit(ConstantExpression&);
    ...
    virtual void visit(Statement&);
    ...

    virtual void finished(ASTNode&);

protected:
    virtual void visit(ASTNode&) = 0;
};
_

visitはタイプごとにオーバーロードされます。デフォルトでは、各オーバーロードがvisit(ASTNode&)を呼び出し、サブクラスは強制的に実装されます。これにより、型ごとにvisitを定義するのは面倒ですが、すばやく簡単に行うことができます。 ASTNodeの各サブクラスは、ツリー構造をトラバースするために使用されるacceptメソッドを実装する必要があります。

_class ASTNode {
public:
    virtual ~ASTNode();
    virtual void accept(Visitor& visitor) = 0;
};
_

ただし、acceptメソッドはよく似ているため、この設計はすぐに退屈になります。

誰が構造、ノード、またはビジターのトラバースを担当する必要がありますか?私はASTNodeに子にアクセスするためのイテレーターを提供させ、ビジターに構造をトラバースさせるようにしています。抽象構文木を設計した経験があれば、私にあなたの知恵を共有してください!

4
user2844493

トラバーサルの責任者は、ビジターで実行する分析、言語構造の詳細、および個人の好みの一部に大きく依存します。

特に、親ノードのビジターが子の処理の途中でアクションを実行する必要がある場合は、トラバーサルロジックをビジターに配置する必要があります。たとえば、言語に、新しく導入された変数が、変数を導入するノードの子ノードの一部でのみ使用可能な構成がある場合。
もう1つのケースは、プレオーダートラバースとポストオーダートラバースの混合が必要な場合です。ノード内のトラバーサルでは、各ノードがビジターを2回呼び出す必要があります。その場合、訪問者にトラバーサルを行わせる方が簡単な場合があります。

そうでなければ、それは主に好みの問題です。トラバーサルは、ノード内またはビジター内のいずれかです。

異なる問題には、異なるトラバーサル戦略が必要です。したがって、私の提案は、戦略パターンを使用して別のトラバーサーを実装することです。

Visitorパターンには、多くのボイラープレートコードが必要になる場合があります。これは実際には自動コード生成に適しています(Javaの jaxb-visitor を参照)。私の提案は、基本的にすべてのノードタイプに対して何もしない基本/デフォルトのビジターを実装することです。何か面白いことをしようとしている訪問者は、ベースの定型訪問者を拡張する必要があります。新しいノードタイプがビジターインターフェースに追加された場合、基本クラスに空の実装を提供するだけで済みます。それ以外の場合は、新しいノードタイプを追加するたびに、すべての実装を探し、新しいメソッドを追加する必要があります。

1
Ryan