{-# LANGUAGE TemplateHaskell #-}
module Test.QuickCheck.TH.Generators.Internal (makeArbitrary) where
import Data.Monoid ((<>))
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Test.QuickCheck
import Test.QuickCheck.TH.Generators.Internal.BuildArbitrary
-- | create buildArb1 through buildArb20 automatically
$(buildArbAny 1)
$(buildArbAny 2)
$(buildArbAny 3)
$(buildArbAny 4)
$(buildArbAny 5)
$(buildArbAny 6)
$(buildArbAny 7)
$(buildArbAny 8)
$(buildArbAny 9)
$(buildArbAny 10)
$(buildArbAny 11)
$(buildArbAny 12)
$(buildArbAny 13)
$(buildArbAny 14)
$(buildArbAny 15)
$(buildArbAny 16)
$(buildArbAny 17)
$(buildArbAny 18)
$(buildArbAny 19)
$(buildArbAny 20)
-- | Boilerplate for top level splices.
--
-- The given 'Name' must be from a type constructor. Furthermore, the
-- type constructor must be either a data type or a newtype. Any other
-- value will result in an exception.
withType :: Name
-> ([TyVarBndr] -> [Con] -> Q a)
-- ^ Function that generates the actual code. Will be applied
-- to the type variable binders and constructors extracted
-- from the given 'Name'.
-> Q a
-- ^ Resulting value in the 'Q'uasi monad.
withType name f = do
info <- reify name
case info of
TyConI dec ->
case dec of
DataD _ _ tvbs _ cons' _ -> f tvbs cons'
NewtypeD _ _ tvbs _ con _ -> f tvbs [con]
other -> error $ "Example.TH.withType: Unsupported type: "
++ show other
_ -> error "Example.TH.withType: I need the name of a type."
-- | Extracts the name from a constructor.
-- | Make a ('Gen' a) for type 'a'
-- Currently support arbitrary Sum types up to 7 params
-- per constructor.
--
-- Record Types not currently supported
makeArbitrary :: Name -> Q [Dec]
makeArbitrary n = withType n runConstructionApp
where
runConstructionApp _ con = do
dec <- applyCon n con
return dec
-- | build the function taht applys the type constructor
applyCon :: Name -> [Con] -> DecsQ
applyCon n cons' = sequence [signature,value]
where
signature = sigD finalFunctionName (appT (conT ''Gen) (conT n))
value = valD (varP finalFunctionName) (normalB (makeArbList cons')) []
finalFunctionName = mkName ("arbitrary" <> nameBase n)
-- | select one of the list of generators
-- Q Exp == oneOf [Gen *]
makeArbList :: [Con] -> Q Exp
makeArbList cons' = appE (varE 'oneof)
(listE $ asNormalOrRecC applyConExp cons' )
-- | Normal Constructors are the only ones we are considering
asNormalOrRecC :: ((Name, [StrictType]) -> a) -> [Con] -> [a]
asNormalOrRecC f cons' = foldr decodeC [] cons'
where
decodeC (RecC n l) lst = (f (n, varStrictToStrict <$> l)) : lst
decodeC (NormalC n l) lst = (f (n, l)) : lst
decodeC _ lst = lst
varStrictToStrict (_ , s,t) = (s,t)
-- | This is where we run the sum type thing
-- Q Exp
applyConExp :: (Name, [StrictType]) -> ExpQ
applyConExp deconstructedConstructor = runMapAndApp argCount
where
conName = fst deconstructedConstructor
argCount = fromIntegral . length . snd $ deconstructedConstructor :: Integer
runMapAndApp :: Integer -> ExpQ
runMapAndApp 0 = appE (varE 'arbReturn ) (conE conName)
runMapAndApp 1 = appE (varE 'buildArb1 ) (conE conName)
runMapAndApp 2 = appE (varE 'buildArb2 ) (conE conName)
runMapAndApp 3 = appE (varE 'buildArb3 ) (conE conName)
runMapAndApp 4 = appE (varE 'buildArb4 ) (conE conName)
runMapAndApp 5 = appE (varE 'buildArb5 ) (conE conName)
runMapAndApp 6 = appE (varE 'buildArb6 ) (conE conName)
runMapAndApp 7 = appE (varE 'buildArb7 ) (conE conName)
runMapAndApp 8 = appE (varE 'buildArb8 ) (conE conName)
runMapAndApp 9 = appE (varE 'buildArb9 ) (conE conName)
runMapAndApp 10 = appE (varE 'buildArb10 ) (conE conName)
runMapAndApp 11 = appE (varE 'buildArb11 ) (conE conName)
runMapAndApp 12 = appE (varE 'buildArb12 ) (conE conName)
runMapAndApp 13 = appE (varE 'buildArb13 ) (conE conName)
runMapAndApp 14 = appE (varE 'buildArb14 ) (conE conName)
runMapAndApp 15 = appE (varE 'buildArb15 ) (conE conName)
runMapAndApp 16 = appE (varE 'buildArb16 ) (conE conName)
runMapAndApp 17 = appE (varE 'buildArb17 ) (conE conName)
runMapAndApp 18 = appE (varE 'buildArb18 ) (conE conName)
runMapAndApp 19 = appE (varE 'buildArb19 ) (conE conName)
runMapAndApp 20 = appE (varE 'buildArb20 ) (conE conName)
runMapAndApp _ = error "Arbitrary TypeConstructors only defined for 0 to 20 parameters"
{- attempting to automate it further
applyConExp :: (Name, [StrictType]) -> ExpQ
applyConExp deconstructedConstructor = -- runMapAndApp argCount
case (argCount >= 0) && (argCount <= 20) of
True -> do
mBuildArbn <- lookupValueName buildArb
case mBuildArbn of
Nothing -> error "Could not find buildArbn function, TH error"
Just buildArbn -> appE (varE buildArbn) (conE conName)
False -> error "Arbitrary TypeConstructors only defined for 0 to 20 parameters"
where
conName = fst deconstructedConstructor
argCount = fromIntegral . length . snd $ deconstructedConstructor :: Int
buildArb = "buildArb" ++ show argCount
-}
arbReturn :: a -> Gen a
arbReturn = return