data-flags-0.0.3.3: src/Data/Flags/TH.hs
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
-- | Template Haskell utils for declaring flags instances.
module Data.Flags.TH
( dataBitsAsFlags
, dataBitsAsBoundedFlags
, bitmaskWrapper
, enumADT
) where
import Language.Haskell.TH
import Data.Bits (Bits(..))
#if MIN_VERSION_base(4,7,0)
import Data.Bits (FiniteBits(..))
#endif
import Data.Maybe (isJust)
import Data.List (find, union, intercalate)
import Foreign.Storable (Storable(..))
import Foreign.Ptr (Ptr, castPtr)
import Control.Applicative ((<$>))
import Data.Flags.Base
#if !MIN_VERSION_base(4,7,0)
finiteBitSize ∷ Bits α ⇒ α → Int
finiteBitSize = bitSize
#endif
inst ∷ Name → Name → [Dec] → Dec
inst className typeName = InstanceD
#if MIN_VERSION_template_haskell(2,11,0)
Nothing
#endif
[] (AppT (ConT className) (ConT typeName))
fun ∷ Name → Exp → Dec
fun name expr = FunD name [Clause [] (NormalB expr) []]
-- | Produces 'Data.Flags.Base.Flags' instance declaration for the specified
-- instance of 'Data.Bits.Bits'.
dataBitsAsFlags ∷ Name → Q [Dec]
dataBitsAsFlags typeName = do
noFlagsE ← appE (varE 'fromInteger) (litE $ IntegerL 0)
andFlagsE ← [| (.|.) |]
commonFlagsE ← [| (.&.) |]
butFlagsE ← [| \x → \y → x .&. (complement y) |]
return [inst ''Flags typeName
[fun 'noFlags noFlagsE,
fun 'andFlags andFlagsE,
fun 'commonFlags commonFlagsE,
fun 'butFlags butFlagsE]]
-- | Produces 'Data.Flags.Base.Flags' and 'Data.Flags.Base.BoundedFlags'
-- instances declarations for the specified instance of 'Data.Bits.Bits'.
dataBitsAsBoundedFlags ∷ Name → Q [Dec]
dataBitsAsBoundedFlags typeName = do
allFlagsE ← appE (varE 'fromInteger) (litE $ IntegerL (-1))
enumFlagsE ← [| \x → map (setBit 0) $
filter (testBit x) [0 .. finiteBitSize x - 1] |]
(++ [inst ''BoundedFlags typeName
[fun 'allFlags allFlagsE,
fun 'enumFlags enumFlagsE]]) <$> dataBitsAsFlags typeName
-- | Declare a newtype wrapper around the specified integral type and make
-- it an instance of 'Data.Flags.Base.BoundedFlags'. For each individual
-- flag declare a constant. If a 'Show' instance wasn't requested for
-- automatic derivation, declare one with
--
-- > show flags = "WrappingTypeName [IndividualFlags in flags]"
bitmaskWrapper ∷ String -- ^ Wrapping type name.
→ Name -- ^ Wrapped type name.
→ [Name] -- ^ Types to derive automatically.
→ [(String, Integer)] -- ^ Individual flags.
→ Q [Dec]
bitmaskWrapper typeNameS wrappedName derives elems = do
typeName ← return $ mkName typeNameS
showE ← [| \flags → $(stringE $ typeNameS ++ " [") ++
(intercalate ", " $ map snd $
filter ((noFlags /=) . commonFlags flags . fst) $
$(listE $
map (\(name, _) →
tupE [varE $ mkName name,
stringE name])
elems)) ++ "]" |]
allFlagsE ← [| foldl andFlags noFlags
$(listE $ map (varE . mkName . fst) elems) |]
enumFlagsE ← [| \flags → filter ((noFlags /=) . commonFlags flags) $
$(listE $ map (varE . mkName . fst) elems) |]
let strictness =
#if MIN_VERSION_template_haskell(2,11,0)
Bang NoSourceUnpackedness NoSourceStrictness
#else
NotStrict
#endif
return $ [ NewtypeD [] typeName []
#if MIN_VERSION_template_haskell(2,11,0)
Nothing
#endif
(NormalC typeName [(strictness, ConT wrappedName)])
#if MIN_VERSION_template_haskell(2,11,0)
. fmap ConT $
#endif
(union [''Eq, ''Flags] derives)
] ++
(concatMap (\(nameS, value) →
let name = mkName nameS in
[SigD name (ConT typeName),
FunD name
[Clause [] (NormalB $
AppE (ConE typeName)
(LitE $ IntegerL value))
[]]]) elems) ++
[inst ''BoundedFlags typeName
[fun 'allFlags allFlagsE,
fun 'enumFlags enumFlagsE]] ++
(if (isJust $ find (''Show ==) derives)
then []
else [inst ''Show typeName [fun 'show showE]])
-- | Declare an ADT with the specified constructors and make it an instance
-- of 'Eq', 'Ord', 'Show' and 'Foreign.Storable.Storable'.
enumADT ∷ String -- ^ Type name.
→ Name -- Numeric type name.
→ [(String, Integer)] -- ^ Enumeration elements.
→ Q [Dec]
enumADT typeNameS numName elems = do
let typeName = mkName typeNameS
wrap i = caseE (varE i) $
(map (\(name, value) →
match (litP $ IntegerL value)
(normalB $ appE (conE 'Just)
(conE $ mkName name))
[]) elems) ++
[match wildP (normalB $ conE 'Nothing) []]
unwrap w = caseE (varE w)
(map (\(name, value) →
match (conP (mkName name) [])
(normalB $ litE $ IntegerL value)
[]) elems) in do
alignmentE ← [| \_ → alignment (undefined ∷ $(conT numName)) |]
sizeOfE ← [| \_ → sizeOf (undefined ∷ $(conT numName)) |]
peekE ← [| \p → do
i ← peek (castPtr p ∷ Ptr $(conT numName))
case $(wrap 'i) of
Just w -> return w
Nothing -> fail $ "Invalid value for " ++ typeNameS |]
pokeE ← [| \p → \v → poke (castPtr p ∷ Ptr $(conT numName))
$(unwrap 'v) |]
return [DataD [] typeName []
#if MIN_VERSION_template_haskell(2,11,0)
Nothing
#endif
(map ((`NormalC` []) . mkName . fst) elems)
#if MIN_VERSION_template_haskell(2,11,0)
. fmap ConT $
#endif
[''Eq, ''Ord, ''Show],
inst ''Storable typeName
[fun 'alignment alignmentE,
fun 'sizeOf sizeOfE,
fun 'peek peekE,
fun 'poke pokeE]]