选择是否在F#中为小型AST使用区分联合或记录类型

dev*_*ium 2 .net f# functional-programming record discriminated-union

假设我正在实现一个非常简单的玩具语言解析器.我决定是使用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
Run Code Online (Sandbox Code Playgroud)

这个简单语言的程序示例:

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

  class cls2 {
    void m2(int i, string j) {}
  }
}
Run Code Online (Sandbox Code Playgroud)

你会如何模仿这个以及为什么?

Mar*_*k H 6

您几乎肯定希望使用DU来实现替换,其中代码结构的任何部分可能是多种可能性之一.混合可能是理想的,虽然您可以使用元组代替记录 - 这可能使其更易于使用,但可能更难以阅读和维护,因为您在元组中没有命名项.

我会把它模仿成这样的东西

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 = ...
Run Code Online (Sandbox Code Playgroud)

使用您的示例语言对DU的需求可能并不明显,但只要您在代码中有任何可能是一个或多个项目的点,就会变得清晰.比方说,例如,如果您向班级添加字段 - 您只需要添加新的Field歧视Member.

如果您使用语法来解析您的语言(LL/LALR或类似),您可能需要为语法中的每个替换规则匹配DU.

  • 我倾向于使用只有元组的DU.它们更容易编写,但是如果你不仅仅有几个参数,你可能会感到困惑,而且依靠文档来告诉你每个参数的用途.例如,对于Method in Method中的参数,您可以改为使用`Parameters:(String*TypeName)列表选项`,并且很明显元组的每个项目代表什么.但是如果您也使用String作为类型而不是TypeName,并且具有`(String*String)`,则需要一条记录来了​​解每种类型的用途. (2认同)