packed-data-0.2.0.0: src/Data/Packed/TH/Transform.hs
module Data.Packed.TH.Transform (transformFName, genTransform) where
import Control.Monad
import Data.Maybe (catMaybes)
import Data.Packed.FieldSize (FieldSize)
import qualified Data.Packed.Needs as N
import Data.Packed.Reader (PackedReader)
import qualified Data.Packed.Reader as R
import Data.Packed.TH.Case (caseFName)
import Data.Packed.TH.Flag
import Data.Packed.TH.Start (startFName)
import Data.Packed.TH.Utils
import Language.Haskell.TH
-- | For a constructor 'Leaf', will generate the function name 'transformLeaf'
transformFName :: Name -> Name
transformFName conName = mkName $ "transform" ++ sanitizeConName conName
-- For a type 'Tree', generates the following function
--
-- transformTree ::
--
-- ('Data.Packed.Reader.PackedReader' '[a] r ('Data.Packed.Needs.NeedsBuilder' (a ': r1) '[Tree a] r1 '[Tree a])) ->
--
-- ('Data.Packed.Reader.PackedReader' '[Tree a, Tree a] r ('Data.Packed.Needs.NeedsBuilder' (Tree a ': Tree a ': r1) '[Tree a] r1 '[Tree a])) ->
-- 'Data.Packed.PackedReader' '[Tree a] r ('Data.Packed.NeedsBuilder' (Tree a ': r1) '[Tree a] r1 '[Tree a])
-- transformTree leafCase nodeCase = caseTree
-- (leafCase R.>>= \l -> return (startLeaf 'Data.Packed.Needs.>=>' l))
-- (nodeCase R.>>= \n -> return (startNode 'Data.Packed.Needs.>=>' n)))
genTransform :: [PackingFlag] -> Name -> Q [Dec]
genTransform flags tyName = do
signature <- genTransformSignature flags tyName
(TyConI (DataD _ _ _ _ cs _)) <- reify tyName
body <-
foldl
( \rest curr ->
let caseName = buildCaseFunctionName curr
in if not $ conHasArguments curr
then [|$rest (R.return $(varE (startFNameForCon curr)))|]
else
[|
$rest
( $(varE caseName)
R.>>= \resWriter ->
R.return
(($(varE (startFNameForCon curr)) N.>=> resWriter))
)
|]
)
(varE $ caseFName tyName)
cs
return
[ signature
, FunD
(transformFName tyName)
[Clause (VarP . buildCaseFunctionName <$> filter conHasArguments cs) (NormalB body) []]
]
where
-- for dataconstructor Leaf, will be 'leafCase'
buildCaseFunctionName = conNameToCaseFunctionName . fst . getNameAndBangTypesFromCon
conNameToCaseFunctionName conName = mkName $ "case_" ++ (sanitizeConName conName)
startFNameForCon = startFName . fst . getNameAndBangTypesFromCon
conHasArguments = not . null . snd . getNameAndBangTypesFromCon
-- For a type 'Tree', generates the following signature
-- transformTree ::
-- ('Data.Packed.Reader.PackedReader' '[a] r ('Data.Packed.Needs.NeedsBuilder' (a ': r1) '[Tree a] r1 '[Tree a])) ->
--
-- ('Data.Packed.Reader.PackedReader' '[Tree a, Tree a] r ('Data.Packed.Needs.NeedsBuilder' (Tree a ': Tree a ': r1) '[Tree a] r1 '[Tree a])) ->
-- 'Data.Packed.PackedReader' '[Tree a] r ('Data.Packed.NeedsBuilder' (Tree a ': r1) '[Tree a] r1 '[Tree a])
genTransformSignature :: [PackingFlag] -> Name -> Q Dec
genTransformSignature flags tyName = do
(sourceType, _) <- resolveAppliedType tyName
(TyConI (DataD _ _ _ _ cs _)) <- reify tyName
rVar <- newName "r"
r1Var <- newName "r1"
let
rType = varT rVar
r1Type = varT r1Var
lambdaTypes = (\c -> buildLambdaType c sourceType rType r1Type) <$> cs
outType =
[t|
PackedReader
'[$(return sourceType)]
$rType
(N.NeedsBuilder ($(return sourceType) ': $(r1Type)) '[$(return sourceType)] $(r1Type) '[$(return sourceType)])
|]
signature <- foldr (\lambda out -> [t|$lambda -> $out|]) outType (catMaybes lambdaTypes)
return $ SigD (transformFName tyName) signature
where
-- From a constructor (say Leaf a), build type PackedTransformer a r
buildLambdaType con ty restType rest1Type =
if null fieldType
then Nothing
else return $ do
[readerType, builderType] <- forM [[t|'[]|], rest1Type] $ \r ->
foldr
( \(fieldTy, _, needsFS) tys ->
if needsFS
then [t|FieldSize ': $(return fieldTy) ': $tys|]
else [t|$(return fieldTy) ': $tys|]
)
r
fieldType
[t|
PackedReader
$(return readerType)
$restType
(N.NeedsBuilder $(return builderType) '[$(return ty)] $(rest1Type) '[$(return ty)])
|]
where
fieldType = getConFieldsIdxAndNeedsFS con flags