{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module What4.Partial
(
Partial(..)
, partialPred
, partialValue
, PartialWithErr(..)
, PartExpr
, pattern PE
, pattern Unassigned
, mkPE
, justPartExpr
, maybePartExpr
, joinMaybePE
, PartialT(..)
, runPartialT
, returnUnassigned
, returnMaybe
, returnPartial
, addCondition
, mergePartial
, mergePartials
) where
#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail( MonadFail )
import qualified Control.Monad.Fail
#endif
import GHC.Generics (Generic, Generic1)
import Data.Data (Data)
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import What4.BaseTypes
import What4.Interface (IsExprBuilder, SymExpr, IsExpr, Pred)
import What4.Interface (truePred, andPred, notPred, itePred, asConstantPred)
import Control.Lens.TH (makeLenses)
import Data.Bifunctor.TH (deriveBifunctor, deriveBifoldable, deriveBitraversable)
import Data.Eq.Deriving (deriveEq1, deriveEq2)
import Data.Ord.Deriving (deriveOrd1, deriveOrd2)
import Text.Show.Deriving (deriveShow1, deriveShow2)
data Partial p v =
Partial { _partialPred :: !p
, _partialValue :: !v
}
deriving (Data, Eq, Functor, Generic, Generic1, Foldable, Traversable, Ord, Show)
makeLenses ''Partial
$(deriveBifunctor ''Partial)
$(deriveBifoldable ''Partial)
$(deriveBitraversable ''Partial)
$(deriveEq1 ''Partial)
$(deriveEq2 ''Partial)
$(deriveOrd1 ''Partial)
$(deriveOrd2 ''Partial)
$(deriveShow1 ''Partial)
$(deriveShow2 ''Partial)
total :: IsExprBuilder sym
=> sym
-> v
-> Partial (Pred sym) v
total sym = Partial (truePred sym)
data PartialWithErr e p v =
NoErr (Partial p v)
| Err e
deriving (Data, Eq, Functor, Generic, Generic1, Foldable, Traversable, Ord, Show)
$(deriveBifunctor ''PartialWithErr)
$(deriveBifoldable ''PartialWithErr)
$(deriveBitraversable ''PartialWithErr)
$(deriveEq1 ''PartialWithErr)
$(deriveEq2 ''PartialWithErr)
$(deriveOrd1 ''PartialWithErr)
$(deriveOrd2 ''PartialWithErr)
$(deriveShow1 ''PartialWithErr)
$(deriveShow2 ''PartialWithErr)
type PartExpr p v = PartialWithErr () p v
pattern Unassigned :: PartExpr p v
pattern Unassigned = Err ()
pattern PE :: p -> v -> PartExpr p v
pattern PE p v = NoErr (Partial p v)
{-# COMPLETE Unassigned, PE #-}
mkPE :: IsExpr p => p BaseBoolType -> a -> PartExpr (p BaseBoolType) a
mkPE p v =
case asConstantPred p of
Just False -> Unassigned
_ -> PE p v
justPartExpr :: IsExprBuilder sym => sym -> v -> PartExpr (Pred sym) v
justPartExpr sym = NoErr . total sym
maybePartExpr :: IsExprBuilder sym
=> sym -> Maybe a -> PartExpr (Pred sym) a
maybePartExpr _ Nothing = Unassigned
maybePartExpr sym (Just r) = justPartExpr sym r
joinMaybePE :: Maybe (PartExpr p v) -> PartExpr p v
joinMaybePE Nothing = Unassigned
joinMaybePE (Just pe) = pe
mergePartial :: (IsExprBuilder sym, MonadIO m) =>
sym ->
(Pred sym -> a -> a -> PartialT sym m a)
->
Pred sym ->
PartExpr (Pred sym) a ->
PartExpr (Pred sym) a ->
m (PartExpr (Pred sym) a)
{-# SPECIALIZE mergePartial ::
IsExprBuilder sym =>
sym ->
(Pred sym -> a -> a -> PartialT sym IO a) ->
Pred sym ->
PartExpr (Pred sym) a ->
PartExpr (Pred sym) a ->
IO (PartExpr (Pred sym) a) #-}
mergePartial _ _ _ Unassigned Unassigned =
return Unassigned
mergePartial sym _ c (PE px x) Unassigned =
do p <- liftIO $ andPred sym px c
return $! mkPE p x
mergePartial sym _ c Unassigned (PE py y) =
do p <- liftIO (andPred sym py =<< notPred sym c)
return $! mkPE p y
mergePartial sym f c (PE px x) (PE py y) =
do p <- liftIO (itePred sym c px py)
runPartialT sym p (f c x y)
mergePartials :: (IsExprBuilder sym, MonadIO m) =>
sym ->
(Pred sym -> a -> a -> PartialT sym m a)
->
[(Pred sym, PartExpr (Pred sym) a)] ->
m (PartExpr (Pred sym) a)
mergePartials sym f = go
where
go [] = return Unassigned
go ((c,x):xs) =
do y <- go xs
mergePartial sym f c x y
newtype PartialT sym m a =
PartialT { unPartial :: sym -> Pred sym -> m (PartExpr (Pred sym) a) }
runPartialT :: sym
-> Pred sym
-> PartialT sym m a
-> m (PartExpr (Pred sym) a)
runPartialT sym p f = unPartial f sym p
instance Functor m => Functor (PartialT sym m) where
fmap f mx = PartialT $ \sym p -> fmap resolve (unPartial mx sym p)
where resolve Unassigned = Unassigned
resolve (PE q x) = PE q (f x)
instance (IsExpr (SymExpr sym), Monad m) => Applicative (PartialT sym m) where
pure a = PartialT $ \_ p -> pure $! mkPE p a
mf <*> mx = mf >>= \f -> mx >>= \x -> pure (f x)
instance (IsExpr (SymExpr sym), Monad m) => Monad (PartialT sym m) where
return = pure
m >>= h =
PartialT $ \sym p -> do
pr <- unPartial m sym p
case pr of
Unassigned -> pure Unassigned
PE q r -> unPartial (h r) sym q
#if !MIN_VERSION_base(4,13,0)
fail msg = PartialT $ \_ _ -> fail msg
#endif
instance (IsExpr (SymExpr sym), MonadFail m) => MonadFail (PartialT sym m) where
fail msg = PartialT $ \_ _ -> fail msg
instance MonadTrans (PartialT sym) where
lift m = PartialT $ \_ p -> PE p <$> m
instance (IsExpr (SymExpr sym), MonadIO m) => MonadIO (PartialT sym m) where
liftIO = lift . liftIO
returnUnassigned :: Applicative m => PartialT sym m a
returnUnassigned = PartialT $ \_ _ -> pure Unassigned
returnMaybe :: (IsExpr (SymExpr sym), Applicative m) => Maybe a -> PartialT sym m a
returnMaybe Nothing = returnUnassigned
returnMaybe (Just a) = PartialT $ \_ p -> pure (mkPE p a)
returnPartial :: (IsExprBuilder sym, MonadIO m)
=> PartExpr (Pred sym) a
-> PartialT sym m a
returnPartial Unassigned = returnUnassigned
returnPartial (PE q a) = PartialT $ \sym p -> liftIO (mkPE <$> andPred sym p q <*> pure a)
addCondition :: (IsExprBuilder sym, MonadIO m)
=> Pred sym
-> PartialT sym m ()
addCondition q = returnPartial (mkPE q ())