{-#LANGUAGE MagicHash, TemplateHaskell, DeriveDataTypeable, StandaloneDeriving, GeneralizedNewtypeDeriving #-} -- BangPatterns, ScopedTypeVariables, ViewPatterns, KindSignatures import Language.Haskell.TH.Syntax ( Exp(..), Pat(..), Stmt(..), Type(..), Dec(..), Range(..), Lit(..), Kind(..), Body(..), Guard(..), Con(..), Match(..), Name(..), mkName, NameFlavour(..), NameSpace(..), Clause(..), Pragma(..), FamFlavour(..), Pred(..), TyVarBndr(..), Foreign, Callconv(..), FunDep(..), Safety(..), Strict(..), InlineSpec(..)) -- testing-feat import Test.Feat import Test.Feat.Access import Test.Feat.Modifiers -- template-haskell import Language.Haskell.TH.Syntax.Internals(OccName(OccName), ModName(ModName), PkgName) import Language.Haskell.TH.Ppr(pprint,Ppr) -- haskell-src-meta import Language.Haskell.Meta(toExp) -- haskell-src-exts import qualified Language.Haskell.Exts as E -- quickcheck import Test.QuickCheck hiding (NonEmpty, (><)) --base import Data.Typeable(Typeable) import Data.Ord import Data.List -- smallcheck import Test.SmallCheck.Series hiding (Nat) import Test.SmallCheck {- -- Currently both of these spit out a lot of errors unless we disable a few of the -- buggier constructors (which we have done below). test_parsesAll = ioAll 15 report_parses -- | Test (at most) 10000 values of each size up to size 100. test_parsesBounded = ioBounded 10000 100 report_parses test_parsesBounded' = ioFeat (boundedWith enumerate 1000) report_parses report_parses e = case prop_parsesM e of Nothing -> return () Just s -> do putStrLn "Failure:" putStrLn (pprint e) print e putStrLn s putStrLn "" prop_parsesM e = case myParse $ pprint (e :: Exp) :: E.ParseResult E.Exp of E.ParseOk _ -> Nothing E.ParseFailed _ s -> Just s test_cycleAll = ioAll 15 report_cycle test_cycleBounded = ioBounded 10000 100 report_cycle report_cycle e = case prop_cycle e of Nothing -> return () Just (ee,ex) -> do putStrLn "Failure:" putStrLn (pprint ex) print ex putStrLn (E.prettyPrint ee) putStrLn "" -- Round-trip property: TH -> String -> HSE -> TH -- Uses haskell-src-meta for HSE -> TH prop_cycle :: Exp -> Maybe (E.Exp,Exp) prop_cycle e = case myParse $ pprint (e :: Exp) :: E.ParseResult E.Exp of E.ParseOk hse -> if e == toExp hse then Nothing else Just $ (hse, toExp hse) E.ParseFailed _ s -> Nothing -- Parse failures do not count as errors! -- Haskell parser myParse :: String -> E.ParseResult E.Exp myParse = E.parseWithMode E.defaultParseMode{E.extensions = [ E.BangPatterns , E.ScopedTypeVariables , E.ViewPatterns , E.KindSignatures , E.ExplicitForAll , E.TypeFamilies ]} -} -- We define both SmallCheck and Feat enumerators for comparison. c1 :: (Serial a, Enumerable a) => (a -> b) -> (Enumerate b, Series b) c1 f = (unary f,cons1 f) c0 f = (nullary f, cons0 f) instance (Serial a, Serial b) => Serial (FreePair a b) where series = map Free . (series >< series) coseries = undefined toSel :: [(Enumerate b, Series b)] -> Enumerate b toSel xs = consts $ map fst xs toSerial :: [(Enumerate b, Series b)] -> Series b toSerial xs = foldl1 (\/) $ map snd xs -- These statements are always expressions newtype ExpStmt = ExpStmt Exp deriving Typeable -- Declarations allowed in where clauses newtype WhereDec = WhereDec{unWhere :: Dec} deriving Typeable -- Lowecase names newtype LcaseN = LcaseN {lcased :: Name} deriving Typeable -- Uppercase names newtype UpcaseName = UpcaseName {ucased :: Name} deriving Typeable newtype BindN = BindN Name deriving Typeable instance (Enumerable a, Serial a) => Serial (NonEmpty a) where series = toSerial [c1 $ NonEmpty . funcurry (:)] coseries = undefined instance (Serial a, Infinite a) => Serial (Nat a) where series = map (\(N a) -> Nat a) . series coseries = undefined newtype CPair a b = CPair {cPair :: (a,b)} deriving Typeable instance (Enumerable a, Serial a,Enumerable b, Serial b) => Serial (CPair a b) where series = toSerial [c1 $ CPair . funcurry (,)] coseries = undefined instance (Serial a,Enumerable a,Enumerable b, Serial b) => Enumerable (CPair a b) where enumerate = toSel [c1 $ CPair . funcurry (,)] cExp = [c1 $ VarE . lcased ,c1 $ ConE . ucased ,c1 LitE ,c1 $ funcurry AppE ,c1 $ \(ExpStmt a,o) -> InfixE (Just a) (either ConE VarE o) Nothing ,c1 $ \(ExpStmt a,o) -> InfixE Nothing (either ConE VarE o) (Just a) ,c1 $ \(a,o,b) -> InfixE (Just a) (either ConE VarE o) (Just b) -- ,c1 $ funcurry $ funcurry $ \a o b -> UInfixE a (VarE o) b -- ,c1 $ funcurry $ funcurry $ \a o b -> UInfixE a (ConE o) b -- ,c1 ParensE ,c1 $ funcurry $ LamE . nonEmpty ,c1 $ \(x1,x2,xs) -> TupE (x1:x2:xs) -- ,c1 UnboxedTupE ,c1 $ funcurry $ funcurry CondE ,c1 $ \(d,ds,e) -> LetE (map unWhere $ d:ds) e -- DISABLED BUGGY EMPTY LETS ,c1 $ \(e,NonEmpty m) -> CaseE e m ,c1 $ \(e,ss) -> DoE (ss ++ [NoBindS e]) ,c1 $ (\((p,e),(CPair (xs,e'))) -> CompE ([BindS p e] ++ xs ++ [NoBindS e'])) -- ,c1 ArithSeqE -- BUGGY! ,c1 ListE -- ,c1 $ funcurry SigE -- BUGGY! ,c1 $ \(e,x) -> RecConE e $ map unCase (nonEmpty x) ,c1 $ \(e,fe) -> RecUpdE e $ map unCase (nonEmpty fe) ] instance Enumerable Exp where enumerate = toSel cExp instance Serial Exp where series = toSerial cExp coseries = undefined unCase (LcaseN n,e) = (n,e) cExpStmt = [ c1 $ ExpStmt . VarE , c1 $ ExpStmt . ConE , c1 $ ExpStmt . LitE , c1 $ \(e1,e2) -> ExpStmt (AppE e1 e2) , c1 $ ExpStmt . LitE -- , c1 parS -- Removed paralell comprehensions ] instance Enumerable ExpStmt where enumerate = toSel cExpStmt instance Serial ExpStmt where series = toSerial cExpStmt coseries = undefined cPat = [ c1 LitP , c1 $ \(BindN n) -> VarP n , c1 TupP , c1 $ \(UpcaseName n,ps) -> ConP n ps , c1 $ \(p1,UpcaseName n,p2) -> InfixP p1 n p2 , c1 TildeP -- , c1 $ \(LcaseN n) -> BangP $ VarP n , c1 $ \(BindN n,p) -> AsP n p , c0 WildP , c1 $ \(UpcaseName e,x) -> RecP e (map (\(BindN n, p) -> (n,p)) (nonEmpty x)) , c1 ListP -- , c1 $ funcurry SigP -- BUGGY! -- , c1 $ funcurry ViewP -- BUGGY! ] instance Enumerable Pat where enumerate = toSel cPat instance Serial Pat where series = toSerial cPat coseries = undefined -- deriveEnumerable ''Match -- Should remove decs cMatch = [c1 $ funcurry $ funcurry $ \x y ds -> Match x y (map unWhere ds) ] instance Enumerable Match where enumerate = toSel cMatch instance Serial Match where series = toSerial cMatch coseries = undefined cStmt = [ c1 $ funcurry BindS , c1 $ \(d) -> LetS $ map unWhere $ nonEmpty d , c1 $ NoBindS -- , c1 parS -- Removed paralell comprehensions ] instance Enumerable Stmt where enumerate = toSel cStmt instance Serial Stmt where series = toSerial cStmt coseries = undefined cName = [ c1 (funcurry Name) ] instance Enumerable Name where enumerate = toSel cName instance Serial Name where series = toSerial cName coseries = undefined cType = [c1 $ funcurry $ funcurry $ (\(x) -> ForallT (nonEmpty x)) ,c1 $ \(BindN a) -> VarT a ,c1 $ \(UpcaseName a) -> ConT a ,c1 $ \n -> TupleT (abs n) ,c0 ArrowT ,c0 ListT ,c1 $ funcurry AppT -- ,c1 $ funcurry SigT -- BUGGY! ] instance Enumerable Type where enumerate = toSel cType instance Serial Type where series = toSerial cType coseries = undefined -- deriveEnumerable ''Dec cWhereDec = [ c1 $ \(n,c) -> WhereDec $ FunD n (nonEmpty c) , c1 $ \(n,p,wds) -> WhereDec $ ValD n p (map unWhere wds) , c1 $ \(BindN a,b) -> WhereDec $ SigD a b -- , c1 $ WhereDec . PragmaD -- Removed pragmas -- , c1 parS -- Removed paralell comprehensions ] instance Enumerable WhereDec where enumerate = toSel cWhereDec instance Serial WhereDec where series = toSerial cWhereDec coseries = undefined cLit = [ c1 StringL , c1 CharL -- TODO: Fair char generation , c1 $ IntegerL . nat -- , c1 RationalL -- BUGGY! -- Removed primitive litterals ] instance Enumerable Lit where enumerate = toSel cLit instance Serial Lit where series = toSerial cLit coseries = undefined cClause = [c1 $ funcurry (funcurry $ \ps bs ds -> Clause ps bs (map unWhere ds))] instance Enumerable Clause where enumerate = toSel cClause instance Serial Clause where series = toSerial cClause coseries = undefined -- deriveEnumerable ''Pred cPred = [ c1 $ funcurry ClassP , c1 $ funcurry EqualP ] instance Enumerable Pred where enumerate = toSel cPred instance Serial Pred where series = toSerial cPred coseries = undefined -- deriveEnumerable ''TyVarBndr cTyVarBndr = [ c1 $ PlainTV , c1 $ funcurry KindedTV ] instance Enumerable TyVarBndr where enumerate = toSel cTyVarBndr instance Serial TyVarBndr where series = toSerial cTyVarBndr coseries = undefined cKind = [c0 StarK ,c1 (funcurry ArrowK) ] instance Enumerable Kind where enumerate = toSel cKind instance Serial Kind where series = toSerial cKind coseries = undefined cBody = [ c1 NormalB , c1 $ \(x) -> GuardedB (nonEmpty x) -- Removed primitive litterals ] instance Enumerable Body where enumerate = toSel cBody instance Serial Body where series = toSerial cBody coseries = undefined cGuard = [c1 $ NormalG ,c1 $ \(s) -> PatG (nonEmpty s) ] instance Enumerable Guard where enumerate = toSel cGuard instance Serial Guard where series = toSerial cGuard coseries = undefined cCallconv = [c0 CCall, c0 StdCall] instance Enumerable Callconv where enumerate = toSel cCallconv instance Serial Callconv where series = toSerial cCallconv coseries = undefined cSafety = [c0 Unsafe, c0 Safe, c0 Interruptible] instance Enumerable Safety where enumerate = toSel cSafety instance Serial Safety where series = toSerial cSafety coseries = undefined cStrict = [c0 IsStrict, c0 NotStrict, c0 Unpacked] instance Enumerable Strict where enumerate = toSel cStrict instance Serial Strict where series = toSerial cStrict coseries = undefined cInlineSpec = [c1 (funcurry $ funcurry $ InlineSpec)] instance Enumerable InlineSpec where enumerate = toSel cInlineSpec instance Serial InlineSpec where series = toSerial cInlineSpec coseries = undefined cOccName = [ c0 $ OccName "Con" , c0 $ OccName "var" ] instance Enumerable OccName where enumerate = toSel cOccName instance Serial OccName where series = toSerial cOccName coseries = undefined cBindN = [c0 $ BindN $ Name (OccName "var") NameS] instance Enumerable BindN where enumerate = toSel cBindN instance Serial BindN where series = toSerial cBindN coseries = undefined cLcaseN = [c1 $ \nf -> LcaseN $ Name (OccName "var") nf] instance Enumerable LcaseN where enumerate = toSel cLcaseN instance Serial LcaseN where series = toSerial cLcaseN coseries = undefined cUpcaseName = [c1 $ \nf -> UpcaseName $ Name (OccName "Con") nf] instance Serial UpcaseName where series = toSerial cUpcaseName coseries = undefined instance Enumerable UpcaseName where enumerate = toSel cUpcaseName cModName = [c0 $ ModName "M", c0 $ ModName "C.M"] instance Enumerable ModName where enumerate = toSel cModName instance Serial ModName where series = toSerial cModName coseries = undefined cRange = [ c1 FromR , c1 (funcurry FromThenR) , c1 (funcurry FromToR) , c1 (funcurry $ funcurry FromThenToR) ] instance Enumerable Range where enumerate = toSel cRange instance Serial Range where series = toSerial cRange coseries = undefined cNameFlavour = ( [ c1 NameQ -- , funcurry $ funcurry NameG -- , \(I# x) -> NameU x -- , \(I# x) -> NameL x , c0 NameS ]) instance Enumerable NameFlavour where enumerate = toSel cNameFlavour instance Serial NameFlavour where series = toSerial cNameFlavour coseries = undefined -- main = test_parsesBounded -- or test_parsesAll, but that takes much longer to find bugs eExp :: Enumerate Exp eExp = toSel cExp