{-# Language TemplateHaskell #-}
module Text.LLVM.Labels.TH (generateRelabel) where
import Control.Monad (zipWithM)
import Language.Haskell.TH
import Language.Haskell.TH.Datatype
generateRelabel :: Name -> Name -> ExpQ
generateRelabel relabel dataCon =
do di <- reifyDatatype dataCon
generateRelabelData di (varE relabel)
generateRelabelData :: DatatypeInfo -> ExpQ -> ExpQ
generateRelabelData di relabelE =
[| \f x -> $(caseE [| x |] (mkMatch [| f |] <$> cons)) |]
where
mkMatch = generateRelabelCon lastArg relabelE
lastArg = tvName (last (datatypeVars di))
cons = datatypeCons di
generateRelabelCon ::
Name ->
ExpQ ->
ExpQ ->
ConstructorInfo ->
MatchQ
generateRelabelCon lastArg relabelE fE ci =
do names <- nameThings "x" (constructorFields ci)
match
(conP cn (map (varP . fst) names))
(normalB (bodyExp cn (map gen names)))
[]
where
cn = constructorName ci
gen :: (Name, Type) -> Either ExpQ ExpQ
gen (n,t) =
let nE = varE n in
case generateRelabelField lastArg fE relabelE t of
Just f -> Right [| $f $nE |]
Nothing -> Left nE
bodyExp ::
Name ->
[Either ExpQ ExpQ] ->
ExpQ
bodyExp conname fields = liftAE conLike updates
where
updates = [r | Right r <- fields]
conLike =
do names <- map fst <$> nameThings "y" updates
lamE
(map varP names)
(appsE (conE conname : replaceRights (map varE names) fields))
replaceRights ::
[a] ->
[Either a b] ->
[a]
replaceRights xs (Left y : ys) = y : replaceRights xs ys
replaceRights (x:xs) (Right _ : ys) = x : replaceRights xs ys
replaceRights [] [] = []
replaceRights _ _ = error "Text.LLVM.Labels.TH.replaceRights: PANIC"
generateRelabelField ::
Name ->
ExpQ ->
ExpQ ->
Type ->
Maybe ExpQ
generateRelabelField lastArg fE relabelE t =
case typeDepth t of
(n, VarT tn) | tn == lastArg -> Just (exprs !! n)
_ -> Nothing
where
exprs = [| $fE Nothing |] : iterate traverseE [| $relabelE $fE |]
typeDepth ::
Type ->
(Int, Type)
typeDepth = go 0
where
go i (AppT _ x) = go (i+1) x
go i t = (i, t)
nameThings ::
String ->
[a] ->
Q [(Name, a)]
nameThings base xs = zipWithM nameThing [0 :: Int ..] xs
where
nameThing i x = do n <- newName (base ++ show i); return (n,x)
traverseE ::
ExpQ ->
ExpQ
traverseE e = [| traverse $e |]
liftAE :: ExpQ -> [ExpQ] -> ExpQ
liftAE c [] = [| pure $c |]
liftAE c (x:xs) = foldl (\f e -> [| $f <*> $e |]) [| $c <$> $x |] xs