{-# LANGUAGE RecordWildCards, CPP #-}
module Jukebox.Toolbox where
import Jukebox.Options
import Jukebox.Form
import Jukebox.Name
import Jukebox.TPTP.Print
import Control.Monad
import Jukebox.TPTP.Parse
import Jukebox.Tools.Clausify hiding (run)
import Jukebox.Tools.AnalyseMonotonicity hiding (guards)
import Jukebox.Tools.EncodeTypes
import Jukebox.Tools.GuessModel
import Jukebox.Tools.InferTypes
import Jukebox.Tools.HornToUnit
import System.Exit
import System.IO
import Jukebox.TPTP.FindFile
import qualified Data.Map.Strict as Map
import qualified Jukebox.SMTLIB as SMT
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif
import Data.IORef
import Data.Set(Set)
data GlobalFlags =
GlobalFlags {
quiet :: Bool }
deriving Show
globalFlags :: OptionParser GlobalFlags
globalFlags =
inGroup "Output options" $
GlobalFlags <$>
flag "quiet"
["Only print essential information."]
False (pure True)
data TSTPFlags =
TSTPFlags {
tstp :: Bool }
deriving Show
tstpFlags :: OptionParser TSTPFlags
tstpFlags =
inGroup "Output options" $
TSTPFlags <$>
bool "tstp"
["Produce TSTP-compatible output (off by default)."]
False
comment :: String -> IO ()
comment msg = mapM_ putStrLn ["% " ++ line | line <- lines msg]
quietly :: GlobalFlags -> IO () -> IO ()
quietly globals action = unless (quiet globals) action
indent :: String -> String
indent msg =
unlines [" " ++ line | line <- lines msg]
(=>>=) :: OptionParser (a -> IO b) -> OptionParser (b -> IO c) -> OptionParser (a -> IO c)
f =>>= g = (>=>) <$> f <*> g
infixl 1 =>>=
(=>>) :: OptionParser (IO a) -> OptionParser (IO b) -> OptionParser (IO b)
x =>> y = (>>) <$> x <*> y
infixl 1 =>>
forAllFilesBox :: OptionParser ((FilePath -> IO ()) -> IO ())
forAllFilesBox = forAllFiles <$> filenames
forAllFiles :: [FilePath] -> (FilePath -> IO ()) -> IO ()
forAllFiles [] _ = do
hPutStrLn stderr "No input files specified! Try --help."
hPutStrLn stderr "You can use \"-\" to read from standard input."
exitWith (ExitFailure 1)
forAllFiles xs f = mapM_ f xs
readProblemBox :: OptionParser (FilePath -> IO (Problem Form))
readProblemBox = readProblem <$> findFileFlags
readProblem :: [FilePath] -> FilePath -> IO (Problem Form)
readProblem dirs f = do
r <- parseProblem dirs f
case r of
Left err -> do
hPutStrLn stderr err
exitWith (ExitFailure 1)
Right x -> return x
printProblemBox :: OptionParser (Problem Form -> IO ())
printProblemBox = prettyPrintIO showProblem <$> writeFileBox
printProblemSMTBox :: OptionParser (Problem Form -> IO ())
printProblemSMTBox = prettyPrintIO SMT.showProblem <$> writeFileBox
printClausesBox :: OptionParser (Problem Clause -> IO ())
printClausesBox = prettyPrintIO showClauses <$> writeFileBox
prettyPrintIO :: (a -> String) -> (String -> IO ()) -> a -> IO ()
prettyPrintIO shw write prob = do
write (shw prob ++ "\n")
writeFileBox :: OptionParser (String -> IO ())
writeFileBox =
inGroup "Output options" $
flag "output"
["Where to write the output file (stdout by default)."]
putStr
(fmap myWriteFile argFile)
where myWriteFile "/dev/null" _ = return ()
myWriteFile "-" contents = putStr contents
myWriteFile file contents = writeFile file contents
clausifyBox :: OptionParser (Problem Form -> IO CNF)
clausifyBox =
(\flags prob -> return $! clausify flags prob) <$> clausifyFlags
oneConjectureBox :: OptionParser (CNF -> IO (Problem Clause))
oneConjectureBox = oneConjecture <$> flags
where
flags =
inGroup "Input and clausifier options" $
flag "conjecture"
["If the problem has multiple conjectures, take only this one.",
"Conjectures are numbered from 1."]
Nothing (Just <$> argNum)
oneConjecture :: Maybe Int -> CNF -> IO (Problem Clause)
oneConjecture conj cnf =
run cnf $ \(CNF axioms obligs _ _) ->
case conj of
Just n ->
if n <= length obligs then choose axioms (obligs !! (n-1))
else err ["Conjecture number out of bounds:",
"problem after clausification has " ++ show (length obligs) ++ " conjectures"]
Nothing ->
case obligs of
[cs] -> choose axioms cs
_ ->
err ["Can't handle more than one conjecture in input problem!",
"This problem has " ++ show (length obligs) ++ " conjectures.",
"Try using the --conjecture flag."]
where
err msgs = return $ do
mapM_ (hPutStrLn stderr) msgs
exitWith (ExitFailure 1)
choose axioms cs = return (return (axioms ++ cs))
toFormulasBox :: OptionParser (Problem Clause -> IO (Problem Form))
toFormulasBox = pure $ \prob -> return (map (fmap toForm) prob)
type Solver = (IO () -> IO ()) -> Problem Clause -> IO Answer
forAllConjecturesBox :: OptionParser (Solver -> CNF -> IO ())
forAllConjecturesBox = forAllConjectures <$> tstpFlags
forAllConjectures :: TSTPFlags -> Solver -> CNF -> IO ()
forAllConjectures (TSTPFlags tstp) solve CNF{..} = do
todo <- newIORef (return ())
loop 1 todo conjectures Nothing
where loop _ todo [] mref =
result todo (unsatisfiable mref)
loop i todo (c:cs) _ = do
when multi $ do
join (readIORef todo)
writeIORef todo (return ())
answer <- solve (\x -> modifyIORef todo (>> x)) (axioms ++ c)
when multi $ do
putStrLn $ "Partial result (" ++ part i ++ "): " ++ show answer
putStrLn ""
case answer of
Sat _ mmodel -> result todo (satisfiable mmodel)
Unsat _ mref -> loop (i+1) todo cs mref
NoAnswer x -> result todo (NoAnswer x)
multi = length conjectures > 1
part i = show i ++ "/" ++ show (length conjectures)
result todo x = do
when tstp $ do
mapM_ putStrLn (answerSZS x)
putStrLn ""
join (readIORef todo)
putStrLn ("RESULT: " ++ show x ++ " (" ++ explainAnswer x ++ ").")
toFofBox :: OptionParser (Problem Form -> IO (Problem Form))
toFofBox = toFof <$> clausifyBox <*> schemeBox
toFof :: (Problem Form -> IO CNF) -> Scheme -> Problem Form -> IO (Problem Form)
toFof clausify scheme f = do
CNF{..} <- clausify f
m <- monotone (map what (axioms ++ concat conjectures))
let isMonotone ty =
case Map.lookup ty m of
Just Nothing -> False
Just (Just _) -> True
Nothing -> True
return (translate scheme isMonotone f)
schemeBox :: OptionParser Scheme
schemeBox =
inGroup "Options for encoding types" $
flag "encoding"
["Which type encoding to use (guards by default)."]
(const guards)
(argOption
[("guards", const guards),
("tags", tags)])
<*> tagsFlags
analyseMonotonicityBox :: OptionParser (Problem Clause -> IO (Set Type))
analyseMonotonicityBox = pure analyseMonotonicity
showMonotonicityBox :: OptionParser (Problem Clause -> IO String)
showMonotonicityBox =
pure $ \cs -> do
m <- monotone (map what cs)
let info (ty, Nothing) = [base ty ++ ": not monotone"]
info (ty, Just m) =
[prettyShow ty ++ ": monotone"] ++
concat
[ case ext of
CopyExtend -> []
TrueExtend -> [" " ++ base p ++ " true-extended"]
FalseExtend -> [" " ++ base p ++ " false-extended"]
| (p, ext) <- Map.toList m ]
return (unlines (concat (map info (Map.toList m))))
guessModelBox :: OptionParser (Problem Form -> IO (Problem Form))
guessModelBox =
inGroup "Options for the model guesser:" $
(\expansive univ prob -> return (guessModel expansive univ prob))
<$> expansive <*> universe
where universe = flag "universe"
["Which universe to find the model in (peano by default)."]
Peano
(argOption [("peano", Peano), ("trees", Trees)])
expansive = manyFlags "expansive"
["Allow a function to construct 'new' terms in its base case."]
(arg "<function>" "expected a function name" Just)
inferBox :: OptionParser (Problem Clause -> IO (Problem Clause, Type -> Type))
inferBox = pure (\prob -> return (run prob inferTypes))
printInferredBox :: OptionParser ((Problem Clause, Type -> Type) -> IO (Problem Clause))
printInferredBox = pure $ \(prob, rep) -> do
forM_ (types prob) $ \ty ->
putStrLn $ show ty ++ " => " ++ show (rep ty)
return prob
hornToUnitBox :: OptionParser (Problem Clause -> IO (Either Answer (Problem Clause)))
hornToUnitBox = hornToUnitIO <$> hornFlags
hornToUnitIO :: HornFlags -> Problem Clause -> IO (Either Answer (Problem Clause))
hornToUnitIO flags prob = do
res <- hornToUnit flags prob
case res of
Left clause -> do
mapM_ (hPutStrLn stderr) [
"Expected a Horn problem, but the input file contained",
"the following non-Horn clause:",
indent (show (pPrintClauses [clause])) ]
exitWith (ExitFailure 1)
Right prob -> return prob