【问题标题】:Haskell AST Annotation with Fix带修复的 Haskell AST 注释
【发布时间】:2018-10-03 01:49:34
【问题描述】:

我正在努力在 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,但我认为我在这里做错了。下面是部分函数的 sn-p(较简单的部分之一):

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 ...,还是我使用的固定板错误?

另外,我关心的是如何泛化函数。如何使我的函数一般适用于Programs、LabelPrograms 和TypePrograms?

【问题讨论】:

  • Attr f a 只是Mu (Ann f a) 的同义词,因此您可以使用固定板中提供的遍历聚宝盆。 toANF 本质上是 forget,但是阅读您的意图,您不想从 AST 中删除标签,对吗? hackage.haskell.org/package/fixplate-0.1.7/docs/…

标签: haskell abstract-syntax-tree recursive-datastructures fixpoint-combinators


【解决方案1】:

编辑:ProgramFs 添加一个带有通用注释的函数示例。

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

toANF 中,请注意您的Let bindingANF nbody 以及bindingANFnbody 的伴随定义只是特定构造函数fmap toANF 的重新实现Let

也就是说,如果您为您的ProgramF 派生一个Functor 实例,那么您可以将您的toANF sn-p 重写为:

toANF :: LabelProgram -> Program
toANF (Fix (Ann label l@(Let _ _))) = Fix (fmap toANF l)

如果toANF只是剥离标签,那么此定义适用于所有构造函数,而不仅仅是Let,因此您可以删除该模式:

toANF :: LabelProgram -> Program
toANF (Fix (Ann label l)) = Fix (fmap toANF l)

现在,根据@Regis_Kuckaertz 的评论,您刚刚重新实现了forget,其定义为:

forget = Fix . fmap forget . unAnn . unFix

关于编写在ProgramLabelProgram 等上通用的函数,我认为在(单个)注释中编写通用函数更有意义:

foo :: Attr ProgramF a -> Attr ProgramF a

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

type ProgramU = Attr ProgramF ()

ProgramU 中的“U”代表“单位”。显然,如果确实需要,您可以轻松编写翻译器以使用 Programs 作为 ProgramUs:

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

作为一个具体的(如果愚蠢的话)示例,这里有一个函数,它将具有多个绑定的Lets 分隔为具有单例绑定的嵌套Lets(因此打破了Program 语言中的相互递归绑定)。它假定多重绑定Let 上的注释将被复制到每个生成的单例Lets:

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

像这样:

> mapU splitBindings testprog
Fix (Unary (Fix (Let {bindings = [(Identifier "x",Fix (Number 1))],
body = Fix (Let {bindings = [(Identifier "y",Fix (Number 2))], 
body = Fix (Unary (Fix (Number 3)) NegOp)})})) NegOp)
>

这是我的完整工作示例:

{-# LANGUAGE DeriveFunctor #-}
{-# OPTIONS_GHC -Wall #-}

import Data.Generics.Fixplate

data Identifier = Identifier String deriving (Show)
data PLabel = PLabel deriving (Show)
data Operator = NegOp deriving (Show)

data ProgramF a
  = Unary a
          Operator
  | Number Int
  | Let { bindings :: [(Identifier, a)]
        , body :: a }
  deriving (Show, Functor)
instance ShowF ProgramF where showsPrecF = showsPrec

type Program = Mu ProgramF
type LabelProgram = Attr ProgramF PLabel

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)

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

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

main :: IO ()
main = print $ mapU splitBindings testprog

【讨论】:

    猜你喜欢
    • 2015-01-16
    • 2022-10-02
    • 2013-09-17
    • 2020-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多