3

I'm upgrading a library where I translate Haskell to another language. Right now I'm using Meta.Parse to read in a Haskell module, and get back its TemplateHaskell AST, as described here.

The problem I'm running into is that, when I run the parse, I get a bunch of infix operators parsed as UInfixE and UInfixP, meaning that they are of unresolved associativity.

The description of the associativity talks about how the Haskell compiler resolves these.

I'm wondering, is there a function avaliable which can do this reassociation on the trees I get from parsing? I'm looking for something like this:

reassoc :: [Dec] -> [Dec]

I could write the massive AST traversal that does this, but it seems like it would be a massive amount of boilerplate, and clearly the function already exists in some form (hopefully in a form that plays nice with TH).

Does such a thing exist? Is there an easy way to get rid of unresolved infix operators in the AST I get from parsing declarations?

EDIT: Even a function which, given the Name of an operator, could give its precedence and associativity, would be very useful.

jmite
  • 8,171
  • 6
  • 40
  • 81

1 Answers1

3

I don't know if such a function exists, but you can write one yourself, using Scrap Your Boilerplate to get rid of all the boilerplate code. In particular, writing a function that gets applied to every UInfixE (when traversing a declaration top-down) is easy:

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 can be applied to anything that has the type Data, including Dec, Exp and other TH data types.

You probably don't care about the order of UInfixE, as the tree is ambiguous anyway and needs to be re-associated anyway. Going a bit further in this direction, we can enhance the function to fold over every tree of UInfixEs it finds:

-- | 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

This should allow you to build whatever tree-like structure you need to re-arrange the expression using appropriate precedences.

Petr
  • 62,528
  • 13
  • 153
  • 317