{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
module Cryptol.REPL.Monad (
REPL(..), runREPL
, io
, raise
, stop
, catch
, finally
, rPutStrLn
, rPutStr
, rPrint
, REPLException(..)
, rethrowEvalError
, getFocusedEnv
, getModuleEnv, setModuleEnv
, getDynEnv, setDynEnv
, uniqify, freshName
, whenDebug
, getExprNames
, getTypeNames
, getPropertyNames
, getModNames
, LoadedModule(..), getLoadedMod, setLoadedMod, clearLoadedMod
, setEditPath, getEditPath, clearEditPath
, setSearchPath, prependSearchPath
, getPrompt
, shouldContinue
, unlessBatch
, asBatch
, disableLet
, enableLet
, getLetEnabled
, validEvalContext
, updateREPLTitle
, setUpdateREPLTitle
, EnvVal(..)
, OptionDescr(..)
, setUser, getUser, getKnownUser, tryGetUser
, userOptions
, getUserSatNum
, getUserShowProverStats
, getUserProverValidate
, parsePPFloatFormat
, getProverConfig
, getPutStr
, getLogger
, setPutStr
, smokeTest
, Smoke(..)
) where
import Cryptol.REPL.Trie
import Cryptol.Eval (EvalError, Unsupported)
import qualified Cryptol.ModuleSystem as M
import qualified Cryptol.ModuleSystem.Env as M
import qualified Cryptol.ModuleSystem.Name as M
import qualified Cryptol.ModuleSystem.NamingEnv as M
import Cryptol.Parser (ParseError,ppError)
import Cryptol.Parser.NoInclude (IncludeError,ppIncludeError)
import Cryptol.Parser.NoPat (Error)
import Cryptol.Parser.Position (emptyRange, Range(from))
import qualified Cryptol.TypeCheck.AST as T
import qualified Cryptol.TypeCheck as T
import qualified Cryptol.IR.FreeVars as T
import qualified Cryptol.Utils.Ident as I
import Cryptol.Utils.PP
import Cryptol.Utils.Panic (panic)
import Cryptol.Utils.Logger(Logger, logPutStr, funLogger)
import qualified Cryptol.Parser.AST as P
import Cryptol.Symbolic (SatNum(..))
import Cryptol.Symbolic.SBV (SBVPortfolioException)
import Cryptol.Symbolic.What4 (W4Exception)
import Cryptol.Eval.Monad(PPFloatFormat(..),PPFloatExp(..))
import qualified Cryptol.Symbolic.SBV as SBV (proverNames, setupProver, defaultProver, SBVProverConfig)
import qualified Cryptol.Symbolic.What4 as W4 (proverNames, setupProver, W4ProverConfig)
import Control.Monad (ap,unless,when)
import Control.Monad.Base
import qualified Control.Monad.Catch as Ex
import Control.Monad.IO.Class
import Control.Monad.Trans.Control
import Data.Char (isSpace, toLower)
import Data.IORef
(IORef,newIORef,readIORef,modifyIORef,atomicModifyIORef)
import Data.List (intercalate, isPrefixOf, unfoldr, sortBy)
import Data.Maybe (catMaybes)
import Data.Ord (comparing)
import Data.Typeable (Typeable)
import System.Directory (findExecutable)
import qualified Control.Exception as X
import qualified Data.Map as Map
import qualified Data.Set as Set
import Text.Read (readMaybe)
import Data.SBV (SBVException)
import Prelude ()
import Prelude.Compat
data LoadedModule = LoadedModule
{ lName :: Maybe P.ModName
, lPath :: M.ModulePath
}
data RW = RW
{ eLoadedMod :: Maybe LoadedModule
, eEditFile :: Maybe FilePath
, eContinue :: Bool
, eIsBatch :: Bool
, eModuleEnv :: M.ModuleEnv
, eUserEnv :: UserEnv
, eLogger :: Logger
, eLetEnabled :: Bool
, eUpdateTitle :: REPL ()
, eProverConfig :: Either SBV.SBVProverConfig W4.W4ProverConfig
}
defaultRW :: Bool -> Logger -> IO RW
defaultRW isBatch l = do
env <- M.initialModuleEnv
return RW
{ eLoadedMod = Nothing
, eEditFile = Nothing
, eContinue = True
, eIsBatch = isBatch
, eModuleEnv = env
, eUserEnv = mkUserEnv userOptions
, eLogger = l
, eLetEnabled = True
, eUpdateTitle = return ()
, eProverConfig = Left SBV.defaultProver
}
mkPrompt :: RW -> String
mkPrompt rw
| eIsBatch rw = ""
| detailedPrompt = withEdit ++ "> "
| otherwise = modLn ++ "> "
where
detailedPrompt = False
modLn =
case lName =<< eLoadedMod rw of
Nothing -> show (pp I.preludeName)
Just m
| M.isLoadedParamMod m (M.meLoadedModules (eModuleEnv rw)) ->
modName ++ "(parameterized)"
| otherwise -> modName
where modName = pretty m
withFocus =
case eLoadedMod rw of
Nothing -> modLn
Just m ->
case (lName m, lPath m) of
(Nothing, M.InFile f) -> ":r to reload " ++ show f ++ "\n" ++ modLn
_ -> modLn
withEdit =
case eEditFile rw of
Nothing -> withFocus
Just e
| Just (M.InFile f) <- lPath <$> eLoadedMod rw
, f == e -> withFocus
| otherwise -> ":e to edit " ++ e ++ "\n" ++ withFocus
newtype REPL a = REPL { unREPL :: IORef RW -> IO a }
runREPL :: Bool -> Logger -> REPL a -> IO a
runREPL isBatch l m = do
ref <- newIORef =<< defaultRW isBatch l
unREPL m ref
instance Functor REPL where
{-# INLINE fmap #-}
fmap f m = REPL (\ ref -> fmap f (unREPL m ref))
instance Applicative REPL where
{-# INLINE pure #-}
pure = return
{-# INLINE (<*>) #-}
(<*>) = ap
instance Monad REPL where
{-# INLINE return #-}
return x = REPL (\_ -> return x)
{-# INLINE (>>=) #-}
m >>= f = REPL $ \ref -> do
x <- unREPL m ref
unREPL (f x) ref
instance MonadIO REPL where
liftIO = io
instance MonadBase IO REPL where
liftBase = liftIO
instance MonadBaseControl IO REPL where
type StM REPL a = a
liftBaseWith f = REPL $ \ref ->
f $ \m -> unREPL m ref
restoreM x = return x
instance M.FreshM REPL where
liftSupply f = modifyRW $ \ RW { .. } ->
let (a,s') = f (M.meSupply eModuleEnv)
in (RW { eModuleEnv = eModuleEnv { M.meSupply = s' }, .. },a)
instance Ex.MonadThrow REPL where
throwM e = liftIO $ X.throwIO e
instance Ex.MonadCatch REPL where
catch op handler = control $ \runInBase -> Ex.catch (runInBase op) (runInBase . handler)
instance Ex.MonadMask REPL where
mask op = REPL $ \ref -> Ex.mask $ \u -> unREPL (op (q u)) ref
where q u (REPL b) = REPL (\ref -> u (b ref))
uninterruptibleMask op = REPL $ \ref ->
Ex.uninterruptibleMask $ \u -> unREPL (op (q u)) ref
where q u (REPL b) = REPL (\ref -> u (b ref))
generalBracket acq rls op = control $ \runInBase ->
Ex.generalBracket (runInBase acq)
(\saved -> \e -> runInBase (restoreM saved >>= \a -> rls a e))
(\saved -> runInBase (restoreM saved >>= op))
data REPLException
= ParseError ParseError
| FileNotFound FilePath
| DirectoryNotFound FilePath
| NoPatError [Error]
| NoIncludeError [IncludeError]
| EvalError EvalError
| Unsupported Unsupported
| ModuleSystemError NameDisp M.ModuleError
| EvalPolyError T.Schema
| TypeNotTestable T.Type
| EvalInParamModule [M.Name]
| SBVError String
| SBVException SBVException
| SBVPortfolioException SBVPortfolioException
| W4Exception W4Exception
deriving (Show,Typeable)
instance X.Exception REPLException
instance PP REPLException where
ppPrec _ re = case re of
ParseError e -> ppError e
FileNotFound path -> sep [ text "File"
, text ("`" ++ path ++ "'")
, text"not found"
]
DirectoryNotFound path -> sep [ text "Directory"
, text ("`" ++ path ++ "'")
, text"not found or not a directory"
]
NoPatError es -> vcat (map pp es)
NoIncludeError es -> vcat (map ppIncludeError es)
ModuleSystemError ns me -> fixNameDisp ns (pp me)
EvalError e -> pp e
Unsupported e -> pp e
EvalPolyError s -> text "Cannot evaluate polymorphic value."
$$ text "Type:" <+> pp s
TypeNotTestable t -> text "The expression is not of a testable type."
$$ text "Type:" <+> pp t
EvalInParamModule xs ->
text "Expression depends on definitions from a parameterized module:"
$$ nest 2 (vcat (map pp xs))
SBVError s -> text "SBV error:" $$ text s
SBVException e -> text "SBV exception:" $$ text (show e)
SBVPortfolioException e -> text "SBV exception:" $$ text (show e)
W4Exception e -> text "What4 exception:" $$ text (show e)
raise :: REPLException -> REPL a
raise exn = io (X.throwIO exn)
catch :: REPL a -> (REPLException -> REPL a) -> REPL a
catch m k = REPL (\ ref -> rethrowEvalError (unREPL m ref) `X.catch` \ e -> unREPL (k e) ref)
finally :: REPL a -> REPL b -> REPL a
finally m1 m2 = REPL (\ref -> unREPL m1 ref `X.finally` unREPL m2 ref)
rethrowEvalError :: IO a -> IO a
rethrowEvalError m = run `X.catch` rethrow `X.catch` rethrowUnsupported
where
run = do
a <- m
return $! a
rethrow :: EvalError -> IO a
rethrow exn = X.throwIO (EvalError exn)
rethrowUnsupported :: Unsupported -> IO a
rethrowUnsupported exn = X.throwIO (Unsupported exn)
io :: IO a -> REPL a
io m = REPL (\ _ -> m)
getRW :: REPL RW
getRW = REPL readIORef
modifyRW :: (RW -> (RW,a)) -> REPL a
modifyRW f = REPL (\ ref -> atomicModifyIORef ref f)
modifyRW_ :: (RW -> RW) -> REPL ()
modifyRW_ f = REPL (\ ref -> modifyIORef ref f)
getPrompt :: REPL String
getPrompt = mkPrompt `fmap` getRW
clearLoadedMod :: REPL ()
clearLoadedMod = do modifyRW_ (\rw -> rw { eLoadedMod = upd <$> eLoadedMod rw })
updateREPLTitle
where upd x = x { lName = Nothing }
setLoadedMod :: LoadedModule -> REPL ()
setLoadedMod n = do
modifyRW_ (\ rw -> rw { eLoadedMod = Just n })
updateREPLTitle
getLoadedMod :: REPL (Maybe LoadedModule)
getLoadedMod = eLoadedMod `fmap` getRW
setEditPath :: FilePath -> REPL ()
setEditPath p = modifyRW_ $ \rw -> rw { eEditFile = Just p }
getEditPath :: REPL (Maybe FilePath)
getEditPath = eEditFile <$> getRW
clearEditPath :: REPL ()
clearEditPath = modifyRW_ $ \rw -> rw { eEditFile = Nothing }
setSearchPath :: [FilePath] -> REPL ()
setSearchPath path = do
me <- getModuleEnv
setModuleEnv $ me { M.meSearchPath = path }
prependSearchPath :: [FilePath] -> REPL ()
prependSearchPath path = do
me <- getModuleEnv
setModuleEnv $ me { M.meSearchPath = path ++ M.meSearchPath me }
getProverConfig :: REPL (Either SBV.SBVProverConfig W4.W4ProverConfig)
getProverConfig = eProverConfig <$> getRW
shouldContinue :: REPL Bool
shouldContinue = eContinue `fmap` getRW
stop :: REPL ()
stop = modifyRW_ (\ rw -> rw { eContinue = False })
unlessBatch :: REPL () -> REPL ()
unlessBatch body = do
rw <- getRW
unless (eIsBatch rw) body
asBatch :: REPL a -> REPL a
asBatch body = do
wasBatch <- eIsBatch `fmap` getRW
modifyRW_ $ (\ rw -> rw { eIsBatch = True })
a <- body
modifyRW_ $ (\ rw -> rw { eIsBatch = wasBatch })
return a
disableLet :: REPL ()
disableLet = modifyRW_ (\ rw -> rw { eLetEnabled = False })
enableLet :: REPL ()
enableLet = modifyRW_ (\ rw -> rw { eLetEnabled = True })
getLetEnabled :: REPL Bool
getLetEnabled = fmap eLetEnabled getRW
validEvalContext :: T.FreeVars a => a -> REPL ()
validEvalContext a =
do me <- eModuleEnv <$> getRW
let ds = T.freeVars a
badVals = foldr badName Set.empty (T.valDeps ds)
bad = foldr badName badVals (T.tyDeps ds)
badName nm bs =
case M.nameInfo nm of
M.Declared m _
| M.isLoadedParamMod m (M.meLoadedModules me) -> Set.insert nm bs
_ -> bs
unless (Set.null bad) $
raise (EvalInParamModule (Set.toList bad))
updateREPLTitle :: REPL ()
updateREPLTitle = unlessBatch $ do
rw <- getRW
eUpdateTitle rw
setUpdateREPLTitle :: REPL () -> REPL ()
setUpdateREPLTitle m = modifyRW_ (\rw -> rw { eUpdateTitle = m })
setPutStr :: (String -> IO ()) -> REPL ()
setPutStr fn = modifyRW_ $ \rw -> rw { eLogger = funLogger fn }
getPutStr :: REPL (String -> IO ())
getPutStr =
do rw <- getRW
return (logPutStr (eLogger rw))
getLogger :: REPL Logger
getLogger = eLogger <$> getRW
rPutStr :: String -> REPL ()
rPutStr str = do
f <- getPutStr
io (f str)
rPutStrLn :: String -> REPL ()
rPutStrLn str = rPutStr $ str ++ "\n"
rPrint :: Show a => a -> REPL ()
rPrint x = rPutStrLn (show x)
getFocusedEnv :: REPL M.ModContext
getFocusedEnv = M.focusedEnv <$> getModuleEnv
getExprNames :: REPL [String]
getExprNames =
do fNames <- M.mctxNames <$> getFocusedEnv
return (map (show . pp) (Map.keys (M.neExprs fNames)))
getTypeNames :: REPL [String]
getTypeNames =
do fNames <- M.mctxNames <$> getFocusedEnv
return (map (show . pp) (Map.keys (M.neTypes fNames)))
getPropertyNames :: REPL ([M.Name],NameDisp)
getPropertyNames =
do fe <- getFocusedEnv
let xs = M.ifDecls (M.mctxDecls fe)
ps = sortBy (comparing (from . M.nameLoc))
[ x | (x,d) <- Map.toList xs,
T.PragmaProperty `elem` M.ifDeclPragmas d ]
return (ps, M.mctxNameDisp fe)
getModNames :: REPL [I.ModName]
getModNames =
do me <- getModuleEnv
return (map T.mName (M.loadedModules me))
getModuleEnv :: REPL M.ModuleEnv
getModuleEnv = eModuleEnv `fmap` getRW
setModuleEnv :: M.ModuleEnv -> REPL ()
setModuleEnv me = modifyRW_ (\rw -> rw { eModuleEnv = me })
getDynEnv :: REPL M.DynamicEnv
getDynEnv = (M.meDynEnv . eModuleEnv) `fmap` getRW
setDynEnv :: M.DynamicEnv -> REPL ()
setDynEnv denv = do
me <- getModuleEnv
setModuleEnv (me { M.meDynEnv = denv })
uniqify :: M.Name -> REPL M.Name
uniqify name =
case M.nameInfo name of
M.Declared ns s ->
M.liftSupply (M.mkDeclared ns s (M.nameIdent name) (M.nameFixity name) (M.nameLoc name))
M.Parameter ->
panic "[REPL] uniqify" ["tried to uniqify a parameter: " ++ pretty name]
freshName :: I.Ident -> M.NameSource -> REPL M.Name
freshName i sys =
M.liftSupply (M.mkDeclared I.interactiveName sys i Nothing emptyRange)
type UserEnv = Map.Map String EnvVal
data EnvVal
= EnvString String
| EnvProg String [String]
| EnvNum !Int
| EnvBool Bool
deriving (Show)
mkUserEnv :: OptionMap -> UserEnv
mkUserEnv opts = Map.fromList $ do
opt <- leaves opts
return (optName opt, optDefault opt)
setUser :: String -> String -> REPL ()
setUser name val = case lookupTrieExact name userOptions of
[opt] -> setUserOpt opt
[] -> rPutStrLn ("Unknown env value `" ++ name ++ "`")
_ -> rPutStrLn ("Ambiguous env value `" ++ name ++ "`")
where
setUserOpt opt = case optDefault opt of
EnvString _ -> doCheck (EnvString val)
EnvProg _ _ ->
case splitOptArgs val of
prog:args -> doCheck (EnvProg prog args)
[] -> rPutStrLn ("Failed to parse command for field, `" ++ name ++ "`")
EnvNum _ -> case reads val of
[(x,_)] -> doCheck (EnvNum x)
_ -> rPutStrLn ("Failed to parse number for field, `" ++ name ++ "`")
EnvBool _
| any (`isPrefixOf` val) ["enable", "on", "yes", "true"] ->
writeEnv (EnvBool True)
| any (`isPrefixOf` val) ["disable", "off", "no", "false"] ->
writeEnv (EnvBool False)
| otherwise ->
rPutStrLn ("Failed to parse boolean for field, `" ++ name ++ "`")
where
doCheck v = do (r,ws) <- optCheck opt v
case r of
Just err -> rPutStrLn err
Nothing -> do mapM_ rPutStrLn ws
writeEnv v
writeEnv ev =
do optEff opt ev
modifyRW_ (\rw -> rw { eUserEnv = Map.insert (optName opt) ev (eUserEnv rw) })
splitOptArgs :: String -> [String]
splitOptArgs = unfoldr (parse "")
where
parse acc (c:cs) | isQuote c = quoted (c:acc) cs
| not (isSpace c) = parse (c:acc) cs
| otherwise = result acc cs
parse acc [] = result acc []
quoted acc (c:cs) | isQuote c = parse (c:acc) cs
| otherwise = quoted (c:acc) cs
quoted acc [] = result acc []
result [] [] = Nothing
result [] cs = parse [] (dropWhile isSpace cs)
result acc cs = Just (reverse acc, dropWhile isSpace cs)
isQuote :: Char -> Bool
isQuote c = c `elem` ("'\"" :: String)
tryGetUser :: String -> REPL (Maybe EnvVal)
tryGetUser name = do
rw <- getRW
return (Map.lookup name (eUserEnv rw))
getUser :: String -> REPL EnvVal
getUser name = do
mb <- tryGetUser name
case mb of
Just ev -> return ev
Nothing -> panic "[REPL] getUser" ["option `" ++ name ++ "` does not exist"]
getKnownUser :: IsEnvVal a => String -> REPL a
getKnownUser x = fromEnvVal <$> getUser x
class IsEnvVal a where
fromEnvVal :: EnvVal -> a
instance IsEnvVal Bool where
fromEnvVal x = case x of
EnvBool b -> b
_ -> badIsEnv "Bool"
instance IsEnvVal Int where
fromEnvVal x = case x of
EnvNum b -> b
_ -> badIsEnv "Num"
instance IsEnvVal (String,[String]) where
fromEnvVal x = case x of
EnvProg b bs -> (b,bs)
_ -> badIsEnv "Prog"
instance IsEnvVal String where
fromEnvVal x = case x of
EnvString b -> b
_ -> badIsEnv "String"
badIsEnv :: String -> a
badIsEnv x = panic "fromEnvVal" [ "[REPL] Expected a `" ++ x ++ "` value." ]
getUserShowProverStats :: REPL Bool
getUserShowProverStats = getKnownUser "prover-stats"
getUserProverValidate :: REPL Bool
getUserProverValidate = getKnownUser "prover-validate"
type OptionMap = Trie OptionDescr
mkOptionMap :: [OptionDescr] -> OptionMap
mkOptionMap = foldl insert emptyTrie
where
insert m d = insertTrie (optName d) d m
type Checker = EnvVal -> REPL (Maybe String, [String])
noCheck :: Checker
noCheck _ = return (Nothing, [])
noWarns :: Maybe String -> REPL (Maybe String, [String])
noWarns mb = return (mb, [])
data OptionDescr = OptionDescr
{ optName :: String
, optDefault :: EnvVal
, optCheck :: Checker
, optHelp :: String
, optEff :: EnvVal -> REPL ()
}
simpleOpt :: String -> EnvVal -> Checker -> String -> OptionDescr
simpleOpt optName optDefault optCheck optHelp =
OptionDescr { optEff = \ _ -> return (), .. }
userOptions :: OptionMap
userOptions = mkOptionMap
[ simpleOpt "base" (EnvNum 16) checkBase
"The base to display words at (2, 8, 10, or 16)."
, simpleOpt "debug" (EnvBool False) noCheck
"Enable debugging output."
, simpleOpt "ascii" (EnvBool False) noCheck
"Whether to display 7- or 8-bit words using ASCII notation."
, simpleOpt "infLength" (EnvNum 5) checkInfLength
"The number of elements to display for infinite sequences."
, simpleOpt "tests" (EnvNum 100) noCheck
"The number of random tests to try with ':check'."
, simpleOpt "satNum" (EnvString "1") checkSatNum
"The maximum number of :sat solutions to display (\"all\" for no limit)."
, simpleOpt "prover" (EnvString "z3") checkProver $
"The external SMT solver for ':prove' and ':sat'\n(" ++ proverListString ++ ")."
, simpleOpt "warnDefaulting" (EnvBool False) noCheck
"Choose whether to display warnings when defaulting."
, simpleOpt "warnShadowing" (EnvBool True) noCheck
"Choose whether to display warnings when shadowing symbols."
, simpleOpt "smtfile" (EnvString "-") noCheck
"The file to use for SMT-Lib scripts (for debugging or offline proving).\nUse \"-\" for stdout."
, OptionDescr "mono-binds" (EnvBool True) noCheck
"Whether or not to generalize bindings in a 'where' clause." $
\case EnvBool b -> do me <- getModuleEnv
setModuleEnv me { M.meMonoBinds = b }
_ -> return ()
, OptionDescr "tc-solver" (EnvProg "z3" [ "-smt2", "-in" ])
noCheck
"The solver that will be used by the type checker." $
\case EnvProg prog args -> do me <- getModuleEnv
let cfg = M.meSolverConfig me
setModuleEnv me { M.meSolverConfig =
cfg { T.solverPath = prog
, T.solverArgs = args } }
_ -> return ()
, OptionDescr "tc-debug" (EnvNum 0)
noCheck
(unlines
[ "Enable type-checker debugging output:"
, " * 0 no debug output"
, " * 1 show type-checker debug info"
, " * >1 show type-checker debug info and interactions with SMT solver"]) $
\case EnvNum n -> do me <- getModuleEnv
let cfg = M.meSolverConfig me
setModuleEnv me { M.meSolverConfig = cfg{ T.solverVerbose = n } }
_ -> return ()
, OptionDescr "core-lint" (EnvBool False)
noCheck
"Enable sanity checking of type-checker." $
let setIt x = do me <- getModuleEnv
setModuleEnv me { M.meCoreLint = x }
in \case EnvBool True -> setIt M.CoreLint
EnvBool False -> setIt M.NoCoreLint
_ -> return ()
, simpleOpt "hash-consing" (EnvBool True) noCheck
"Enable hash-consing in the What4 symbolic backends."
, simpleOpt "prover-stats" (EnvBool True) noCheck
"Enable prover timing statistics."
, simpleOpt "prover-validate" (EnvBool False) noCheck
"Validate :sat examples and :prove counter-examples for correctness."
, simpleOpt "show-examples" (EnvBool True) noCheck
"Print the (counter) example after :sat or :prove"
, simpleOpt "fp-base" (EnvNum 16) checkBase
"The base to display floating point numbers at (2, 8, 10, or 16)."
, simpleOpt "fp-format" (EnvString "free") checkPPFloatFormat
$ unlines
[ "Specifies the format to use when showing floating point numbers:"
, " * free show using as many digits as needed"
, " * free+exp like `free` but always show exponent"
, " * .NUM show NUM (>=1) digits after floating point"
, " * NUM show using NUM (>=1) significant digits"
, " * NUM+exp like NUM but always show exponent"
]
, simpleOpt "ignore-safety" (EnvBool False) noCheck
"Ignore safety predicates when performing :sat or :prove checks"
]
parsePPFloatFormat :: String -> Maybe PPFloatFormat
parsePPFloatFormat s =
case s of
"free" -> Just $ FloatFree AutoExponent
"free+exp" -> Just $ FloatFree ForceExponent
'.' : xs -> FloatFrac <$> readMaybe xs
_ | (as,res) <- break (== '+') s
, Just n <- readMaybe as
, Just e <- case res of
"+exp" -> Just ForceExponent
"" -> Just AutoExponent
_ -> Nothing
-> Just (FloatFixed n e)
_ -> Nothing
checkPPFloatFormat :: Checker
checkPPFloatFormat val =
case val of
EnvString s | Just _ <- parsePPFloatFormat s -> noWarns Nothing
_ -> noWarns $ Just "Failed to parse `fp-format`"
checkBase :: Checker
checkBase val = case val of
EnvNum n
| n `elem` [2,8,10,16] -> noWarns Nothing
| otherwise -> noWarns $ Just "base must be 2, 8, 10, or 16"
_ -> noWarns $ Just "unable to parse a value for base"
checkInfLength :: Checker
checkInfLength val = case val of
EnvNum n
| n >= 0 -> noWarns Nothing
| otherwise -> noWarns $ Just "the number of elements should be positive"
_ -> noWarns $ Just "unable to parse a value for infLength"
checkProver :: Checker
checkProver val = case val of
EnvString (map toLower -> s)
| s `elem` W4.proverNames ->
io (W4.setupProver s) >>= \case
Left msg -> noWarns (Just msg)
Right (ws, cfg) ->
do modifyRW_ (\rw -> rw{ eProverConfig = Right cfg })
return (Nothing, ws)
| s `elem` SBV.proverNames ->
io (SBV.setupProver s) >>= \case
Left msg -> noWarns (Just msg)
Right (ws, cfg) ->
do modifyRW_ (\rw -> rw{ eProverConfig = Left cfg })
return (Nothing, ws)
| otherwise ->
noWarns $ Just $ "Prover must be " ++ proverListString
_ -> noWarns $ Just "unable to parse a value for prover"
allProvers :: [String]
allProvers = SBV.proverNames ++ W4.proverNames
proverListString :: String
proverListString = concatMap (++ ", ") (init allProvers) ++ "or " ++ last allProvers
checkSatNum :: Checker
checkSatNum val = case val of
EnvString "all" -> noWarns Nothing
EnvString s ->
case readMaybe s :: Maybe Int of
Just n | n >= 1 -> noWarns Nothing
_ -> noWarns $ Just "must be an integer > 0 or \"all\""
_ -> noWarns $ Just "unable to parse a value for satNum"
getUserSatNum :: REPL SatNum
getUserSatNum = do
s <- getKnownUser "satNum"
case s of
"all" -> return AllSat
_ | Just n <- readMaybe s -> return (SomeSat n)
_ -> panic "REPL.Monad.getUserSatNum"
[ "invalid satNum option" ]
whenDebug :: REPL () -> REPL ()
whenDebug m = do
b <- getKnownUser "debug"
when b m
smokeTest :: REPL [Smoke]
smokeTest = catMaybes <$> sequence tests
where
tests = [ z3exists ]
type SmokeTest = REPL (Maybe Smoke)
data Smoke
= Z3NotFound
deriving (Show, Eq)
instance PP Smoke where
ppPrec _ smoke =
case smoke of
Z3NotFound -> text . intercalate " " $ [
"[error] z3 is required to run Cryptol, but was not found in the"
, "system path. See the Cryptol README for more on how to install z3."
]
z3exists :: SmokeTest
z3exists = do
mPath <- io $ findExecutable "z3"
case mPath of
Nothing -> return (Just Z3NotFound)
Just _ -> return Nothing