-- | This module implements the top-level API for interfacing with Fixpoint -- In particular it exports the functions that solve constraints supplied -- either as .fq files or as FInfo. {-# LANGUAGE CPP #-} module Language.Fixpoint.Interface ( -- * Containing Constraints FInfo (..) -- * Invoke Solver on an FInfo , solve -- * Invoke Solver on a .fq file , solveFQ -- * Function to determine outcome , resultExit -- * Parse Qualifiers from File , parseFInfo ) where import Control.Monad (when) import qualified Data.HashMap.Strict as M import Data.List hiding (partition) #if __GLASGOW_HASKELL__ < 710 import Data.Functor import Data.Monoid (mconcat, mempty) import Data.Hashable import System.Directory (getTemporaryDirectory) import System.FilePath (()) #endif import System.Exit import System.IO (IOMode (..), hPutStr, withFile) import Text.Printf import Language.Fixpoint.Solver.Eliminate (eliminateAll) import Language.Fixpoint.Solver.Uniqify (renameAll) import qualified Language.Fixpoint.Solver.Solve as S import Language.Fixpoint.Config hiding (solver) import Language.Fixpoint.Files hiding (Result) import Language.Fixpoint.Misc import Language.Fixpoint.Statistics (statistics) import Language.Fixpoint.Partition (partition) import Language.Fixpoint.Parse (rr, rr') import Language.Fixpoint.Types hiding (kuts, lits) import Language.Fixpoint.Errors (exit) import Language.Fixpoint.PrettyPrint (showpp) import System.Console.CmdArgs.Verbosity hiding (Loud) import Text.PrettyPrint.HughesPJ --------------------------------------------------------------------------- -- | Solve .fq File ------------------------------------------------------- --------------------------------------------------------------------------- solveFQ :: Config -> IO ExitCode --------------------------------------------------------------------------- solveFQ cfg | native cfg = solveNative cfg (solve cfg) | otherwise = solveFile cfg --------------------------------------------------------------------------- -- | Solve FInfo system of horn-clause constraints ------------------------ --------------------------------------------------------------------------- -- parts cfg = partition cfg x -- stats cfg = statistics cfg x -- native cfg = solveNativeWithFInfo cfg x -- otherwise = solveExt cfg x solve :: (Fixpoint a) => Config -> FInfo a -> IO (Result a) solve cfg | parts cfg = partition cfg | stats cfg = statistics cfg | native cfg = solveNativeWithFInfo cfg | otherwise = solveExt cfg --------------------------------------------------------------------------- -- | Native Haskell Solver --------------------------------------------------------------------------- solveNative :: Config -> (FInfo () -> IO (Result ())) -> IO ExitCode solveNative cfg s = exit (ExitFailure 2) $ do let file = inFile cfg str <- readFile file let fi = rr' file str :: FInfo () res <- s fi return $ resultExit (resStatus res) solveNativeWithFInfo :: (Fixpoint a) => Config -> FInfo a -> IO (Result a) solveNativeWithFInfo cfg fi = do whenLoud $ putStrLn $ "fq file in: \n" ++ render (toFixpoint cfg fi) donePhase Loud "Read Constraints" let fi' = renameAll fi whenLoud $ putStrLn $ "fq file after uniqify: \n" ++ render (toFixpoint cfg fi') donePhase Loud "Uniqify" fi'' <- elim cfg fi' donePhase Loud "Eliminate" whenLoud $ putStrLn $ "fq file after eliminate: \n" ++ render (toFixpoint cfg fi') Result stat soln <- S.solve cfg fi'' donePhase Loud "Solve" let stat' = sid <$> stat putStrLn $ "Solution:\n" ++ showpp soln putStrLn $ "Result: " ++ show stat' return $ Result stat soln elim :: (Fixpoint a) => Config -> FInfo a -> IO (FInfo a) elim cfg fi | eliminate cfg = do let fi' = eliminateAll fi whenLoud $ putStrLn $ "fq file after eliminate: \n" ++ render (toFixpoint cfg fi') return fi' | otherwise = return fi --------------------------------------------------------------------------- -- | External Ocaml Solver --------------------------------------------------------------------------- solveExt :: (Fixpoint a) => Config -> FInfo a -> IO (Result a) solveExt cfg fi = {-# SCC "Solve" #-} execFq cfg fn fi >>= {-# SCC "exitFq" #-} exitFq fn (cm fi) where fn = srcFile cfg execFq :: (Fixpoint a) => Config -> FilePath -> FInfo a -> IO ExitCode execFq cfg fn fi = do writeFile fq qstr withFile fq AppendMode (\h -> {-# SCC "HPrintDump" #-} hPutStr h (render d)) solveFile $ cfg `withTarget` fq where fq = extFileName Fq fn d = {-# SCC "FixPointify" #-} toFixpoint cfg fi qstr = render (vcat (toFix <$> quals fi) $$ text "\n") solveFile :: Config -> IO ExitCode solveFile cfg = do fp <- getFixpointPath z3 <- getZ3LibPath v <- (\b -> if b then "-v 1" else "") <$> isLoud {-# SCC "sysCall:Fixpoint" #-} executeShellCommand "fixpoint" $ fixCommand fp z3 v where fixCommand fp z3 verbosity = printf "LD_LIBRARY_PATH=%s %s %s %s -notruekvars -refinesort -nosimple -strictsortcheck -sortedquals %s" z3 fp verbosity rf (command cfg) where rf = if real cfg then realFlags else "" realFlags :: String realFlags = "-no-uif-multiply " ++ "-no-uif-divide " exitFq :: FilePath -> M.HashMap Integer (SubC a) -> ExitCode -> IO (Result a) exitFq _ _ (ExitFailure n) | n /= 1 = return $ Result (Crash [] "Unknown Error") M.empty exitFq fn z _ = do str <- {-# SCC "readOut" #-} readFile (extFileName Out fn) let (x, y) = parseFixpointOutput str let x' = fmap (mlookup z) x return $ Result x' y parseFixpointOutput :: String -> (FixResult Integer, FixSolution) parseFixpointOutput str = {-# SCC "parseFixOut" #-} rr ({-# SCC "sanitizeFixpointOutput" #-} sanitizeFixpointOutput str) sanitizeFixpointOutput :: String -> String sanitizeFixpointOutput = unlines . filter (not . ("//" `isPrefixOf`)) . chopAfter ("//QUALIFIERS" `isPrefixOf`) . lines --------------------------------------------------------------------------- resultExit :: FixResult a -> ExitCode --------------------------------------------------------------------------- resultExit Safe = ExitSuccess resultExit (Unsafe _) = ExitFailure 1 resultExit _ = ExitFailure 2 --------------------------------------------------------------------------- -- | Parse External Qualifiers -------------------------------------------- --------------------------------------------------------------------------- parseFInfo :: [FilePath] -> IO (FInfo a) -- [Qualifier] --------------------------------------------------------------------------- parseFInfo fs = mconcat <$> mapM parseFI fs parseFI :: FilePath -> IO (FInfo a) --[Qualifier] parseFI f = do str <- readFile f let fi = rr' f str :: FInfo () return $ mempty { quals = quals fi , gs = gs fi }