{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
module Main where
import qualified Data.List as L
import Data.Hashable
#if MIN_VERSION_mtl(2,3,0)
import Control.Monad (guard)
#endif
import Control.Monad.Identity
import qualified Arith as A
import qualified Data.HashMap.Strict as M
import qualified ExploredTerms
import OpOrdering
import DSL
import WQO
import MultisetOrder
import Nat
import RPO
import KBO
import StrictOC
import LazyOC
import SMT
import qualified QuickCheckTests
import System.IO
import Language.REST.ExploredTerms
import Language.REST.OCAlgebra
import Language.REST.OCToAbstract
import Language.REST.Core
import Language.REST.LPO (lpo)
import Language.REST.KBO (kbo)
import Language.REST.WQOConstraints as OC
import Language.REST.Op
import Language.REST.RPO
import Language.REST.Internal.OpOrdering
import Language.REST.RewriteRule
import Language.REST.RuntimeTerm
import Language.REST.MetaTerm as MT
import Language.REST.Internal.Rewrite
import Language.REST.Internal.WQO
import Language.REST.Rest
import Language.REST.SMT
import qualified Language.REST.WQOConstraints.ADT as AC
import Language.REST.Internal.WorkStrategy
import qualified Data.Maybe as Mb
import qualified Data.HashSet as S
-- | 'canOrient' returns true iff the ordering constraint algebra permits an ordering
-- that orients, the path, i.e., the constraints generated by 'orient' are satisfiable.
canOrient :: forall oc m . Show oc
=> (?impl :: OCAlgebra oc RuntimeTerm m) => [RuntimeTerm] -> m Bool
canOrient terms = isSat ?impl (orient ?impl terms)
diverges :: (Show oc) => OCAlgebra oc RuntimeTerm IO -> [RuntimeTerm] -> IO Bool
diverges impl ts = not <$> isSat impl (orient impl ts)
rewrites :: (Show oc, Hashable oc, Eq oc)
=> OCAlgebra oc RuntimeTerm IO
-> S.HashSet Rewrite -> S.HashSet Rewrite -> RuntimeTerm -> IO (S.HashSet RuntimeTerm)
rewrites impl evalRWs userRWs t0 =
resultTerms . fst <$> rest
RESTParams
{ re = evalRWs
, ru = userRWs
, target = Nothing
, workStrategy = notVisitedFirst
, ocImpl = ?impl
, initRes = termsResult
, etStrategy = ExploreWhenNeeded
} t0
where
?impl = impl
runTest :: (String, IO Bool) -> IO ()
runTest (name, test) = do
putStr $ "Running " ++ name ++ "... "
hFlush stdout
ok <- test
if ok
then putStrLn "Passed"
else error $ "Test " ++ name ++ " FAILED"
class Testable a where
toTest :: a -> IO Bool
instance Testable Bool where
toTest = return
instance Testable (IO Bool) where
toTest = id
runTestSuite :: Testable a => String -> [(String, a)] -> IO ()
runTestSuite name tests1 = do
putStrLn $ "Running test suite: " ++ name
mapM_ (runTest . go) tests1
where
go (name1, test) = (name1, toTest test)
orderingTests :: (Hashable (oc Op), Show (oc Op), Ord (oc Op)) => (?impl :: WQOConstraints oc IO) => [(String, IO Bool)]
orderingTests =
[
("simple1", return $ not $ rpoGTE "f(t1)" "g(t2)" `permits'` (t1Op =. t2Op))
, ("simple2", return $ rpoGTE "f(t1)" "g(t2)" `permits'` Mb.fromJust (merge (f >. g) (t1Op =. t2Op)))
, ("simple3", return $ rpoGTE "f(t1)" "g(t2)" `permits'` Mb.fromJust (merge (f >. g) (t1Op >. t2Op)))
, ("subterm", return $ rpoGTE "f(g)" "f" == noConstraints ?impl)
, ("intersect", OC.isUnsatisfiable ?impl $ OC.intersect ?impl (OC.singleton ?impl (f >. g)) (OC.singleton ?impl (g >. f)))
]
where
permits' = permits ?impl
proveEQ :: (Show oc, Hashable oc, Eq oc) =>
OCAlgebra oc RuntimeTerm IO
-> S.HashSet Rewrite -> S.HashSet Rewrite
-> RuntimeTerm -> RuntimeTerm -> IO Bool
proveEQ impl evalRWs userRWs have want =
do
rw1 <- rewrites impl evalRWs userRWs have
rw2 <- rewrites impl evalRWs userRWs want
return $ not $ disjoint rw1 rw2
where
disjoint s1 s2 = S.null $ s1 `S.intersection` s2
eval :: S.HashSet Rewrite -> RuntimeTerm -> IO RuntimeTerm
eval rws t0 =
do
result <- mapM (apply t0) (S.toList rws)
case S.toList $ S.unions result of
[] -> return t0
(t : _) -> eval rws t
arithTests :: (Show oc, Hashable oc, Eq oc) => OCAlgebra oc RuntimeTerm IO -> [(String, IO Bool)]
arithTests impl =
[
("Contains", return $ contains (intToTerm 2) (intToTerm 1))
, ("Diverge", not <$> diverges impl [ intToTerm 2 .+ t1
, intToTerm 1 .+ t1
])
, ("Diverge3", not <$> diverges impl [ (t1 .+ t2) .+ t3
, t1 .+ (t2 .+ t3)
, (t2 .+ t3) .+ t1
])
, ("Eval1", arithEQ (intToTerm 2 .+ intToTerm 3) 5)
, ("Eval2", arithEQ (ack (intToTerm 3) (intToTerm 2)) 29)
, ("Subst1", return $ subst (M.fromList [("X", intToTerm 1), ("Y", intToTerm 2)]) (x #+ y) == (intToTerm 1 .+ intToTerm 2))
, ("ArithTerm", termTest)
, ("ArithTerm2", termTest2)
, ("Arith0", eq (t1 .+ t2 .+ intToTerm 1) (t1 .+ (intToTerm 1 .+ t2)))
, ("Arith1", eq (intToTerm 2 .+ intToTerm 3) (intToTerm 3 .+ intToTerm 2))
, ("Arith2", eq (t1 .+ t2) (t2 .+ t1))
, ("Arith3", not <$> eq (t2 .+ t1) (t2 .+ t2))
, ("Arith4", eq ((t1 .+ t2) .+ t3) (t1 .+ (t2.+ t3)))
, ("Arith4.1", eq (t1 .+ t2 .+ t3) (t3 .+ t2 .+ t1))
, ("Arith5", eq (zero .+ t1) t1)
, ("Arith5.1", eq (zero .+ zero .+ t1) t1)
, ("Arith5.2", eq (zero .+ zero .+ zero .+ t1) t1)
, ("Arith6", eq (((t1 .+ t1) .+ t3) .+ t4) (t1 .+ (t1 .+ (t3 .+ t4))))
, ("Arith7", eq ((intToTerm 2 .+ intToTerm 1) .+ t1) (intToTerm 2 .+ (intToTerm 1 .+ t1)))
]
where
arithEQ t n = do
t' <- eval A.evalRWs t
return $ termToInt t' == Just n
termTest = proveEQ impl evalRWs userRWs (App f1 [t1]) zero
where
evalRWs = S.union termEvalRWs A.evalRWs
userRWs = S.insert (MT.RWApp g1 [x] ~> MT.RWApp f1 [x]) A.userRWs
termEvalRWs = S.fromList
[ MT.RWApp f1 [x] ~> MT.RWApp g1 [suc' x]
, MT.RWApp g1 [x] ~> zero'
]
f1 = Op "f"
g1 = Op "g"
termTest2 = proveEQ impl evalRWs userRWs (App f1 [zero]) (App g1 [zero])
where
evalRWs = S.union termEvalRWs A.evalRWs
userRWs = S.insert (MT.RWApp f1 [x] ~> MT.RWApp g1 [suc' (suc' x)]) A.userRWs
termEvalRWs = S.fromList
[ MT.RWApp f1 [suc' x] ~> MT.RWApp g1 [suc' x]
, MT.RWApp f1 [zero'] ~> zero'
, MT.RWApp g1 [suc' x] ~> MT.RWApp f1 [x]
, MT.RWApp g1 [zero'] ~> zero'
]
f1 = Op "f"
g1 = Op "g"
eq = proveEQ impl A.evalRWs A.userRWs
completeTests :: (Show oc, Hashable oc, Eq oc) => OCAlgebra oc RuntimeTerm IO -> [(String, IO Bool)]
completeTests impl =
[ ("CompleteDiverges", not <$> diverges impl [App start [], App mid [], App finish []])
, ("Complete1" , eq (App start []) (App finish []))
, ("EvalComplete2" , (== App finish []) <$> eval completeUserRWs (App start' [App s1 []]) )
, ("Complete2" , eq (App start' [App s1 []]) (App finish []))
]
where
completeUserRWs = S.fromList
[ MT.RWApp start [] ~> MT.RWApp mid []
, MT.RWApp mid [] ~> MT.RWApp finish []
, MT.RWApp start' [MT.RWApp s2 []] ~> MT.RWApp mid' [MT.RWApp s1 []]
, MT.RWApp s1 [] ~> MT.RWApp s2 []
, MT.RWApp mid' [MT.RWApp s2 []] ~> MT.RWApp finish []
]
eq :: RuntimeTerm -> RuntimeTerm -> IO Bool
eq = proveEQ impl S.empty completeUserRWs
start = Op "start"
mid = Op "mid"
finish = Op "finish"
start' = Op "start'"
mid' = Op "mid'"
s1 = Op "s1"
s2 = Op "s2"
ocTests :: (Handle, Handle) -> IO ()
ocTests z3 = do
runTestSuite "LazyOC" LazyOC.tests
runTestSuite "StrictOC" StrictOC.tests
runTestSuite "RPO" RPO.tests
runTestSuite "Ordering" orderingTests
where
?impl = AC.adtOC z3
main :: IO ()
main = spawnZ3 >>= go where
implTests implName impl toSkip = do
runTestSuite ("Arith" ++ implName) (withSkips $ arithTests impl)
runTestSuite ("Complete" ++ implName) (withSkips $ completeTests impl)
where
withSkips tests1 = do
(name, test) <- tests1
guard $ L.notElem name toSkip
return (name, test)
go :: SolverHandle -> IO ()
go z3 =
do
putStrLn "Running REST Test Suite"
runTestSuite "ExploredTerms" ExploredTerms.tests
runTestSuite "SMT" SMT.tests
runTestSuite "KBO" (KBO.tests z3)
_ <- QuickCheckTests.tests
runTestSuite "OpOrdering" OpOrdering.tests
ocTests z3
runTestSuite "MultisetOrder" MultisetOrder.tests
runTestSuite "WQO" WQO.tests
implTests "KBO" (kbo z3) []
implTests "RPO" (lift (AC.adtOC z3) rpo) []
implTests "LPO" (lift (AC.adtOC z3) lpo) ["Diverge3", "Arith4", "Arith4.1", "Arith6"]
killZ3 z3