packed-data-0.2.0.0: src/Data/Packed/TH/Pattern.hs
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
module Data.Packed.TH.Pattern (genPatterns, patternFName, tagReaderFName) where
import Control.Monad (forM)
import Data.Packed.Reader (PackedFragment (..))
import Data.Packed.TH.Flag (PackingFlag (..))
import Data.Packed.TH.Utils
import GHC.Exts
import GHC.Ptr
import Language.Haskell.TH
-- | Generates patterns for packed values of the given type
--
-- __Example:__
--
-- For the 'Tree' data type, it generates the following patterns:
--
-- @
-- PackedLeaf :: (PackedFragment (a ': r)) -> PackedFragment (Tree a ': r)
--
-- PackedNode :: (PackedFragment (Tree A ': Tree a ': r)) -> PackedFragment (Tree a ': r)
-- @
--
-- More specifically, it generates the following code:
--
-- @
--
-- {-# INLINE readTreeTag #-}
-- readTreeTag :: PackedFragment (Tree a ': r) -> (# Word8#, PackedFragment n #)
-- readTreeTag (PF ptr@(Ptr addr) i) = case readWord8OffAddr# addr 0# realWorld# of
-- (# _, t #) -> case t of
-- 0 -> (# t, PF (ptr `plusPtr` 1) (i - 1) #)
-- 1 -> (# t, PF (ptr `plusPtr` 1) (i - 1) #)
-- _ -> error $ "Bag tag: Got " ++ show (W8# t)
--
-- {-# INLINE PackedLeaf #-}
-- pattern PackedLeaf :: PackedFragment (a ': r) -> 'PackedFragment' (Tree a ': r)
-- pattern PackedLeaf pf <- (readTreeTag -> (# 0, pf #))
--
-- {-# INLINE PackedNode #-}
-- pattern PackedNode :: 'PackedFragment' (Tree a ': Tree a ': r) -> 'PackedFragment' (Tree a ': r)
-- pattern PackedNode pf <- (readTreeTag -> (# 1, pf #))
--
-- {-# COMPLETE PackedLeaf, PackedNode #-}
-- @
genPatterns :: [PackingFlag] -> Name -> Q [Dec]
genPatterns flags tyName = do
(TyConI (DataD _ _ _ _ cs _)) <- reify tyName
patterns <- concat <$> forM (zip cs [0 ..]) (uncurry (genPattern flags tyName))
tagReader <- genTagReader flags tyName
let completePragma =
let
patternNames = patternFName . fst . getNameAndBangTypesFromCon <$> cs
in
CompleteP patternNames Nothing
return $ tagReader ++ patterns ++ [PragmaD completePragma]
genPattern :: [PackingFlag] -> Name -> Con -> Integer -> Q [Dec]
genPattern flags tyName con conIdx = do
let patternName = patternFName $ fst $ getNameAndBangTypesFromCon con
pfName <- newName "pf"
(ty, _) <- resolveAppliedType tyName
rVar <- newName "r"
conArgTypes <- getBranchTyList con flags
patternType <- do
patternFieldType <- foldr (\curr rest -> [t|$(return curr) ': $rest|]) (varT rVar) conArgTypes
fromType <- [t|$(return ty) ': $(varT rVar)|]
[t|PackedFragment ($(return patternFieldType)) -> PackedFragment ($(return fromType))|]
let patSynSig =
PatSynSigD
patternName
patternType
patSyn <- do
let viewPat = [p|(# $(litP $ WordPrimL conIdx), $(varP pfName) #)|]
patSynD
patternName
(prefixPatSyn [pfName])
unidir
(viewP (varE $ tagReaderFName tyName) viewPat)
return
[ PragmaD (InlineP patternName Inline FunLike AllPhases)
, patSynSig
, patSyn
]
patternFName :: Name -> Name
patternFName name = mkName $ "Packed" ++ sanitizeConName name
genTagReader :: [PackingFlag] -> Name -> Q [Dec]
genTagReader flags tyName = do
rType <- newName "r"
(ty, _) <- resolveAppliedType tyName
conTypes <- getBranchesTyList tyName flags
let fName = tagReaderFName tyName
prototype <-
let
inType = [t|PackedFragment ($(return ty) ': $(varT rType))|]
outType = [t|(# Word#, PackedFragment $(varT $ mkName "n") #)|]
in
[t|$inType -> $outType|]
(pat, body) <- do
let addrName = mkName "addr"
ptrName = mkName "ptr"
lengthName = mkName "l"
tagVarName = mkName "t"
readWordExp <- [|readWord8OffAddr# $(varE addrName) 0# realWorld#|]
caseBranches <- forM (zip [0 ..] conTypes) $ \(conIdx, _) -> do
tName <- newName "t"
let pat = AsP tName (LitP $ WordPrimL conIdx)
branchExp <- [|(# $(varE tName), PF ($(varE ptrName) `plusPtr` 1) ($(varE lengthName) - 1) #)|]
return $ Match pat (NormalB branchExp) []
errorBranch <- do
tName <- newName "t"
match (asP tName wildP) (normalB [|error $ "Bad tag, got " ++ show (W# $(varE tName))|]) []
let caseExpr = caseE [|word8ToWord# $(varE tagVarName)|] (return <$> (caseBranches ++ [errorBranch]))
body <- [|case $(return readWordExp) of (# _, $(varP tagVarName) #) -> $caseExpr|]
pattern_ <- do
ptrPattern <- asP ptrName [p|Ptr $(varP addrName)|]
[p|(PF $(return ptrPattern) $(varP lengthName))|]
return (pattern_, body)
return
[ PragmaD (InlineP fName Inline FunLike AllPhases)
, SigD fName prototype
, FunD fName [Clause [pat] (NormalB body) []]
]
tagReaderFName :: Name -> Name
tagReaderFName tyName = mkName $ "read" ++ sanitizeConName tyName ++ "Tag"