g2-0.2.0.0: src/G2/Config/Config.hs
module G2.Config.Config ( Mode (..)
, LogMode (..)
, LogMethod (..)
, Sharing (..)
, SMTSolver (..)
, SearchStrategy (..)
, HigherOrderSolver (..)
, IncludePath
, Config (..)
, BoolDef (..)
, mkConfig
, mkConfigDirect
, mkLogMode
, strArg
, boolArg
, boolArg'
, baseDef
, baseSimple) where
import Data.Char
import Data.List
import qualified Data.Map as M
import Data.Monoid ((<>))
import Options.Applicative
import Text.Read
data Mode = Regular | Liquid deriving (Eq, Show, Read)
data LogMode = Log LogMethod String | NoLog deriving (Eq, Show, Read)
data LogMethod = Raw | Pretty deriving (Eq, Show, Read)
-- | Do we use sharing to only reduce variables once?
data Sharing = Sharing | NoSharing deriving (Eq, Show, Read)
data SMTSolver = ConZ3 | ConCVC4 deriving (Eq, Show, Read)
data SearchStrategy = Iterative | Subpath deriving (Eq, Show, Read)
data HigherOrderSolver = AllFuncs
| SingleFunc
| SymbolicFunc
| SymbolicFuncTemplate deriving (Eq, Show, Read)
type IncludePath = FilePath
data Config = Config {
mode :: Mode
, baseInclude :: [IncludePath]
, base :: [FilePath] -- ^ Filepath(s) to base libraries. Get compiled in order from left to right
, extraDefaultInclude :: [IncludePath]
, extraDefaultMods :: [FilePath]
, logStates :: LogMode -- ^ Determines whether to Log states, and if logging states, how to do so.
, sharing :: Sharing
, maxOutputs :: Maybe Int -- ^ Maximum number of examples/counterexamples to output. TODO: Currently works only with LiquidHaskell
, returnsTrue :: Bool -- ^ If True, shows only those inputs that do not return True
, higherOrderSolver :: HigherOrderSolver -- ^ How to try and solve higher order functions
, search_strat :: SearchStrategy -- ^ The search strategy for the symbolic executor to use
, subpath_length :: Int -- ^ When using subpath search strategy, the length of the subpaths.
, smt :: SMTSolver -- ^ Sets the SMT solver to solve constraints with
, steps :: Int -- ^ How many steps to take when running States
, hpc :: Bool -- ^ Should HPC ticks be generated and tracked during execution?
, strict :: Bool -- ^ Should the function output be strictly evaluated?
, timeLimit :: Int -- ^ Seconds
, validate :: Bool -- ^ If True, run on G2's input, and check against expected output.
}
mkConfig :: String -> Parser Config
mkConfig homedir = Config Regular
<$> mkBaseInclude homedir
<*> mkBase homedir
<*> mkExtraDefault homedir
<*> pure []
<*> mkLogMode
<*> flag Sharing NoSharing (long "no-sharing" <> help "disable sharing")
<*> mkMaxOutputs
<*> switch (long "returns-true" <> help "assert that the function returns true, show only those outputs which return false")
<*> mkHigherOrder
<*> mkSearchStrategy
<*> option auto (long "subpath-len"
<> metavar "L"
<> value 4
<> help "when using subpath search strategy, the length of the subpaths")
<*> mkSMTSolver
<*> option auto (long "n"
<> metavar "N"
<> value 1000
<> help "how many steps to take when running states")
<*> flag False True (long "hpc"
<> help "Generate and report on HPC ticks")
<*> flag True False (long "no-strict" <> help "do not evaluate the output strictly")
<*> option auto (long "time"
<> metavar "T"
<> value 600
<> help "time limit, in seconds")
<*> switch (long "validate" <> help "use GHC to automatically compile and run on generated inputs, and check that generated outputs are correct")
mkBaseInclude :: String -> Parser [IncludePath]
mkBaseInclude homedir =
option (eitherReader (Right . baseIncludeDef))
( long "base"
<> metavar "FILE"
<> value (baseIncludeDef homedir)
<> help "where to look for base files")
mkBase :: String -> Parser [IncludePath]
mkBase homedir =
option (eitherReader (Right . baseDef))
( long "base-def"
<> metavar "FILE"
<> value (baseDef homedir)
<> help "where to look for base files")
mkExtraDefault :: String -> Parser [IncludePath]
mkExtraDefault homedir =
option (eitherReader (\v -> Right (v:extraDefaultIncludePaths homedir)))
( long "extra-def"
<> metavar "FILE"
<> value (extraDefaultIncludePaths homedir)
<> help "where to look for base files")
mkLogMode :: Parser LogMode
mkLogMode =
(option (eitherReader (Right . Log Raw))
(long "log-states"
<> metavar "FOLDER"
<> value NoLog
<> help "log all states with raw printing"))
<|>
(option (eitherReader (Right . Log Pretty))
(long "log-pretty"
<> metavar "FOLDER"
<> value NoLog
<> help "log all states with pretty printing"))
mkMaxOutputs :: Parser (Maybe Int)
mkMaxOutputs =
option (maybeReader (Just . readMaybe))
( long "max-outputs"
<> metavar "MAX"
<> value Nothing
<> help "the maximum number of input/output pairs to output")
mkHigherOrder :: Parser HigherOrderSolver
mkHigherOrder =
option (eitherReader (\s -> case s of
"all" -> Right AllFuncs
"single" -> Right SingleFunc
"symbolic" -> Right SymbolicFunc
"symbolic-temp" -> Right SymbolicFuncTemplate
_ -> Left "Unsupported higher order function handling"))
( long "higher-order"
<> metavar "HANDLING"
<> value SingleFunc
<> help "either all or single, to specify whether all possible higher order instantiations should be searched for, or just a single instantiation")
mkSMTSolver :: Parser SMTSolver
mkSMTSolver =
option (eitherReader (\s -> case s of
"z3" -> Right ConZ3
"cvc4" -> Right ConCVC4
_ -> Left "Unsupported SMT solver"))
( long "smt"
<> metavar "SMT-SOLVER"
<> value ConZ3
<> help "either z3 or cvc4, to select the solver to use")
mkSearchStrategy :: Parser SearchStrategy
mkSearchStrategy =
option (eitherReader (\s -> case s of
"iter" -> Right Iterative
"subpath" -> Right Subpath
_ -> Left "Unsupported search strategy"))
( long "search"
<> metavar "SEARCH"
<> value Iterative
<> help "either iter or subpath, to select a search strategy")
mkConfigDirect :: String -> [String] -> M.Map String [String] -> Config
mkConfigDirect homedir as m = Config {
mode = Regular
, baseInclude = baseIncludeDef (strArg "base" as m id homedir)
, base = baseDef (strArg "base" as m id homedir)
, extraDefaultInclude = extraDefaultIncludePaths (strArg "extra-base-inc" as m id homedir)
, extraDefaultMods = []
, logStates = strArg "log-states" as m (Log Raw)
(strArg "log-pretty" as m (Log Pretty) NoLog)
, sharing = boolArg' "sharing" as Sharing Sharing NoSharing
, maxOutputs = strArg "max-outputs" as m (Just . read) Nothing
, returnsTrue = boolArg "returns-true" as m Off
, higherOrderSolver = strArg "higher-order" as m higherOrderSolArg SingleFunc
, search_strat = Iterative
, subpath_length = 4
, smt = strArg "smt" as m smtSolverArg ConZ3
, steps = strArg "n" as m read 1000
, hpc = False
, strict = boolArg "strict" as m On
, timeLimit = strArg "time" as m read 300
, validate = boolArg "validate" as m Off
}
baseIncludeDef :: FilePath -> [FilePath]
baseIncludeDef root =
[ root ++ "/.g2/base-4.9.1.0/Control/Exception/"
, root ++ "/.g2/base-4.9.1.0/"
, root ++ "/.g2/base-4.9.1.0/Data/Internal/"
]
baseDef :: FilePath -> [FilePath]
baseDef root = baseSimple root
baseSimple :: FilePath -> [FilePath]
baseSimple root =
[ root ++ "/.g2/base-4.9.1.0/Control/Exception/Base.hs"
, root ++ "/.g2/base-4.9.1.0/Prelude.hs"
, root ++ "/.g2/base-4.9.1.0/Control/Monad.hs" ]
extraDefaultIncludePaths :: FilePath -> [FilePath]
extraDefaultIncludePaths root =
[ root ++ "/.g2/G2Stubs/src/" ]
smtSolverArg :: String -> SMTSolver
smtSolverArg = smtSolverArg' . map toLower
smtSolverArg' :: String -> SMTSolver
smtSolverArg' "z3" = ConZ3
smtSolverArg' "cvc4" = ConCVC4
smtSolverArg' _ = error "Unrecognized SMT solver."
higherOrderSolArg :: String -> HigherOrderSolver
higherOrderSolArg = higherOrderSolArg' . map toLower
higherOrderSolArg' :: String -> HigherOrderSolver
higherOrderSolArg' "all" = AllFuncs
higherOrderSolArg' "single" = SingleFunc
higherOrderSolArg' _ = error "Unrecognized higher order solver."
data BoolDef = On | Off deriving (Eq, Show)
-- If the given string is on the command line, returns True
-- If --no-[str] is on the command line, returns False
-- otherwise, looks in the config file, and if there is not option there,
-- uses the default to decide
boolArg :: String -> [String] -> M.Map String [String] -> BoolDef -> Bool
boolArg s a m bd =
let
d = if bd == On then True else False
in
if "--" ++ s `elem` a
then True
else if "--no-" ++ s `elem` a
then False
else case M.lookup s m of
Just st -> strToBool st d
Nothing -> d
boolArg' :: String -> [String] -> b -> b -> b -> b
boolArg' s a b_default b1 b2 =
if "--" ++ s `elem` a
then b1
else if "--no-" ++ s `elem` a
then b2
else b_default
strToBool :: [String] -> Bool -> Bool
strToBool [s] b
| s' == "true" = True
| s' == "1" = True
| s' == "false" = False
| s' == "0" = False
| otherwise = b
where
s' = map toLower s
strToBool _ b = b
--Converts strings arguments to arbitrary types
strArg :: String -> [String] -> M.Map String [String] -> (String -> a) -> a -> a
strArg s a m f d =
case elemIndex ("--" ++ s) a of
Just i -> if i >= length a
then error ("Invalid use of " ++ s)
else f (a !! (i + 1))
Nothing -> case M.lookup s m of
Just st -> strToArg st f d
Nothing -> d
strToArg :: [String] -> (String -> a) -> a -> a
strToArg [s] f _ = f s
strToArg _ _ d = d