Haskell AST Annotation with Fix

时间:2018-04-23 05:25:50

标签: haskell abstract-syntax-tree

我正在努力在Haskell中创建一个AST。我想添加不同的注释,例如类型和位置信息,所以我最终使用了fixplate。但是,我在网上找不到任何例子,并且遇到了一些困难。

我按照fixplate的推荐设置了我的AST(有些条纹):

data ProgramF a
  = Unary a
          Operator
  | Number Int
  | Let { bindings :: [(Identifier, a)]
        , body :: a }

type Program = Mu ProgramF

接下来添加标签我创建了另一个类型,以及一个基于树遍历添加标签的函数。

type LabelProgram = Attr ProgramF PLabel

labelProgram :: Program -> LabelProgram
labelProgram =
  annMap (PLabel . show . fst) . (snd . synthAccumL (\i x -> (i + 1, (i, x))) 0)

然而,除此之外,我遇到了一些问题。例如,我正在尝试编写一个对AST进行一些转换的函数。因为它需要一个标签来运行,所以我做了类型LabelProgram -> Program,但我认为我在做错了。下面是一部分功能的片段(一个更简单的部分):

toANF :: LabelProgram -> Program
toANF (Fix (Ann label (Let {bindings, body}))) = Fix $ Let bindingANF nbody
  where
    bindingANF = map (\(i, e) -> (i, toANF e)) bindings
    nbody = toANF body

我觉得我在这里处于错误的抽象层面。我应该明确地与Fix Ann ...匹配并返回Fix ...这样,还是我使用了fixplate错误?

此外,我担心如何概括功能。如何使我的功能一般适用于ProgramLabelProgramTypeProgram

1 个答案:

答案 0 :(得分:3)

编辑:使用通用注释为ProgramF添加函数示例。

是的,至少在toANF的情况下,你使用的是错误的。

toANF中,请注意您的Let bindingANF nbody以及bindingANFnbody的配套定义只是针对特定构造函数{fmap toANF的重新实现{{} 1}}。

也就是说,如果您为Let派生Functor个实例,那么您可以将ProgramF代码段重写为:

toANF

如果toANF :: LabelProgram -> Program toANF (Fix (Ann label l@(Let _ _))) = Fix (fmap toANF l) 只是 剥离标签,那么此定义适用于所有构造函数,而不仅仅是toANF,因此您可以删除模式:

Let

现在,根据@Regis_Kuckaertz的评论,您刚刚重新实施toANF :: LabelProgram -> Program toANF (Fix (Ann label l)) = Fix (fmap toANF l) ,其定义为:

forget

关于编写在forget = Fix . fmap forget . unAnn . unFix Program等上一般工作的函数,我认为在(单个)注释中编写泛型函数更有意义:

LabelProgram

并且,如果确实需要将它们应用于未注释的程序,请定义:

foo :: Attr ProgramF a -> Attr ProgramF a

type ProgramU = Attr ProgramF () 中的“U”代表“单位”。显然,如果确实需要,您可以轻松编写译员以ProgramU作为Program s进行编写:

ProgramU

作为一个具体的 - 如果是愚蠢的 - 例子,这里是一个函数,用多个绑定将toU :: Functor f => Mu f -> Attr f () toU = synthetise (const ()) fromU :: Functor f => Attr f () -> Mu f fromU = forget mapU :: (Functor f) => (Attr f () -> Attr f ()) -> Mu f -> Mu f mapU f = fromU . f . toU foo' :: Mu ProgramF -> Mu ProgramF foo' = mapU foo s分隔成带有单例绑定的嵌套Let(并因此打破{{中的相互递归绑定) 1}}语言)。它假定多绑定Let上的注释将被复制到每个生成的单个Program中:

Let

它可以应用于示例Let

splitBindings :: Attr ProgramF a -> Attr ProgramF a
splitBindings (Fix (Ann a (Let (x:y:xs) e)))
  = Fix (Ann a (Let [x] (splitBindings (Fix (Ann a (Let (y:xs) e))))))
splitBindings (Fix e) = Fix (fmap splitBindings e)
像这样:

Program

这是我完整的工作示例:

testprog :: Program
testprog = Fix $ Unary (Fix $ Let [(Identifier "x", Fix $ Number 1), 
                                   (Identifier "y", Fix $ Number 2)] 
                                  (Fix $ Unary (Fix $ Number 3) NegOp))
                       NegOp