{-# OPTIONS -fglasgow-exts #-}
module Reify (tests) where
import Test.HUnit
import Data.Maybe
import Data.Generics
import Control.Monad.State
import CompanyDatatypes
type TypeVal a = a -> ()
typeVal :: TypeVal a
typeVal = const ()
sameType :: (Typeable a, Typeable b) => TypeVal a -> TypeVal b -> Bool
sameType tva tvb = typeOf (type2val tva) ==
typeOf (type2val tvb)
val2type :: a -> TypeVal a
val2type _ = typeVal
type2val :: TypeVal a -> a
type2val _ = undefined
withType :: a -> TypeVal a -> a
withType x _ = x
argType :: (a -> b) -> TypeVal a
argType _ = typeVal
resType :: (a -> b) -> TypeVal b
resType _ = typeVal
paraType :: t a -> TypeVal a
paraType _ = typeVal
type TypeFun a r = TypeVal a -> r
type GTypeFun r = forall a. Data a => TypeFun a r
extType :: (Data a, Typeable r) => GTypeFun r -> TypeFun a r -> GTypeFun r
extType f x = maybe f id (cast x)
gmapType :: ([(Constr,r')] -> r)
-> GTypeFun (Constr -> r')
-> GTypeFun r
gmapType (o::[(Constr,r')] -> r) f (t::TypeVal a)
=
o $ zip cons query
where
cons :: [Constr]
cons = if isAlgType $ dataTypeOf $ type2val t
then dataTypeConstrs $ dataTypeOf $ type2val t
else []
query :: [r']
query = map (f t) cons
gmapConstr :: ([r] -> r')
-> GTypeFun r
-> GTypeFun (Constr -> r')
gmapConstr (o::[r] -> r') f (t::TypeVal a) c
=
o $ query
where
term :: a
term = fromConstr c
query :: [r]
query = gmapQ (f . val2type) term
constrArity :: GTypeFun (Constr -> Int)
constrArity t c = glength $ withType (fromConstr c) t
gmapSubtermTypes :: (Data a, Typeable r)
=> (r -> r -> r) -> r -> GTypeFun r -> TypeVal a -> r
gmapSubtermTypes o (r::r) f (t::TypeVal a)
=
reduce (concat (map (gmapQ (query . val2type)) terms))
(GTypeFun' f)
where
cons :: [Constr]
cons = if isAlgType $ dataTypeOf $ type2val t
then dataTypeConstrs $ dataTypeOf $ type2val t
else []
terms :: [a]
terms = map fromConstr cons
query :: Data b => TypeVal b -> GTypeFun' r -> (r,GTypeFun' r)
query t f = (unGTypeFun' f t, GTypeFun' (disable t (unGTypeFun' f)))
disable :: Data b => TypeVal b -> GTypeFun r -> GTypeFun r
disable (t::TypeVal b) f = f `extType` \(_::TypeVal b) -> r
reduce :: [GTypeFun' r -> (r,GTypeFun' r)] -> GTypeFun' r -> r
reduce [] _ = r
reduce (xy:z) g = fst (xy g) `o` reduce z (snd (xy g))
newtype GTypeFun' r = GTypeFun' (GTypeFun r)
unGTypeFun' (GTypeFun' f) = f
gmapSubtermTypesConst :: (Data a, Typeable r)
=> (r -> r -> r)
-> r
-> GTypeFun r
-> TypeVal a
-> r
gmapSubtermTypesConst o (r::r) f (t::TypeVal a)
=
gmapSubtermTypes o r f' t
where
f' :: GTypeFun r
f' = f `extType` \(_::TypeVal a) -> r
gcountSubtermTypes :: Data a => TypeVal a -> Int
gcountSubtermTypes = gmapSubtermTypes (+) (0::Int) (const 1)
_gmapSubtermTypes :: (Data a, Typeable r)
=> (r -> r -> r) -> r -> GTypeFun r -> TypeVal a -> r
_gmapSubtermTypes o (r::r) f
=
gmapType otype (gmapConstr oconstr f)
where
otype :: [(Constr,r)] -> r
otype = foldr (\x y -> snd x `o` y) r
oconstr :: [r] -> r
oconstr = foldr o r
reachableType :: (Data a, Data b) => TypeVal a -> TypeVal b -> Bool
reachableType (a::TypeVal a) (b::TypeVal b)
=
or [ sameType a b
, gmapSubtermTypesConst (\x y -> or [x,y]) False (reachableType a) b
]
depthOfType :: GTypeFun Bool -> GTypeFun (Maybe (Constr, Maybe Int))
depthOfType p (t::TypeVal a)
=
gmapType o f t
where
o :: [(Constr, Maybe Int)] -> Maybe (Constr, Maybe Int)
o l = if null l then Nothing else Just (foldr1 min' l)
f :: GTypeFun (Constr -> Maybe Int)
f = depthOfConstr p'
min' :: (Constr, Maybe Int) -> (Constr, Maybe Int) -> (Constr, Maybe Int)
min' x (_, Nothing) = x
min' (_, Nothing) x = x
min' (c, Just i) (c', Just i') | i <= i' = (c, Just i)
min' (c, Just i) (c', Just i') = (c', Just i')
p' :: GTypeFun Bool
p' = p `extType` \(_::TypeVal a) -> False
depthOfConstr :: GTypeFun Bool -> GTypeFun (Constr -> Maybe Int)
depthOfConstr p (t::TypeVal a) c
=
gmapConstr o f t c
where
o :: [Maybe Int] -> Maybe Int
o = inc' . foldr max' (Just 0)
f :: GTypeFun (Maybe Int)
f t' = if p t'
then
case depthOfType p t' of
Nothing -> Just 0
Just (_, x) -> x
else Nothing
max' Nothing _ = Nothing
max' _ Nothing = Nothing
max' (Just i) (Just i') | i >= i' = Just i
max' (Just i) (Just i') = Just i'
inc' Nothing = Nothing
inc' (Just i) = Just (i+1)
shallowTerm :: (forall a. Data a => Maybe a) -> (forall b. Data b => b)
shallowTerm cust
= result
where
result :: forall b. Data b => b
result = maybe gdefault id cust
where
gdefault :: b
gdefault = case con of
Just (con, Just _) -> fromConstrB (shallowTerm cust) con
_ -> error "no shallow term!"
typeVal :: TypeVal b
typeVal = val2type gdefault
con :: Maybe (Constr, Maybe Int)
con = depthOfType (const True) typeVal
shallowTermBase :: GenericR Maybe
shallowTermBase = Nothing
`extR` Just (1.23::Float)
`extR` Just ("foo"::String)
data T1 = T1a deriving (Typeable, Data)
data T2 = T2 T1 deriving (Typeable, Data)
data T3 = T3a T3 | T3b T2 deriving (Typeable, Data)
data T4 = T4 T3 T3 deriving (Typeable, Data)
t0 = typeVal :: TypeVal Int
t1 = typeVal :: TypeVal T1
t2 = typeVal :: TypeVal T2
t3 = typeVal :: TypeVal T3
t4 = typeVal :: TypeVal T4
tCompany = typeVal :: TypeVal Company
tPerson = typeVal :: TypeVal Person
tEmployee = typeVal :: TypeVal Employee
tDept = typeVal :: TypeVal Dept
test0 = t1 `reachableType` t1
test1 = t1 `reachableType` t2
test2 = t2 `reachableType` t1
test3 = t1 `reachableType` t3
test4 = tPerson `reachableType` tCompany
test5 = gcountSubtermTypes tPerson
test6 = gcountSubtermTypes tEmployee
test7 = gcountSubtermTypes tDept
test8 = shallowTerm shallowTermBase :: Person
test9 = shallowTerm shallowTermBase :: Employee
test10 = shallowTerm shallowTermBase :: Dept
tests = ( test0
, ( test1
, ( test2
, ( test3
, ( test4
, ( test5
, ( test6
, ( test7
, ( test8
, ( test9
, ( test10
))))))))))) ~=? output
output = (True,(True,(False,(True,(True,(1,(2,(3,(P "foo" "foo",
(E (P "foo" "foo") (S 1.23),
D "foo" (E (P "foo" "foo") (S 1.23)) []))))))))))