{- | Module : Process The following module contains method that facilitate the comunication with the SMTSolvers. -} module Hsmtlib.Solvers.Cmd.ProcCom.Process ( beginProcess , send , endProcess , sendContext , sendScript , Process , CmdPath , Args ) where import Control.Exception import GHC.IO.Handle import System.Exit import System.Process -- | Path to the process type CmdPath = String -- | Argumants passed to process type Args = [String] -- |Type returned by CreateProcess type Process = ( Maybe Handle -- process std_in pipe , Maybe Handle -- process std_out pipe , Maybe Handle -- process std_err pipe , ProcessHandle -- process pid ) -- | Context is just a string wich will be sent to std_in type Context = String -- Functions used in Online Mode {- | Generates a CreateProcess with just the command, the arguments and creates the pipes to comunicate -} newProcess :: CmdPath -> Args -> CreateProcess newProcess p a = CreateProcess { cmdspec = RawCommand p a , cwd = Nothing , env = Nothing , std_in = CreatePipe , std_out = CreatePipe , std_err = CreatePipe , close_fds = False , create_group = False } -- | Creates a Process ready to be executed. beginProcess :: CmdPath -> Args -> IO Process beginProcess cmd path = createProcess (newProcess cmd path) -- | trys to run the function. tryIO ::(a -> IO b ) -> a -> IO ( Either IOException b ) tryIO f arg = try $ f arg {-| Sends the desired input to the process std_in and then reads from std out. Working smt with this method: - z3 - mathSat - cvc4 -} send :: Process -> String -> IO String send (Just hIn, Just hOut,_,_) cmd = do let put_str = flip hPutStr cmd resPut <-tryIO put_str hIn -- trys to write to std in case resPut of --If there was an excepion writing then return the error Left exception -> return $ "send1: " ++ show exception Right _ -> do -- if it was successeful resFlush <- tryIO hFlush hIn -- trys to flush std in case resFlush of --if there was an exception flushing then return the error Left exception -> return $ "send2: " ++ show exception --if it was succeful then start reading from the std out Right _ -> readResponse (-1) "" hOut {-| Receive a inital time to wait for the process to write to the handle, a String wich will be added the text read from the handle and the handle. If it was able to read a line from the handle then call the function again but with time equals to 10. Working smt with this methid: - z3 - mathSat - cvc4 -} readResponse :: Int -> String -> Handle -> IO String readResponse time str procHandle = do -- if the process dosent write to std out this function will block. let hWait = flip hWaitForInput time readOut <- tryIO hWait procHandle -- trys to wait for some output in std out. case readOut of -- if the wait gave an exception returns the error. Left exception -> return $ "readResponse1:" ++ show exception Right False -> return str -- returns the lines read until now. Right True -> do -- if there is something to read then trys to read a line. res_get <- tryIO hGetLine procHandle case res_get of -- if there was an exception then return it. Left exception -> return $ "readResponse2:" ++ show exception -- if some text was read then trys to read the pipe again. Right text -> readResponse 10 (str++text) procHandle -- | Sends the signal to terminate to the running process. endProcess :: Process -> IO ExitCode endProcess (_,_,_,processHandle) = do terminateProcess processHandle waitForProcess processHandle -- Function used in ContextMode {-| It's the same function as readProcess. <http://hackage.haskell.org/package/process-1.1.0.1/docs/System-Process.html> -} sendContext :: CmdPath -> Args -> Context -> IO String sendContext = readProcess {-| Calls readProcess and pass as arguments the arguments given plus the name of the file with the context. An empty String is passed to the std_in. It's the same function as readProcess. readProcess: <http://hackage.haskell.org/package/process-1.1.0.1/docs/System-Process.html> -} sendScript :: CmdPath -> Args -> FilePath -> IO String sendScript cmdPath args script_name = readProcess cmdPath (args ++ [script_name]) ""