{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE StandaloneDeriving #-}
module Test.StateMachine.Logic
( Logic(..)
, LogicPredicate(..)
, dual
, strongNeg
, Counterexample(..)
, Value(..)
, boolean
, logic
, evalLogicPredicate
, gatherAnnotations
, (.==)
, (./=)
, (.<)
, (.<=)
, (.>)
, (.>=)
, member
, notMember
, (.//)
, (.&&)
, (.||)
, (.=>)
, forall
, exists
)
where
import Prelude
data Logic
= Bot
| Top
| Logic :&& Logic
| Logic :|| Logic
| Logic :=> Logic
| Not Logic
| LogicPredicate LogicPredicate
| forall a. Show a => Forall [a] (a -> Logic)
| forall a. Show a => Exists [a] (a -> Logic)
| Boolean Bool
| Annotate String Logic
data LogicPredicate
= forall a. (Eq a, Show a) => a :== a
| forall a. (Eq a, Show a) => a :/= a
| forall a. (Ord a, Show a) => a :< a
| forall a. (Ord a, Show a) => a :<= a
| forall a. (Ord a, Show a) => a :> a
| forall a. (Ord a, Show a) => a :>= a
| forall t a. (Foldable t, Eq a, Show a, Show (t a)) => Member a (t a)
| forall t a. (Foldable t, Eq a, Show a, Show (t a)) => NotMember a (t a)
deriving stock instance Show LogicPredicate
dual :: LogicPredicate -> LogicPredicate
dual p = case p of
x :== y -> x :/= y
x :/= y -> x :== y
x :< y -> x :>= y
x :<= y -> x :> y
x :> y -> x :<= y
x :>= y -> x :< y
x `Member` xs -> x `NotMember` xs
x `NotMember` xs -> x `Member` xs
strongNeg :: Logic -> Logic
strongNeg l0 = case l0 of
Bot -> Top
Top -> Bot
l :&& r -> strongNeg l :|| strongNeg r
l :|| r -> strongNeg l :&& strongNeg r
l :=> r -> l :&& strongNeg r
Not l -> l
LogicPredicate p -> LogicPredicate (dual p)
Forall xs p -> Exists xs (strongNeg . p)
Exists xs p -> Forall xs (strongNeg . p)
Boolean b -> Boolean (not b)
Annotate s l -> Annotate s (strongNeg l)
data Counterexample
= BotC
| Fst Counterexample
| Snd Counterexample
| EitherC Counterexample Counterexample
| ImpliesC Counterexample
| NotC Counterexample
| PredicateC LogicPredicate
| forall a. Show a => ForallC a Counterexample
| forall a. Show a => ExistsC [a] [Counterexample]
| BooleanC
| AnnotateC String Counterexample
deriving stock instance Show Counterexample
data Value
= VFalse Counterexample
| VTrue
deriving stock Show
boolean :: Logic -> Bool
boolean l = case logic l of
VFalse _ -> False
VTrue -> True
logic :: Logic -> Value
logic Bot = VFalse BotC
logic Top = VTrue
logic (l :&& r) = case logic l of
VFalse ce -> VFalse (Fst ce)
VTrue -> case logic r of
VFalse ce' -> VFalse (Snd ce')
VTrue -> VTrue
logic (l :|| r) = case logic l of
VTrue -> VTrue
VFalse ce -> case logic r of
VTrue -> VTrue
VFalse ce' -> VFalse (EitherC ce ce')
logic (l :=> r) = case logic l of
VFalse _ -> VTrue
VTrue -> case logic r of
VTrue -> VTrue
VFalse ce -> VFalse (ImpliesC ce)
logic (Not l) = case logic (strongNeg l) of
VTrue -> VTrue
VFalse ce -> VFalse (NotC ce)
logic (LogicPredicate p) = evalLogicPredicate p
logic (Forall xs0 p) = go xs0
where
go [] = VTrue
go (x : xs) = case logic (p x) of
VTrue -> go xs
VFalse ce -> VFalse (ForallC x ce)
logic (Exists xs0 p) = go xs0 []
where
go [] ces = VFalse (ExistsC xs0 (reverse ces))
go (x : xs) ces = case logic (p x) of
VTrue -> VTrue
VFalse ce -> go xs (ce : ces)
logic (Boolean b) = if b then VTrue else VFalse BooleanC
logic (Annotate s l) = case logic l of
VTrue -> VTrue
VFalse ce -> VFalse (AnnotateC s ce)
evalLogicPredicate :: LogicPredicate -> Value
evalLogicPredicate p0 = let b = go p0 in case p0 of
x :== y -> b (x == y)
x :/= y -> b (x /= y)
x :< y -> b (x < y)
x :<= y -> b (x <= y)
x :> y -> b (x > y)
x :>= y -> b (x >= y)
x `Member` xs -> b (x `elem` xs)
x `NotMember` xs -> b (x `notElem` xs)
where
go :: LogicPredicate -> Bool -> Value
go _ True = VTrue
go p False = VFalse (PredicateC (dual p))
gatherAnnotations :: Logic -> [String]
gatherAnnotations = go []
where
go _acc Bot = []
go acc Top = acc
go acc (l :&& r) | boolean l && boolean r = go (go acc l) r
| otherwise = acc
go acc (l :|| r) | boolean l = go acc l
| boolean r = go acc r
| otherwise = acc
go acc (l :=> r) | boolean (l :=> r) = go (go acc l) r
| otherwise = acc
go acc (Not l) | not (boolean l) = go acc l
| otherwise = acc
go acc (LogicPredicate _p) = acc
go acc (Forall xs p)
| boolean (Forall xs p) = acc ++ concat [ go [] (p x)
| x <- xs, boolean (p x)
]
| otherwise = acc
go acc (Exists xs p)
| boolean (Exists xs p) = acc ++ concat (take 1 [ go [] (p x)
| x <- xs, boolean (p x)
])
| otherwise = acc
go acc (Boolean _b) = acc
go acc (Annotate s l) = go (acc ++ [s]) l
infix 5 .==
infix 5 ./=
infix 5 .<
infix 5 .<=
infix 5 .>
infix 5 .>=
infix 8 `member`
infix 8 `notMember`
infixl 4 .//
infixr 3 .&&
infixr 2 .||
infixr 1 .=>
(.==) :: (Eq a, Show a) => a -> a -> Logic
x .== y = LogicPredicate (x :== y)
(./=) :: (Eq a, Show a) => a -> a -> Logic
x ./= y = LogicPredicate (x :/= y)
(.<) :: (Ord a, Show a) => a -> a -> Logic
x .< y = LogicPredicate (x :< y)
(.<=) :: (Ord a, Show a) => a -> a -> Logic
x .<= y = LogicPredicate (x :<= y)
(.>) :: (Ord a, Show a) => a -> a -> Logic
x .> y = LogicPredicate (x :> y)
(.>=) :: (Ord a, Show a) => a -> a -> Logic
x .>= y = LogicPredicate (x :>= y)
member :: (Foldable t, Eq a, Show a, Show (t a)) => a -> t a -> Logic
member x xs = LogicPredicate (Member x xs)
notMember :: (Foldable t, Eq a, Show a, Show (t a)) => a -> t a -> Logic
notMember x xs = LogicPredicate (NotMember x xs)
(.//) :: Logic -> String -> Logic
l .// s = Annotate s l
(.&&) :: Logic -> Logic -> Logic
(.&&) = (:&&)
(.||) :: Logic -> Logic -> Logic
(.||) = (:||)
(.=>) :: Logic -> Logic -> Logic
(.=>) = (:=>)
forall :: Show a => [a] -> (a -> Logic) -> Logic
forall = Forall
exists :: Show a => [a] -> (a -> Logic) -> Logic
exists = Exists