{-# 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