{-# LANGUAGE OverloadedStrings #-}
module SMTLIB.Backends
( Backend (..),
QueuingFlag (..),
Solver,
initSolver,
command,
command_,
flushQueue,
)
where
import Control.Monad ((<=<))
import Data.ByteString.Builder (Builder)
import qualified Data.ByteString.Lazy.Char8 as LBS
import Data.Char (isSpace)
import Data.IORef (IORef, modifyIORef, newIORef, readIORef, writeIORef)
import Data.List (intersperse)
import Prelude hiding (log)
data Backend = Backend
{
Backend -> Builder -> IO ByteString
send :: Builder -> IO LBS.ByteString,
Backend -> Builder -> IO ()
send_ :: Builder -> IO ()
}
type Queue = IORef Builder
data QueuingFlag = Queuing | NoQueuing
put :: Queue -> Builder -> IO ()
put :: Queue -> Builder -> IO ()
put Queue
q Builder
cmd = forall a. IORef a -> (a -> a) -> IO ()
modifyIORef Queue
q (forall a. Semigroup a => a -> a -> a
<> Builder
cmd)
flush :: Queue -> IO Builder
flush :: Queue -> IO Builder
flush Queue
q = do
Builder
cmds <- forall a. IORef a -> IO a
readIORef Queue
q
forall a. IORef a -> a -> IO ()
writeIORef Queue
q forall a. Monoid a => a
mempty
forall (m :: * -> *) a. Monad m => a -> m a
return Builder
cmds
data Solver = Solver
{
Solver -> Backend
backend :: Backend,
Solver -> Maybe Queue
queue :: Maybe Queue
}
initSolver ::
QueuingFlag ->
Backend ->
IO Solver
initSolver :: QueuingFlag -> Backend -> IO Solver
initSolver QueuingFlag
queuing Backend
solverBackend = do
Maybe Queue
solverQueue <- case QueuingFlag
queuing of
QueuingFlag
Queuing -> do
Queue
ref <- forall a. a -> IO (IORef a)
newIORef forall a. Monoid a => a
mempty
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just Queue
ref
QueuingFlag
NoQueuing -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
let solver :: Solver
solver = Backend -> Maybe Queue -> Solver
Solver Backend
solverBackend Maybe Queue
solverQueue
case QueuingFlag
queuing of
QueuingFlag
Queuing -> forall (m :: * -> *) a. Monad m => a -> m a
return ()
QueuingFlag
NoQueuing ->
Solver -> Builder -> Builder -> IO ()
setOption Solver
solver Builder
"print-success" Builder
"true"
Solver -> Builder -> Builder -> IO ()
setOption Solver
solver Builder
"produce-models" Builder
"true"
forall (m :: * -> *) a. Monad m => a -> m a
return Solver
solver
command :: Solver -> Builder -> IO LBS.ByteString
command :: Solver -> Builder -> IO ByteString
command Solver
solver Builder
cmd = do
Backend -> Builder -> IO ByteString
send (Solver -> Backend
backend Solver
solver)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< case Solver -> Maybe Queue
queue Solver
solver of
Maybe Queue
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return Builder
cmd
Just Queue
q -> (forall a. Semigroup a => a -> a -> a
<> Builder
cmd) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Queue -> IO Builder
flush Queue
q
command_ :: Solver -> Builder -> IO ()
command_ :: Solver -> Builder -> IO ()
command_ Solver
solver Builder
cmd =
case Solver -> Maybe Queue
queue Solver
solver of
Maybe Queue
Nothing -> do
ByteString
res <- Backend -> Builder -> IO ByteString
send (Solver -> Backend
backend Solver
solver) Builder
cmd
if ByteString -> ByteString
trim ByteString
res forall a. Eq a => a -> a -> Bool
== ByteString
"success"
then forall (m :: * -> *) a. Monad m => a -> m a
return ()
else
forall (m :: * -> *) a. MonadFail m => [Char] -> m a
fail forall a b. (a -> b) -> a -> b
$
[[Char]] -> [Char]
unlines
[ [Char]
"Unexpected result from the SMT solver:",
[Char]
" Expected: success",
[Char]
" Got: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show ByteString
res
]
Just Queue
q -> Queue -> Builder -> IO ()
put Queue
q Builder
cmd
where
trim :: ByteString -> ByteString
trim = (Char -> Bool) -> ByteString -> ByteString
LBS.dropWhile Char -> Bool
isSpace forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
LBS.reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> ByteString -> ByteString
LBS.dropWhile Char -> Bool
isSpace forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
LBS.reverse
flushQueue :: Solver -> IO ()
flushQueue :: Solver -> IO ()
flushQueue Solver
solver = forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (m :: * -> *) a. Monad m => a -> m a
return ()) (Backend -> Builder -> IO ()
send_ (Solver -> Backend
backend Solver
solver) forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< Queue -> IO Builder
flush) forall a b. (a -> b) -> a -> b
$ Solver -> Maybe Queue
queue Solver
solver
setOption :: Solver -> Builder -> Builder -> IO ()
setOption :: Solver -> Builder -> Builder -> IO ()
setOption Solver
solver Builder
name Builder
value = Solver -> Builder -> IO ()
command_ Solver
solver forall a b. (a -> b) -> a -> b
$ [Builder] -> Builder
list [Builder
"set-option", Builder
":" forall a. Semigroup a => a -> a -> a
<> Builder
name, Builder
value]
list :: [Builder] -> Builder
list :: [Builder] -> Builder
list [Builder]
bs = Builder
"(" forall a. Semigroup a => a -> a -> a
<> forall a. Monoid a => [a] -> a
mconcat (forall a. a -> [a] -> [a]
intersperse Builder
" " [Builder]
bs) forall a. Semigroup a => a -> a -> a
<> Builder
")"