私は小さな DSL を含むプロジェクトに取り組んでいます。この言語で文字列を字句解析して解析すると、Expr と呼ばれる抽象クラスとして実装された解析ツリーが生成されます。このクラスには、代入である解析ツリー ノードに対応する、AssignmentExpr、InvokeExpr、AdditionExpr などの多くの通常の派生クラスがあります。 、関数の呼び出し、追加など。プロジェクトは C# で実装されています。
現在、この DSL の型推論の実装を検討しています。これは、Expr クラスのインスタンスを取得して、ツリー内のさまざまなノードの型に関する情報をエンコードしたものを返せるようにしたいということです。この型情報は、シンボル テーブル (変数の型) と関数テーブル (関数シグネチャ) に依存します。したがって、私は次のようなことをしたいと思います:
TypedExpr typedExpr = inferTypes(expr, symbolTable, functionTable)
ここで、TypedExpr は理想的には Expr のようになりますが、式の型を与える Type プロパティを除きます。ただし、これには次の設計上の問題があります。
TypedExpr が Expr から継承し、単純に追加のプロパティ Type を実装することは理にかなっています。ただし、これにより、2 つの並列継承階層が作成されます。1 つは TypedExpr (TypedAssignmentExpr、TypedInvokeExpr など) 用で、もう 1 つは Expr (AssignmentExpr、InvokeExpr など) 用です。これは維持するのに不便であり、解析ツリーをさらに拡張する必要がある場合、問題は拡大します。これをどのように軽減できるかわかりません。1 つの可能性として橋の設計パターンがありますが、これで問題が完全に解決できるとは思いません。
別の方法として、Expr は単純に Type プロパティを実装することもできます。このプロパティは、パーサーからの構築時に null であり、後で型推論アルゴリズムによって埋められます。ただし、null フィールドを持つオブジェクトを渡すと、NullReferenceExceptions が発生します。TypedExpr のアイデアは、これを軽減していたでしょう。さらに、Expr クラスの考え方が解析ツリーを表現することであることを考えると、型情報は実際にはツリーの一部ではありません。型付けは状況依存であり、特定のシンボル テーブルと関数テーブルが必要です。
3 番目に、型推論メソッドは、すべてのノードに関する型情報をエンコードする Dictionary< Expr, Type> を単純に返すこともできます。これは、Expr が解析ツリーのみを表すままであることを意味します。これの欠点は、構築されたディクショナリ オブジェクトが、型推論メソッドに渡された Expr オブジェクトに明確にリンクされていることを示す明白なプロパティを持たないことです。
上記の 3 つの解決策のいずれにも完全に満足しているわけではありません。
私の質問は、この問題に対するさまざまなアプローチの利点と欠点は何ですか? 型情報を解析ツリーで直接エンコードする必要がありますか、それとも並列ツリー クラスを使用する必要がありますか? それとも辞書ソリューションが最適ですか? 受け入れられた「ベストプラクティス」ソリューションはありますか?