Haskell中用于简单语言的AST的良好类型设计

时间:2014-12-29 21:26:26

标签: parsing haskell parsec

我是Haskell的新手,正在研究Haskell LLVM tutorial。在其中,作者定义了一个简单的代数数据类型来表示AST。

type Name = String

data Expr
  = Float Double
  | BinOp Op Expr Expr
  | Var String
  | Call Name [Expr]
  | Function Name [Expr] Expr
  | Extern Name [Expr]
  deriving (Eq, Ord, Show)

data Op
  = Plus
  | Minus
  | Times
  | Divide
  deriving (Eq, Ord, Show)

但是,这不是一个理想的结构,因为解析器实际上期望Expr中的Extern列表只包含表示变量的表达式(即在这种情况下的参数不能是任意表达式)。我想让类型反映这个约束(使用QuickCheck更容易生成随机有效的AST);但是,为了解析器函数(所有类型都有Parser Expr)的一致性,我不只想说| Expr Name [Name]。我想做这样的事情:

data Expr
  = ...
  | Var String
    ...
  | Function Name [Expr] Expr
  | Extern Name [Var] -- enforce constraint here
  deriving (Eq, Ord, Show)

但是在Haskell中这是不可能的。

总而言之,ExternVar都应该是Expr,而Extern应该有一个代表参数的Vars列表。最好的方法是将所有这些分开并使它们成为Expr类型类的实例(没有任何方法)?或者是否有更惯用的方法(或者更好地废弃这些类型并做一些完全不同的事情)?

1 个答案:

答案 0 :(得分:5)

免责声明,我是您提到的LLVM教程的作者。

只需使用Extern Name [Name],教程第3章后面的所有内容都会使用exact definition。我想我只是忘了让第2章Syntax.hs与其他人保持一致。

我不担心使解析器定义一致,它们可以返回不同的类型。这是后来解析器使用的内容。 identifier只是来自LanguageDef的字母数字标识符的parsec,它成为AST中的Name类型。

extern :: Parser Expr
extern = do
  reserved "extern"
  name <- identifier
  args <- parens $ many identifier
  return $ Extern name args