module Test.Target.Targetable
( Targetable(..), qquery
, unfold, apply, unapply
, oneOf, whichOf
, constrain, ofReft
) where
import Control.Applicative
import Control.Arrow (second)
import Control.Monad.Reader
import Control.Monad.State
import Data.Char
import qualified Data.HashMap.Strict as M
import Data.List
import Data.Maybe
import Data.Proxy
import qualified Data.Text as T
import Data.Word (Word8)
import GHC.Generics
import GHC.Stack
import Language.Fixpoint.Types hiding (prop, ofReft, reft)
import Language.Haskell.Liquid.Types.RefType
import Language.Haskell.Liquid.Types hiding (var)
import Test.Target.Expr
import Test.Target.Eval
import Test.Target.Monad
import Test.Target.Util
class Targetable a where
query :: (?loc :: CallStack) => Proxy a -> Depth -> Symbol -> SpecType -> Target Symbol
decode :: Symbol
-> SpecType
-> Target a
check :: a -> SpecType -> Target (Bool, Expr)
toExpr :: a -> Expr
getType :: Proxy a -> Sort
default getType :: (Generic a, Rep a ~ D1 d f, Datatype d)
=> Proxy a -> Sort
getType _ = FObj $ qualifiedDatatypeName (undefined :: Rep a a)
default query :: (?loc :: CallStack) => (Generic a, GQuery (Rep a))
=> Proxy a -> Int -> Symbol -> SpecType -> Target Symbol
query p d x t = do
gquery (reproxyRep p) d x t
default toExpr :: (Generic a, GToExpr (Rep a))
=> a -> Expr
toExpr = gtoExpr . from
default decode :: (Generic a, GDecode (Rep a))
=> Symbol -> SpecType -> Target a
decode v _ = do
x <- whichOf v
(c, fs) <- unapply x
to <$> gdecode c fs
default check :: (Generic a, GCheck (Rep a))
=> a -> SpecType -> Target (Bool, Expr)
check v t = gcheck (from v) t
qquery :: Targetable a => Proxy a -> Int -> SpecType -> Target Symbol
qquery p d t = fresh (getType p) >>= \x -> query p d x t
reproxy :: proxy a -> Proxy b
reproxy _ = Proxy
unfold :: Symbol -> SpecType -> Target [(Symbol, SpecType)]
unfold cn t = do
dcp <- lookupCtor cn t
tyi <- gets tyconInfo
emb <- gets embEnv
let ts = applyPreds (addTyConInfo emb tyi t) dcp
return ts
apply :: Symbol -> SpecType -> [Expr] -> Target Expr
apply c t vs = do
mc <- gets chosen
case mc of
Just ch -> mapM_ (addDep ch) vs
Nothing -> return ()
let x = app c vs
t <- lookupCtor c t
let (xs, _, _, rt) = bkArrowDeep t
su = mkSubst $ zip (map symbol xs) vs
addConstructor (c, rTypeSort mempty t)
constrain $ ofReft (subst su $ reft rt) x
return x
unapply :: Symbol -> Target (Symbol, [Symbol])
unapply c = do
let [_,cn,_] = T.splitOn "-" $ symbolText c
deps <- gets deps
return (symbol cn, M.lookupDefault [] c deps)
oneOf :: Symbol -> [(Expr,Expr)] -> Target ()
oneOf x cs
= do cs <- forM cs $ \(y,c) -> do
addDep x c
constrain $ prop c `imp` (var x `eq` y)
return $ prop c
constrain $ pOr cs
constrain $ pAnd [ PNot $ pAnd [x, y]
| [x, y] <- filter ((==2) . length) $ subsequences cs ]
whichOf :: Symbol -> Target Symbol
whichOf v = do
deps <- gets deps
let Just cs = M.lookup v deps
c:_ <- catMaybes <$> forM cs (\c -> do
val <- getValue c
if val == "true"
then return (Just c)
else return Nothing)
return c
constrain :: (?loc :: CallStack) => Expr -> Target ()
constrain p = do
mc <- gets chosen
case mc of
Nothing -> addConstraint p
Just c -> let p' = prop (var c) `imp` p
in addConstraint p'
ofReft :: Reft -> Expr -> Expr
ofReft (Reft (v, p)) e
= let x = mkSubst [(v, e)]
in subst x p
instance Targetable () where
getType _ = FObj "GHC.Tuple.()"
query _ _ x _ = return x
toExpr _ = app ("()" :: Symbol) []
decode _ _ = return ()
check _ t = do
let e = app ("()" :: Symbol) []
b <- eval (reft t) e
return (b,e)
instance Targetable Int where
getType _ = FObj "GHC.Types.Int"
query _ d x t =
do
constrain $ ofReft (reft t) (var x)
constrain $ var x `ge` fromIntegral (negate d)
constrain $ var x `le` fromIntegral d
return x
toExpr i = ECon $ I $ fromIntegral i
decode v _ = read . T.unpack <$> getValue v
check v t = do
let e = fromIntegral v
b <- eval (reft t) e
return (b, e)
instance Targetable Integer where
getType _ = FObj "GHC.Integer.Type.Integer"
query _ d x t = query (Proxy :: Proxy Int) d x t
toExpr x = toExpr (fromIntegral x :: Int)
decode v t = decode v t >>= \(x::Int) -> return . fromIntegral $ x
check v t = do
let e = fromIntegral v
b <- eval (reft t) e
return (b, e)
instance Targetable Char where
getType _ = FObj "GHC.Types.Char"
query _ d x t =
do constrain $ var x `ge` 0
constrain $ var x `le` fromIntegral d
constrain $ ofReft (reft t) (var x)
return x
toExpr c = ESym $ SL $ T.singleton c
decode v t = decode v t >>= \(x::Int) -> return . chr $ x + ord 'a'
check v t = do
let e = ESym $ SL $ T.singleton v
b <- eval (reft t) e
return (b, e)
instance Targetable Word8 where
getType _ = FObj "GHC.Word.Word8"
query _ d x t =
do _ <- asks depth
constrain $ var x `ge` 0
constrain $ var x `le` fromIntegral d
constrain $ ofReft (reft t) (var x)
return x
toExpr i = ECon $ I $ fromIntegral i
decode v t = decode v t >>= \(x::Int) -> return $ fromIntegral x
check v t = do
let e = fromIntegral v
b <- eval (reft t) e
return (b, e)
instance Targetable Bool
instance Targetable a => Targetable [a]
instance Targetable a => Targetable (Maybe a)
instance (Targetable a, Targetable b) => Targetable (Either a b)
instance (Targetable a, Targetable b) => Targetable (a,b)
instance (Targetable a, Targetable b, Targetable c) => Targetable (a,b,c)
instance (Targetable a, Targetable b, Targetable c, Targetable d) => Targetable (a,b,c,d)
reproxyRep :: Proxy a -> Proxy (Rep a a)
reproxyRep = reproxy
class GToExpr f where
gtoExpr :: f a -> Expr
class GQuery f where
gquery :: (?loc :: CallStack) => Proxy (f a) -> Int -> Symbol -> SpecType -> Target Symbol
class GDecode f where
gdecode :: Symbol -> [Symbol] -> Target (f a)
class GCheck f where
gcheck :: f a -> SpecType -> Target (Bool, Expr)
reproxyGElem :: Proxy (M1 d c f a) -> Proxy (f a)
reproxyGElem = reproxy
instance (Datatype c, GToExprCtor f) => GToExpr (D1 c f) where
gtoExpr (M1 x) = app (qualify mod (symbolString d)) xs
where
mod = GHC.Generics.moduleName (undefined :: D1 c f a)
(EVar d, xs) = splitEApp $ gtoExprCtor x
instance (Datatype c, GQueryCtors f) => GQuery (D1 c f) where
gquery p d x t = inModule mod . making sort $ do
xs <- gqueryCtors (reproxyGElem p) d t
oneOf x xs
constrain $ ofReft (reft t) (var x)
return x
where
mod = symbol $ GHC.Generics.moduleName (undefined :: D1 c f a)
sort = FObj $ qualifiedDatatypeName (undefined :: D1 c f a)
instance (Datatype c, GDecode f) => GDecode (D1 c f) where
gdecode c vs = M1 <$> making sort (gdecode c vs)
where
sort = FObj $ qualifiedDatatypeName (undefined :: D1 c f a)
instance (Datatype c, GCheck f) => GCheck (D1 c f) where
gcheck (M1 x) t = inModule mod . making sort $ gcheck x t
where
mod = symbol $ GHC.Generics.moduleName (undefined :: D1 c f a)
sort = FObj $ qualifiedDatatypeName (undefined :: D1 c f a)
instance (Targetable a) => GToExpr (K1 i a) where
gtoExpr (K1 x) = toExpr x
instance (Targetable a) => GQuery (K1 i a) where
gquery p d _ t = do
let p' = reproxy p :: Proxy a
ty <- gets makingTy
depth <- asks depth
sc <- asks scDepth
let d' = if getType p' == ty || sc
then d
else depth
qquery p' d' t
instance Targetable a => GDecodeFields (K1 i a) where
gdecodeFields (v:vs) = do
x <- decode v undefined
return (vs, K1 x)
gdecodeFields _ = error "gdecodeFields []"
instance Targetable a => GCheckFields (K1 i a) where
gcheckFields (K1 x) ((f,t):ts) = do
(b, v) <- check x t
return (b, [v], subst (mkSubst [(f, v)]) ts)
gcheckFields _ _ = error "gcheckFields _ []"
qualify :: String -> String -> String
qualify m x = m ++ ('.':x)
qualifiedDatatypeName :: Datatype d => D1 d f a -> Symbol
qualifiedDatatypeName d = symbol $ qualify m (datatypeName d)
where m = GHC.Generics.moduleName d
class GToExprCtor f where
gtoExprCtor :: f a -> Expr
class GQueryCtors f where
gqueryCtors :: (?loc :: CallStack) => Proxy (f a) -> Int -> SpecType -> Target [(Expr, Expr)]
reproxyLeft :: Proxy ((c (f :: * -> *) (g :: * -> *)) a) -> Proxy (f a)
reproxyLeft = reproxy
reproxyRight :: Proxy ((c (f :: * -> *) (g :: * -> *)) a) -> Proxy (g a)
reproxyRight = reproxy
instance (GToExprCtor f, GToExprCtor g) => GToExprCtor (f :+: g) where
gtoExprCtor (L1 x) = gtoExprCtor x
gtoExprCtor (R1 x) = gtoExprCtor x
instance (GQueryCtors f, GQueryCtors g) => GQueryCtors (f :+: g) where
gqueryCtors p d t = do
xs <- gqueryCtors (reproxyLeft p) d t
ys <- gqueryCtors (reproxyRight p) d t
return $! xs++ys
instance (GDecode f, GDecode g) => GDecode (f :+: g) where
gdecode c vs = L1 <$> gdecode c vs
<|> R1 <$> gdecode c vs
instance (GCheck f, GCheck g) => GCheck (f :+: g) where
gcheck (L1 x) t = gcheck x t
gcheck (R1 x) t = gcheck x t
instance (Constructor c, GToExprFields f) => GToExprCtor (C1 c f) where
gtoExprCtor c@(M1 x) = app (symbol $ conName c) (gtoExprFields x)
instance (Constructor c, GRecursive f, GQueryFields f) => GQueryCtors (C1 c f) where
gqueryCtors p d t | d <= 0
= do ty <- gets makingTy
if gisRecursive p ty
then return []
else pure <$> gqueryCtor p 0 t
gqueryCtors p d t = pure <$> gqueryCtor p d t
instance (Constructor c, GDecodeFields f) => GDecode (C1 c f) where
gdecode c vs
| c == symbol (conName (undefined :: C1 c f a))
= M1 . snd <$> gdecodeFields vs
| otherwise
= empty
instance (Constructor c, GCheckFields f) => GCheck (C1 c f) where
gcheck (M1 x) t = do
mod <- symbolString <$> gets modName
let cn = symbol $ qualify mod (conName (undefined :: C1 c f a))
ts <- unfold cn t
(b, vs, _) <- gcheckFields x ts
let v = app cn vs
b' <- eval (reft t) v
return (b && b', v)
gisRecursive :: (Constructor c, GRecursive f)
=> Proxy (C1 c f a) -> Sort -> Bool
gisRecursive (p :: Proxy (C1 c f a)) t
= t `elem` gconArgTys (reproxyGElem p)
gqueryCtor :: (?loc :: CallStack) => (Constructor c, GQueryFields f)
=> Proxy (C1 c f a) -> Int -> SpecType -> Target (Expr, Expr)
gqueryCtor (p :: Proxy (C1 c f a)) d t
= guarded cn $ do
mod <- symbolString <$> gets modName
ts <- unfold (symbol $ qualify mod cn) t
xs <- gqueryFields (reproxyGElem p) d ts
apply (symbol $ qualify mod cn) t xs
where
cn = conName (undefined :: C1 c f a)
class GToExprFields f where
gtoExprFields :: f a -> [Expr]
class GRecursive f where
gconArgTys :: Proxy (f a) -> [Sort]
class GQueryFields f where
gqueryFields :: (?loc :: CallStack) => Proxy (f a) -> Int -> [(Symbol,SpecType)] -> Target [Expr]
class GDecodeFields f where
gdecodeFields :: [Symbol] -> Target ([Symbol], f a)
class GCheckFields f where
gcheckFields :: f a -> [(Symbol, SpecType)]
-> Target (Bool, [Expr], [(Symbol, SpecType)])
instance (GToExprFields f, GToExprFields g) => GToExprFields (f :*: g) where
gtoExprFields (f :*: g) = gtoExprFields f ++ gtoExprFields g
instance (GRecursive f, GRecursive g) => GRecursive (f :*: g) where
gconArgTys p = gconArgTys (reproxyLeft p) ++ gconArgTys (reproxyRight p)
instance (GQueryFields f, GQueryFields g) => GQueryFields (f :*: g) where
gqueryFields p d ts = do
xs <- gqueryFields (reproxyLeft p) d ts
let su = mkSubst $ zipWith (\x t -> (fst t, x)) xs ts
let ts' = drop (length xs) ts
ys <- gqueryFields (reproxyRight p) d (map (second (subst su)) ts')
return $ xs ++ ys
instance (GDecodeFields f, GDecodeFields g) => GDecodeFields (f :*: g) where
gdecodeFields vs = do
(vs', ls) <- gdecodeFields vs
(vs'', rs) <- gdecodeFields vs'
return (vs'', ls :*: rs)
instance (GCheckFields f, GCheckFields g) => GCheckFields (f :*: g) where
gcheckFields (f :*: g) ts = do
(bl,fs,ts') <- gcheckFields f ts
(br,gs,ts'') <- gcheckFields g ts'
return (bl && br, fs ++ gs, ts'')
instance (GToExpr f) => GToExprFields (S1 c f) where
gtoExprFields (M1 x) = [gtoExpr x]
instance Targetable a => GRecursive (S1 c (K1 i a)) where
gconArgTys _ = [getType (Proxy :: Proxy a)]
instance (GQuery f) => GQueryFields (S1 c f) where
gqueryFields p d (t:_) = sequence [var <$> gquery (reproxyGElem p) (d1) "" (snd t)]
gqueryFields _ _ _ = error "gqueryfields _ _ []"
instance GDecodeFields f => GDecodeFields (S1 c f) where
gdecodeFields vs = do
(vs', x) <- gdecodeFields vs
return (vs', M1 x)
instance (GCheckFields f) => GCheckFields (S1 c f) where
gcheckFields (M1 x) ts = gcheckFields x ts
instance GToExprFields U1 where
gtoExprFields _ = []
instance GRecursive U1 where
gconArgTys _ = []
instance GQueryFields U1 where
gqueryFields _ _ _ = return []
instance GDecodeFields U1 where
gdecodeFields vs = return (vs, U1)
instance GCheckFields U1 where
gcheckFields _ ts = return (True, [], ts)