module Jukebox.Toolbox where
import Jukebox.Options
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Jukebox.Form
import Jukebox.Name
import qualified Jukebox.NameMap as NameMap
import Jukebox.TPTP.Print
import Control.Monad
import Control.Applicative
import Jukebox.Clausify
import Jukebox.TPTP.ParseProblem
import Jukebox.Monotonox.Monotonicity hiding (guards)
import Jukebox.Monotonox.ToFOF
import System.Exit
import System.IO
import Jukebox.TPTP.FindFile
import Text.PrettyPrint.HughesPJ
import Jukebox.GuessModel
import Jukebox.InferTypes
import Jukebox.TPTP.Parsec hiding (Error)
import qualified Jukebox.TPTP.Parsec as Parser
import Jukebox.TPTP.ClauseParser
import Jukebox.TPTP.Lexer hiding (Error, name, Normal)
import qualified Jukebox.TPTP.Lexer as Lexer
data GlobalFlags =
GlobalFlags {
quiet :: Bool }
deriving Show
globalFlags :: OptionParser GlobalFlags
globalFlags =
inGroup "Global options" $
GlobalFlags <$>
bool "quiet"
["Do not print any informational output.",
"Default: (off)"]
(=>>=) :: (Monad m, Applicative f) => f (a -> m b) -> f (b -> m c) -> f (a -> m c)
f =>>= g = (>=>) <$> f <*> g
infixl 1 =>>=
(=>>) :: (Monad m, Applicative f) => f (m a) -> f (m b) -> f (m b)
x =>> y = (>>) <$> x <*> y
infixl 1 =>>
greetingBox :: Tool -> OptionParser (IO ())
greetingBox t = greetingBoxIO t <$> globalFlags
greetingBoxIO :: Tool -> GlobalFlags -> IO ()
greetingBoxIO t GlobalFlags{quiet = quiet} =
unless quiet $ hPutStrLn stderr (greeting t)
allFilesBox :: OptionParser ((FilePath -> IO ()) -> IO ())
allFilesBox = flip allFiles <$> filenames
allFiles :: (FilePath -> IO ()) -> [FilePath] -> IO ()
allFiles _ [] = do
hPutStrLn stderr "No input files specified! Try --help."
exitWith (ExitFailure 1)
allFiles f xs = mapM_ f xs
parseProblemBox :: OptionParser (FilePath -> IO (Problem Form))
parseProblemBox = parseProblemIO <$> findFileFlags
parseProblemIO :: [FilePath] -> FilePath -> IO (Problem Form)
parseProblemIO dirs f = do
r <- parseProblem dirs f
case r of
Left err -> do
hPutStrLn stderr err
exitWith (ExitFailure 1)
Right x -> return x
withString :: (Symbolic a, Pretty a) => String -> (Problem Form -> IO (Problem a)) -> String -> IO String
withString kind f x = do
let errorAt (UserState _ (At (Lexer.Pos l c) _)) err =
error $ "At line " ++ show l ++ ", column " ++ show c ++ ": " ++ err
case run_ (section (const True) <* eof)
(UserState initialState (scan (BSL.pack x))) of
Ok (UserState (MkState p _ _ _ _ n) (At _ (Cons Eof _))) Nothing -> do
let prob = close_ n (return (reverse p))
res <- f prob
return (render (prettyProblem kind Normal res))
Ok s@(UserState _ (At _ (Cons Eof _))) (Just _) ->
errorAt s "can't handle include files"
Ok s _ ->
errorAt s "lexical error"
Parser.Error s msg -> errorAt s $ "parse error: " ++ msg
Expected s exp -> errorAt s $ "parse error: expected " ++ show exp
encodeString :: String -> IO String
encodeString = withString "fof" f
where
f = toFofIO globals (return . clausify clFlags) (tags False)
globals = GlobalFlags { quiet = True }
clFlags = ClausifyFlags { splitting = False }
clausifyBox :: OptionParser (Problem Form -> IO CNF)
clausifyBox = clausifyIO <$> globalFlags <*> clausifyFlags
clausifyIO :: GlobalFlags -> ClausifyFlags -> Problem Form -> IO CNF
clausifyIO globals flags prob = do
unless (quiet globals) $ hPutStrLn stderr "Clausifying problem..."
return $! clausify flags prob
toFofBox :: OptionParser (Problem Form -> IO (Problem Form))
toFofBox = toFofIO <$> globalFlags <*> clausifyBox <*> schemeBox
oneConjectureBox :: OptionParser (CNF -> IO (Problem Clause))
oneConjectureBox = pure oneConjecture
oneConjecture :: CNF -> IO (Problem Clause)
oneConjecture cnf = closedIO (close cnf f)
where f (Obligs cs [cs'] _ _) = return (return (cs ++ cs'))
f _ = return $ do
hPutStrLn stderr "Error: more than one conjecture found in input problem"
exitWith (ExitFailure 1)
toFofIO :: GlobalFlags -> (Problem Form -> IO CNF) -> Scheme -> Problem Form -> IO (Problem Form)
toFofIO globals clausify scheme f = do
cs <- clausify f >>= oneConjecture
unless (quiet globals) $ hPutStrLn stderr "Monotonicity analysis..."
m <- monotone (map what (open cs))
let isMonotone ty =
case NameMap.lookup (name ty) m of
Just (_ ::: Nothing) -> False
Just (_ ::: Just _) -> True
Nothing -> True
return (translate scheme isMonotone f)
schemeBox :: OptionParser Scheme
schemeBox =
choose <$>
flag "encoding"
["Which type encoding to use.",
"Default: --encoding guards"]
"guards"
(argOption ["guards", "tags"])
<*> tagsFlags
where choose "guards" flags = guards
choose "tags" flags = tags flags
monotonicityBox :: OptionParser (Problem Clause -> IO String)
monotonicityBox = monotonicity <$> globalFlags
monotonicity :: GlobalFlags -> Problem Clause -> IO String
monotonicity globals cs = do
unless (quiet globals) $ hPutStrLn stderr "Monotonicity analysis..."
m <- monotone (map what (open cs))
let info (ty ::: Nothing) = [BS.unpack (baseName ty) ++ ": not monotone"]
info (ty ::: Just m) =
[prettyShow ty ++ ": monotone"] ++
concat
[ case ext of
CopyExtend -> []
TrueExtend -> [" " ++ BS.unpack (baseName p) ++ " true-extended"]
FalseExtend -> [" " ++ BS.unpack (baseName p) ++ " false-extended"]
| p ::: ext <- NameMap.toList m ]
return (unlines (concat (map info (NameMap.toList m))))
annotateMonotonicityBox :: OptionParser (Problem Clause -> IO (Problem Clause))
annotateMonotonicityBox = (\globals x -> do
unless (quiet globals) $ putStrLn "Monotonicity analysis..."
annotateMonotonicity x) <$> globalFlags
prettyPrintBox :: (Symbolic a, Pretty a) => OptionParser (Problem a -> IO ())
prettyPrintBox = prettyFormIO <$> globalFlags <*> writeFileBox
prettyFormIO :: (Symbolic a, Pretty a) => GlobalFlags -> (String -> IO ()) -> Problem a -> IO ()
prettyFormIO globals write prob
| isFof (open prob) = prettyPrintIO globals "fof" write prob
| otherwise = prettyPrintIO globals "tff" write prob
prettyClauseBox :: OptionParser (Problem Clause -> IO ())
prettyClauseBox = f <$> globalFlags <*> writeFileBox
where
f globals write cs
| isFof (open cs) = prettyPrintIO globals "cnf" write cs
| otherwise = prettyPrintIO globals "tff" write (fmap (map (fmap toForm)) cs)
prettyPrintIO :: (Symbolic a, Pretty a) => GlobalFlags -> String -> (String -> IO ()) -> Problem a -> IO ()
prettyPrintIO globals kind write prob = do
unless (quiet globals) $ hPutStrLn stderr "Writing output..."
write (render (prettyProblem kind Normal prob) ++ "\n")
writeFileBox :: OptionParser (String -> IO ())
writeFileBox =
flag "output"
["Where to write the output.",
"Default: stdout"]
putStr
(fmap myWriteFile argFile)
where myWriteFile "/dev/null" _ = return ()
myWriteFile file contents = writeFile file contents
guessModelBox :: OptionParser (Problem Form -> IO (Problem Form))
guessModelBox = guessModelIO <$> expansive <*> universe
where universe = choose <$>
flag "universe"
["Which universe to find the model in.",
"Default: peano"]
"peano"
(argOption ["peano", "trees"])
choose "peano" = Peano
choose "trees" = Trees
expansive = manyFlags "expansive"
["Allow a function to construct 'new' terms in its base base."]
(arg "<function>" "expected a function name" Just)
guessModelIO :: [String] -> Universe -> Problem Form -> IO (Problem Form)
guessModelIO expansive univ prob = return (guessModel expansive univ prob)
allObligsBox :: OptionParser ((Problem Clause -> IO Answer) -> Closed Obligs -> IO ())
allObligsBox = pure allObligsIO
allObligsIO solve obligs = loop 1 conjectures
where Obligs { axioms = axioms, conjectures = conjectures,
satisfiable = satisfiable, unsatisfiable = unsatisfiable } =
open obligs
loop _ [] = result unsatisfiable
loop i (c:cs) = do
when multi $ putStrLn $ "Part " ++ part i
answer <- solve (close_ obligs (return (axioms ++ c)))
when multi $ putStrLn $ "+++ PARTIAL (" ++ part i ++ "): " ++ show answer
case answer of
Satisfiable -> result satisfiable
Unsatisfiable -> loop (i+1) cs
NoAnswer x -> result (show x)
multi = length conjectures > 1
part i = show i ++ "/" ++ show (length conjectures)
result x = putStrLn ("+++ RESULT: " ++ x)
inferBox :: OptionParser (Problem Clause -> IO (Problem Clause, Type -> Type))
inferBox = (\globals prob -> do
unless (quiet globals) $ putStrLn "Inferring types..."
let prob' = close prob inferTypes
return (fmap fst prob', snd (open prob'))) <$> globalFlags
printInferredBox :: OptionParser ((Problem Clause, Type -> Type) -> IO (Problem Clause))
printInferredBox = pure $ \(prob, rep) -> do
forM_ (types (open prob)) $ \ty ->
putStrLn $ show ty ++ " => " ++ show (rep ty)
return prob
equinoxBox :: OptionParser (Problem Clause -> IO Answer)
equinoxBox = pure (\f -> return (NoAnswer GaveUp))