module Test.Sloth.Refine
(
Ref(..), ResRef, ArgRef, ResPos(..), ArgPos(..),
refineTree,
fmapArgRefs, fmapResRefs,
cut, bfs, dfs, coSeq,
A(..)
) where
import Control.Applicative
import Data.Data
import Control.Monad.State hiding ( lift )
import Control.Monad.Identity
import Test.Sloth.Pos ( Pos, (|>), bottom, root )
import Test.Sloth.Poly ( A(..), isDataTypeA, polyA )
import Test.Sloth.Function ( isDataTypeFun )
import Test.Sloth.Generics ( Compose(..), inCompose, gunfoldWithIndex )
import Test.Sloth.CoMonad
data Ref m a
= Ref a [ResRef m a]
| NoRef [Ref m a]
data ResPos a = ResPos { resPos :: Pos, resRefs :: a }
data ArgPos a = ArgPos { argPos :: Pos, argRefs :: a }
type ResRef m a = ResPos [m (ArgRef m a)]
type ArgRef m a = ArgPos [Ref m a]
fmapResRefs :: Functor f => (Ref f a -> Ref f b) -> [ResRef f a] -> [ResRef f b]
fmapResRefs f = map (fmap (fmapArgRefs f))
fmapArgRefs :: Functor f => (Ref f a -> Ref g b)
-> [f (ArgRef f a)] -> [f (ArgRef g b)]
fmapArgRefs f = map (fmap (fmap (map f)))
instance Functor ResPos where
fmap f (ResPos p x) = ResPos p (f x)
instance Functor ArgPos where
fmap f (ArgPos p x) = ArgPos p (f x)
instance Functor f => Functor (Ref f) where
fmap f (Ref v rs) = Ref (f v) (fmapResRefs (fmap f) rs)
fmap f (NoRef rs) = NoRef (map (fmap f) rs)
instance Functor f => Applicative (Ref f) where
pure v = Ref v []
r1@(Ref f fs) <*> r2@(Ref v rs) =
Ref (f v) (fmapResRefs (<*> r2) fs ++ fmapResRefs (r1 <*>) rs)
r1@(NoRef fs) <*> r2@(NoRef rs) =
NoRef (map (<*> r2) fs ++ map (r1 <*>) rs)
r1 <*> NoRef rs = NoRef (map (r1 <*>) rs)
NoRef fs <*> r2 = NoRef (map (<*> r2) fs)
refineTree :: Data a => Ref Identity a
refineTree = refineData root
#if MIN_VERSION_base(4,2,0)
refineData :: forall a. Data a => Pos -> Ref Identity a
refineData =
case typeRep of
AlgRep cs -> refineAlgRep dt cs
IntRep -> fmap (fromConstr . mkIntegralConstr dt) . intRef
FloatRep -> error "refineData: floats not implemented"
CharRep -> fmap (fromConstr . mkCharConstr dt) . charRef
NoRep -> error "refineData: used a data type with no representation"
where
dt = dataTypeOf (undefined :: a)
typeRep = dataTypeRep dt
#else
refineData :: forall a. Data a => Pos -> Ref a
refineData =
case typeRep of
AlgRep cs -> refineAlgRep dt cs
IntRep -> fmap (fromConstr . mkIntConstr dt) . intRef
FloatRep -> error "refineData: floats not implemented"
StringRep -> lifted [cons0 empty, cons2 cons]
NoRep -> error "refineData: data type with no representation"
where
dt = dataTypeOf (undefined :: a)
aDt = dataTypeOf (undefined :: A)
typeRep = dataTypeRep dt
empty = fromConstr (mkStringConstr dt [])
cons x xs = fromConstr (mkStringConstr dt (x:xs))
#endif
refineAlgRep :: Data a => DataType -> [Constr] -> Pos -> Ref Identity a
refineAlgRep dt cs
| isDataTypeA dt = pure . polyA
| isDataTypeFun dt = unlifted (map refineConstr cs)
| otherwise = lifted (map refineConstr cs)
where
refineConstr =
compose . gunfoldWithIndex (inCompose . refineArgument) (Compose . cons0)
refineArgument n f p = f p <*> refineData (p|>n)
lifted :: [Pos -> Ref Identity a] -> Pos -> Ref Identity a
lifted rs p = Ref (bottom p) [ResPos p [return (ArgPos p (map ($p) rs))]]
unlifted :: [Pos -> Ref m a] -> Pos -> Ref m a
unlifted rs p = NoRef (map ($p) rs)
cons0 :: a -> Pos -> Ref Identity a
cons0 x _ = pure x
cons1 :: Data a => (a -> b) -> Pos -> Ref Identity b
cons1 c p = c <$> refineData (p|>0)
cons2 :: (Data a,Data b) => (a -> b -> c) -> Pos -> Ref Identity c
cons2 c p = cons1 c p <*> refineData (p|>1)
intRef :: Pos -> Ref Identity Int
intRef = lifted [cons0 0,linearRef pred (1),linearRef succ 1]
linearRef :: (a -> a) -> a -> Pos -> Ref Identity a
linearRef f = linearRef'
where
linearRef' x = unlifted [cons0 x,linearRef' (f x)]
charRef :: Pos -> Ref Identity Char
charRef =
lifted [cons0 '?',fmap (\i -> toEnum (63i)) . natRef,
fmap (\i -> toEnum (63+i)) . natRef]
where
natRef = boundedNatRef 0 63
boundedNatRef :: Int -> Int -> Pos -> Ref Identity Int
boundedNatRef minNat maxNat = unlifted (boundedNatRef' 1)
where
boundedNatRef' x
| minNat<=x && x<=maxNat = [cons0 x,unlifted (boundedNatRef' (2*x)),
unlifted (boundedNatRef' (succ (2*x)))]
| otherwise = []
cut :: forall a f. Functor f => Int -> Ref f a -> Ref f a
cut d (NoRef rs) = NoRef (if d==0 then [] else map (cut (d1)) rs)
cut d (Ref x rs) = Ref x (map (fmap cut') rs)
where
cut' :: [f (ArgRef f a)] -> [f (ArgRef f a)]
cut' rs'
| d <= 0 = []
| otherwise = fmapArgRefs (cut (d1)) rs'
bfs :: forall a f. CoPointed f => Ref f a -> [a]
bfs t = unfoldOrs [t]
where
partition :: Ref f a -> ([a],[Ref f a]) -> ([a],[Ref f a])
partition (Ref v rs) ~(vs,ors) =
(v:vs,concatMap (argRefs . extract) (concatMap resRefs rs)++ors)
partition (NoRef rs) ~(vs,ors) = (vs,rs++ors)
unfoldOrs [] = []
unfoldOrs (x:xs) = let (vals,ors) = foldr partition ([],[]) (x:xs) in
vals ++ unfoldOrs ors
dfs :: CoPointed f => Ref f a -> [a]
dfs (NoRef rs) = concatMap dfs rs
dfs (Ref v rs) =
v:concatMap dfs (concatMap (argRefs . extract) (concatMap resRefs rs))
coSeq :: (Monad m,CoMonad m) => Ref m a -> Ref Identity (m a)
coSeq = coSeqRef return
coSeqRef :: (Monad m,CoMonad m) => (a -> m a) -> Ref m a
-> Ref Identity (m a)
coSeqRef f (NoRef rs) = NoRef (map (coSeqRef f) rs)
coSeqRef f (Ref v rs) = Ref (f v) (map (fmap (map (Identity . coSeqArgRef f))) rs)
coSeqArgRef :: (Monad m,CoMonad m) => (a -> m a) -> m (ArgRef m a)
-> ArgRef Identity (m a)
coSeqArgRef f ft = fmap (map (coSeqRef (f >=> (ft <<=) . const))) (extract ft)