2

非常に単純なおもちゃの言語パーサーを実装しているとしましょう。DU を使用するか、レコード タイプを使用するかを決定しています (両方の組み合わせでしょうか?)。言語の構造は次のようになります。

a Namespace consists of a name and a list of classes
a Class consists of a name and a list of methods
Method consists of a name, return type and a list of Arguments
Argument consists of a type and a name

この単純な言語でのプログラムの例:

namespace ns {
  class cls1 {
    void m1() {}
  }

  class cls2 {
    void m2(int i, string j) {}
  }
}

これをどのようにモデル化し、その理由を教えてください。

4

2 に答える 2

6

ほとんどの場合、DU を使用して、コード構造の任意の部分が複数の可能性の 1 つになる可能性のある代替を実装します。組み合わせはおそらく理想的ですが、レコードの代わりにタプルを使用することもできます。これにより、タプルに名前付きのアイテムがないため、使用が簡単になる可能性がありますが、読み取りと保守がより難しくなる可能性があります。

私はそれをこのようなものとしてモデル化します

type CompilationUnit = | Namespace list

and Namespace = { Name : String
                  Body : NamespaceBody }

and NamespaceBody = | Classes of Class list

and Class = { Name : String
              Body : ClassBody }

and ClassBody = | Members of Member list

and Member = | Method of Method

and Method = { Name : String
               Parameters : Parameter list option
               ReturnType : TypeName option
               Body : MethodBody }

and Parameter = { Name : String
                  Type : TypeName }

and MethodBody = ...

and TypeName = ...

DU の必要性は、サンプル言語を使用して明らかではないかもしれませんが、1 つまたは複数の項目である可能性があるコードのポイントがあればすぐに明らかになります。たとえば、クラスにフィールドを追加する場合、新しいField識別をに追加するだけで済みますMember

文法を使用して言語 (LL/LALR など) を解析している場合、おそらく、文法に含まれる各代替ルールに対応する DU が必要になります。

于 2011-08-24T23:54:54.673 に答える
0

名前空間は名前とクラスのリストで構成されます クラスは名前とメソッドのリストで構成されます メソッドは名前、戻り値の型と引数のリストで構成されます 引数は型と名前で構成されます この単純なプログラムの例言語:

型システムの型定義も必要であり、それが実際に共用体型が価値のある唯一の場所です。

type Type = Void | Int | String

したがって、言語の型は int または string または void のいずれかですが、何も (null など) にすることはできず、これらのオプションの複数にすることはできません。

名前空間の型は、次のように完全に匿名にすることができます。

string * (string * (Type * string * (Type * string) list) list) list

サンプルの名前空間を次のように定義できます。

"ns", ["cls1", [Void, "m1", []]
       "cls2", [Void, "m2", [Int, "i"; String, "j"]]]

実際には、おそらく、名前空間を別の名前空間に配置したり、クラスをクラスに配置したりして、コードを次のように進化させたいと思うでしょう。

type Type =
  | Void
  | Int
  | String
  | Class of Map<string, Type> * Map<string, Type * (Type * string) list>

type Namespace =
  | Namespace of string * Namespace list * Map<string, Type>

Namespace("ns", [],
          Map
            [ "cls1", Class(Map[], Map["m1", (Void, [])])
              "cls2", Class(Map[], Map["m2", (Void, [Int, "i"; String, "j"])])])

匿名型は、混乱の元にならない限り問題ありません。経験則として、2 つまたは 3 つのフィールドがあり、それらが異なるタイプ (ここでは「メソッド」のように) である場合は、タプルで問題ありません。それ以上のフィールドまたは同じタイプのフィールドが複数ある場合は、レコード タイプに切り替える必要があります。

したがって、この場合、メソッドのレコード タイプを導入することができます。

type Method =
  { ReturnType: Type
    Arguments: (Type * string) list }

and Type =
  | Void
  | Int
  | String
  | Class of Map<string, Type> * Map<string, Method>

type Namespace =
  | Namespace of string * Namespace list * Map<string, Type>

Namespace("ns", [],
          Map
            [ "cls1", Class(Map[], Map["m1", { ReturnType = Void; Arguments = [] }])
              "cls2", Class(Map[], Map["m2", { ReturnType = Void; Arguments = [Int, "i"; String, "j"] }])])

そしておそらく、それらのレコードを構築するためのヘルパー関数:

let Method retTy name args =
  name, { ReturnType = retTy; Arguments = args }

Namespace("ns", [],
          Map
            [ "cls1", Class(Map[], Map[Method Void "m1" []])
              "cls2", Class(Map[], Map[Method Void "m2" [Int, "i"; String, "j"]])])
于 2013-06-27T09:33:12.900 に答える