2016-09-26 4 views
1

私は個人的なプロジェクトとして(クールな言語用の)コンパイラを作成していますが、シンボルテーブルを設計する際に問題があります。コンテキストの場合、私はASTとしてクラスの階層を使用しています。ここではASTの小さな抜粋です:おもちゃのコンパイラのシンボルテーブル

現在
class NodeAST { 
public: 
    virtual void accept(Visitor&) = 0; 
}; 

class ProgramAST : public NodeAST { 
private: 
    const std::vector<ClassPtr> vClasses; 

public: 
    ProgramAST(std::vector<ClassPtr> vClasses); 

    auto class_cbegin() const { 
    return std::cbegin(vClasses); 
    } 
    auto class_cend() const { 
    return std::cend(vClasses); 
    } 

    virtual void accept(Visitor& v) override; 
}; 

class ClassAST : public NodeAST { 
private: 
    const std::string name; 
    const std::vector<FeaturePtr> vFeatures; 

public: 
    ClassAST(std::string name, std::vector<FeaturePtr> vFeatures); 

    auto getName() const { 
    return name; 
    } 

    auto feature_cbegin() const { 
    return std::cbegin(vFeatures); 
    } 
    auto feature_cend() const { 
    return std::cend(vFeatures); 
    } 

    virtual void accept(Visitor& v) override; 
}; 

、私のシンボルテーブルの中心に、以下のように定義されたマップである。

std::unordered_map<std::string, NodeAST*> table 

それはで対応するノードに宣言の名前をマップAST。このようにして、たとえば、私がASTノードで設定した型を使って、ゆるい識別子の型を記入することができます。

ただし、ノードのシンボルテーブルにクエリを実行すると、NodeAST*が返されるという問題があります。実際に使用するにはClassAST*MethodAST*VarDecAST*などにダウンキャストする必要があります。

ダウンキャストの必要性を回避する方法でシンボルテーブルを設計するにはどうすればよいですか?

+2

私はダウンキャスティングが必然的に大きな問題であるとは言いません。コンテキストからターゲットタイプを知ることができます。クールなソースコードにはエラーが含まれているのでダウンキャストは失敗する可能性がありますが、その場合は 'dynamic_cast'が失敗し、直接エラーとして出力します。 'クラスを期待しましたが、" Foo "は関数の名前を付けました。 – MSalters

+0

多分:ベース 'NodeAst'を取り除き、すべての' accept'関数を非仮想テンプレートにしてください。より多くの訪問者がそれぞれのコレクションタイプで異なる動作をする可能性があります。 –

+0

@DieterLücking:通常、基本クラスを削除する必要はほとんどありません。継承によって関連づけられたクラスでテンプレートをインスタンス化することはできます。 – MSalters

答えて

1

私が実装しているプログラミング言語はわかりませんが、実際には動的キャストを完全に避けることができるとは思われません。たとえば、f(1)の場合、fは、言語にラムダがある場合は関数または変数のいずれかになります。あなたはそれをシンボルテーブルで調べることで見つけ出す必要があります。

この可能性を絶対に排除できれば、理論上は各タイプごとに別々のシンボルテーブルを作成できます。しかし、もしあなたがそれらを検出する必要があるならば、明らかに名前の衝突を見つけるのが難しくなり、プログラミング言語が後で拡張するのが難しくなるかもしれないことを覚えておいてください。私はこのソリューションをお勧めしません。動的キャストは、カプセル化され、全体のコードベースを中心に広がらないように

個人的に私はちょうど、あなたのNodeASTクラスにto_class()to_var()is_class()is_var()などのようなメソッドを追加します。 get_class(),get_var()など

RTTIのランタイムコストが懸念される場合は、他のコンパイラが使用しているソリューションを参照することができます。また、シンボルテーブルのクラスを作成することもできます。 LLVMについては、ここで説明します:http://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html

1

私はVisitor Patternを使用して共通のベースを持つポインタのコンテナにアクセスしています。 1つの実装では、dynamic_cast <の実装よりも約40%のスピードアップを経験しました。これは、私が書いていたデータベース抽象レイヤーで重要でした。

ここでの答えの一つでもう少し説明があります.... Visitor PatternRight design pattern to deal with polymorphic collections of objects

Wikipediaのページには、ディスパッチャクラスによって訪問されたベースポインタのコレクションの素敵な短いC++の例を示します。

質問に「訪問可能」クラスのソースコードが表示されています。 dynamic_cast <>が必要な理由を確認するには、「訪問者」の実装を確認する必要があります。それは必要ではありません。使用する正しいvisit()関数は、関数パラメータのオーバーロードによって選択する必要があります。

乾杯

+0

訪問者パターンのテンプレート化されていない実装でダウンキャストする必要はありません(テンプレートを使用して一般的な訪問者の実装を調査する場合は、Lokiライブラリを検索してください)。受け入れられた答えが正しいとは確信していませんが、それについてコメントする評判はありません! –

+0

シンボルテーブルに挿入するために、訪問者パターンを使用してASTをトラバースし、名前付きの値を挿入します。それは素晴らしい作品です。問題は私のTypeCheckVisitorクラスにあります。再度、訪問者パターンを使用してトラバースし、各ASTノードの分析を実行します。分析中、私はシンボルテーブルを照会する必要があります。たとえば、BinaryOperatorノードでLHSとRHSのタイプを取得する必要があります。私は現在、(Visitorパターンの結果として)typeCheckBinaryOperator関数内にそれらの型が必要です。私はLHSとRHSを最初に変数にダウンキャストすることなくこれを行う方法を見ていません。 – ryan

関連する問題