packed-data-0.2.0.0: src/Data/Packed/TH/Case.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ExtendedLiterals #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UnboxedTuples #-}
module Data.Packed.TH.Case (caseFName, genCase) where
import Data.Packed.Reader hiding (return)
import Data.Packed.TH.Flag
import Data.Packed.TH.Pattern (patternFName)
import Data.Packed.TH.Utils (getBranchesTyList, getNameAndBangTypesFromCon, resolveAppliedType, sanitizeConName)
import Language.Haskell.TH
caseFName :: Name -> Name
caseFName tyName = mkName $ "case" ++ sanitizeConName tyName
-- TODO Use generated patterns
-- | Generates a function to allow pattern matching a packed data type using the data constructors
--
-- __Example:__
--
-- For the 'Tree' data type, it generates the following function:
--
-- @
-- caseTree ::
-- ('Data.Packed.PackedReader' '[a] r b) ->
-- ('Data.Packed.PackedReader' '[Tree a, Tree a] r b) ->
-- 'Data.Packed.PackedReader' '[Tree a] r b
-- caseTree leafCase nodeCase = 'Data.Packed.Reader.mkPackedReader' $ \case
-- PackedLeaf l -> 'runReaderStep' leafCase l
-- PackedNode l -> 'runReaderStep' nodeCase l
-- @
genCase ::
[PackingFlag] ->
-- | The name of the type to generate the function for
Name ->
Q [Dec]
genCase flags tyName = do
(TyConI (DataD _ _ _ _ cs _)) <- reify tyName
-- For each data constructor, we build names for the pattern for the case functions
-- Example: leafCase, nodeCase, etc.
let casePatterns = buildCaseCallbackNameAndPattern <$> cs
body <- [|PackedReader $ $(buildCaseExpression casePatterns)|]
signature <- genCaseSignature flags tyName
return
[ PragmaD $ InlineP (caseFName tyName) Inline FunLike AllPhases
, signature
, FunD
(caseFName tyName)
[Clause (VarP . snd <$> casePatterns) (NormalB body) []]
]
where
-- for dataconstructor Leaf, will be 'leafCase'
buildCaseCallbackNameAndPattern = (\name -> (patternFName name, conNameToCaseFunctionName name)) . fst . getNameAndBangTypesFromCon
conNameToCaseFunctionName conName = mkName $ 'c' : (sanitizeConName conName) ++ "Case"
-- Build the case .. of ... expression using the list of available xxxCase, the flag and bytestring
buildCaseExpression :: [(Name, Name)] -> Q Exp
buildCaseExpression casePatterns =
-- For each xxxCase, we build a branch for the case expression
let matches =
( \(patternName, caseCallbackName) -> do
let r = mkName "r"
body <- [|runReaderStep $(varE caseCallbackName) $(varE r)|]
let pat = ConP patternName [] [VarP r]
return $ Match pat (NormalB body) []
)
<$> casePatterns
in lamCaseE matches
-- For a type 'Tree', generates the following signature
-- caseTree ::
-- ('Data.Packed.PackedReader' '[a] r b) ->
-- ('Data.Packed.PackedReader' '[Tree a, Tree a] r b) ->
-- 'Data.Packed.PackedReader' '[Tree a] r b
genCaseSignature :: [PackingFlag] -> Name -> Q Dec
genCaseSignature flags tyName = do
(sourceType, _) <- resolveAppliedType tyName
bVar <- newName "b"
rVar <- newName "r"
branchesTypes <- getBranchesTyList tyName flags
let
bType = varT bVar
rType = varT rVar
lambdaTypes = (\branchTypes -> buildLambdaType branchTypes bType rType) <$> branchesTypes
outType = [t|PackedReader '[$(return sourceType)] $rType $bType|]
signature <- foldr (\lambda out -> [t|$lambda -> $out|]) outType lambdaTypes
return $ SigD (caseFName tyName) signature
where
-- From a constructor (say Leaf a), build type PackedReader '[a] r b
buildLambdaType :: [Type] -> Q Type -> Q Type -> Q Type
buildLambdaType branchType returnType restType = do
let branchTypeList = foldr (\a rest -> [t|$(return a) ': $rest|]) [t|'[]|] branchType
[t|PackedReader $branchTypeList $restType $returnType|]