module Main where import Prelude () import Zeno.Prelude import Zeno.Core import Zeno.HaskellParser import Zeno.Evaluation import Zeno.Proof import Zeno.Checker import Zeno.Solver import Zeno.Isabelle import Zeno.Property import Zeno.Flags import Control.Parallel.Strategies import Data.IORef import System.IO.Unsafe import System.IO import System.Process import System.Directory import System.Exit import System.Environment import System.Timeout import Text.Printf import qualified Data.Map as Map import qualified Data.Text.IO as Text header :: String header = "Zeno 0.2 - An automated theorem prover for Haskell programs." ++ "\nDeveloped at Imperial College London by " ++ "William Sonnex, Sophia Drossopoulou and Susan Eisenbach." ++ "\nhttp://www.haskell.org/haskellwiki/Zeno" main :: IO () main = do !flags <- readFlags <$> getArgs props <- loadProperties <$> loadHaskell flags zeno props zeno :: ZPropertySet -> IO () zeno (PropertySet !pgm !props') = zenoTimeout $ do putStr header >> hFlush stdout when (flagPrintCore flags) $ do putStr "\n\nProgram Code:\n" print pgm putStr "\n\nDefined Properties:\n" forM_ props (putStrLn . showProperty) exitWith ExitSuccess unless (not (null props) || (flagSkipChecker flags && flagSkipSolver flags)) $ do putStr "\n\nNo properties found." unless (null props || flagSkipChecker flags) $ do putStr "\n\nSearching for counter-examples... " hFlush stdout if null falses then putStr "None found." else do forM_ falses $ \(sub, prop) -> printf "\nDisproved \"%s\"\n with %s" (showProperty prop) (showSubstitution sub) unless (null props || flagSkipSolver flags) $ do putStr "\n\nSearching for proofs... " hFlush stdout let proofs = proofsetProofs proofset if null proofs then putStr "None found." else do let showProp (name, proof) = showProperty (name, proofGoal proof) props = map showProp proofs forM_ props (printf "\nProved \"%s\"") unless (null unknowns || (flagSkipSolver flags && flagSkipChecker flags)) $ do putStr "\n\nCould neither prove nor disprove: " let props = map showProperty unknowns forM_ props (printf "\n\"%s\"") when (flagWebMode flags) $ do putStrLn "\n\n\nIsabelle/HOL proofs:\n" Text.putStr proofs_code when (not (flagWebMode flags) && (not (null proven) || flagIsabelleAll flags)) $ do createDirectoryIfMissing True output_dir writeFile ml_path ml_code Text.writeFile proofs_path proofs_code printf "\n\nZeno output at \"%s\"." proofs_path unless (flagSkipIsabelle flags) $ do putStr "\nChecking with Isabelle... " hFlush stdout (exitcode, isa_out, isa_err) <- readProcessWithExitCode "isabelle" ["usedir", "HOL", output_dir] "" case exitcode of ExitSuccess -> putStr "Done." ExitFailure _ -> printf "Failed.%s" isa_err printf "\n" where zenoTimeout :: IO () -> IO () zenoTimeout io | flagTimeout flags <= 0 = io | otherwise = do maybe_io <- timeout (flagTimeout flags * (10^6)) io case maybe_io of Nothing -> putStrLn $ "\nTimed out (" ++ (show . flagTimeout) flags ++ "s)" _ -> return () props | null matches = props' | otherwise = flip filter props' $ \(n, _) -> any (`isInfixOf` n) matches showProperty (name, cls) = name ++ " : " ++ show cls ++ "" flags = programFlags pgm matches = flagMatchWith flags output_dir = flagIsabelleDir flags ml_code = "use_thys [\"Zeno\"]" ml_path = output_dir ++ "/ROOT.ML" proofs_path = output_dir ++ "/Zeno.thy" proofs_code = toIsabelle proofset counter_exs | flagSkipChecker flags = repeat Nothing | otherwise = withStrategy (parList rseq) . flip runReader pgm $ mapM (falsify . snd) props (falses', to_prove') = partition (isJust . fst) (counter_exs `zip` props) to_prove = map snd to_prove' falses = map (first fromJust) falses' maybe_proofs | flagSkipSolver flags = repeat Nothing | otherwise = withStrategy (parList rseq) $ map (solve pgm . snd) to_prove maybe_proven = to_prove `zip` maybe_proofs (proven, unproven) = partition (isJust . snd) maybe_proven proofs = flip map proven $ \((name, _), Just prf) -> (name, prf) proofset = ProofSet pgm proofs unknowns = flip map unproven $ \(prop, Nothing) -> prop