在模板Haskell AST中重新关联树

时间:2014-10-24 23:12:32

标签: haskell ghc operator-precedence infix-notation template-haskell

我正在升级一个库,我将Haskell翻译成另一种语言。现在我正在使用Meta.Parse读取Haskell模块,然后返回其TemplateHaskell AST,如here所述。

我遇到的问题是,当我运行解析时,我得到了一堆解析为UInfixE和UInfixP的中缀运算符,这意味着它们具有未解析的关联性。

description of the associativity讨论了Haskell编译器如何解决这些问题。

我想知道,是否有一个功能可以在我解析的树上重新关联?我正在寻找这样的东西:

reassoc :: [Dec] -> [Dec]

我可以编写大量的AST遍历来实现这一点,但似乎它将是一个大量的样板,并且显然该函数已经以某种形式存在(希望以一种与TH相配的形式)。

这样的事情存在吗?有没有一种简单的方法可以摆脱AST从解析声明中得到的未解析的中缀运算符?

编辑:即使是一个函数,给定运算符的名称,它可以给出它的优先级和关联性,这将是非常有用的。

1 个答案:

答案 0 :(得分:3)

我不知道这样的函数是否存在,但您可以自己编写一个函数,使用Scrap Your Boilerplate来删除所有样板代码。特别是,编写一个应用于每个UInfixE的函数(当自上而下遍历一个声明时)很容易:

import Language.Haskell.TH
import Data.Data
import Data.Generics.Schemes
import Data.Generics.Aliases

fixity :: (Data a)
       => (Exp -> Exp -> Exp -> Exp)    -- ^ a function that converts
                                        -- UInfixE Exp Exp Exp to another Exp
       -> a -> a
fixity f = everywhere' (mkT expf)
  where
    expf (UInfixE l o r) = f l o r
    expf e               = e

fixity可以应用于Data类型的任何内容,包括DecExp和其他TH数据类型。

你可能不关心UInfixE的顺序,因为无论如何树都是模棱两可的,无论如何都需要重新关联。在这个方向上更进一步,我们可以增强功能,折叠它找到的UInfixE的每一棵树:

-- | Converts all `UInfixU` subtrees using given right-folding functions.
--
-- For example if @fixityFold step right result@ finds
-- @1 + 2 * 3 / 4@ somewhere,
-- the corresponding subtree will be replaced by (abusing the notation)
-- @result (step '1 '+ (step '2 '* (step '3 '/ (right '4))))@.
fixityFold :: (Data a)
           => (Exp -> Exp -> a -> a)
           -> (Exp -> a)
           -> (a -> Exp)
           -> a -> a
fixityFold step right result = everywhere' (mkT expf)
  where
    expf exp@(UInfixE _ _ _) = result (foldrUInfixE exp right)
    expf e                   = e
    foldrUInfixE (UInfixE l o r) rf = foldrUInfixE l
                                        (\e -> step e o (foldrUInfixE r rf))
    foldrUInfixE exp             rf = rf exp

这应该允许您构建使用适当的优先级重新排列表达式所需的任何树状结构。