cryptol/bench/Main.hs
Eric Mertens 4ca062966e
Initial implementation of :check-docstrings (#1682)
* Update all commands to track their results

* Implement REPL :check-docstrings command

This command checks that all of the unlabeled and "repl" labeled
codeblocks in docstrings can successfully evaluate as REPL commands.

Cryptol will now indicate exit failure if any of REPL commands fail,
making it possible to use in automated testing.

:check-docstrings internal implementation is set up to track results
of subcommands in support for integration into a remote server API
endpoint.

* Implement :focus

* Add some :check-docstring test cases

* Update changelog

* Update CrashCourse.tex now that more commands are checked

* Require repl and convert \r\n to \n in lexer
2024-07-18 15:44:16 -07:00

177 lines
6.4 KiB
Haskell

-- |
-- Module : Main
-- Copyright : (c) 2015-2016 Galois, Inc.
-- License : BSD3
-- Maintainer : cryptol@galois.com
-- Stability : provisional
-- Portability : portable
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.DeepSeq ( force )
import Control.Monad.IO.Class( liftIO )
import qualified Data.Text as T
import qualified Data.Text.IO as T
import System.FilePath ((</>))
import qualified System.Directory as Dir
import qualified Cryptol.Eval as E
import qualified Cryptol.Eval.Value as E
import qualified Cryptol.Eval.Concrete as C
import qualified Cryptol.ModuleSystem.Base as M
import qualified Cryptol.ModuleSystem.Env as M
import qualified Cryptol.ModuleSystem.Monad as M
import qualified Cryptol.ModuleSystem.NamingEnv as M
import qualified Cryptol.Parser as P
import qualified Cryptol.Parser.AST as P
import qualified Cryptol.Parser.NoInclude as P
import qualified Cryptol.Eval.SBV as S
import qualified Cryptol.TypeCheck as T
import qualified Cryptol.TypeCheck.AST as T
import qualified Cryptol.Utils.Ident as I
import Cryptol.Utils.Logger(quietLogger)
import qualified Data.SBV.Dynamic as SBV
import Criterion.Main
main :: IO ()
main = do
cd <- Dir.getCurrentDirectory
defaultMain [
bgroup "parser" [
parser "Prelude" "lib/Cryptol.cry"
, parser "BigSequence" "bench/data/BigSequence.cry"
, parser "BigSequenceHex" "bench/data/BigSequenceHex.cry"
, parser "AES" "bench/data/AES.cry"
, parser "SHA512" "bench/data/SHA512.cry"
]
, bgroup "typechecker" [
tc cd "Prelude" "lib/Cryptol.cry"
, tc cd "BigSequence" "bench/data/BigSequence.cry"
, tc cd "BigSequenceHex" "bench/data/BigSequenceHex.cry"
, tc cd "AES" "bench/data/AES.cry"
, tc cd "SHA512" "bench/data/SHA512.cry"
]
, bgroup "conc_eval" [
ceval cd "AES" "bench/data/AES.cry" "bench_correct"
, ceval cd "ZUC" "bench/data/ZUC.cry" "ZUC_TestVectors"
, ceval cd "SHA512" "bench/data/SHA512.cry" "testVector1 ()"
]
, bgroup "sym_eval" [
seval cd "AES" "bench/data/AES.cry" "bench_correct"
, seval cd "ZUC" "bench/data/ZUC.cry" "ZUC_TestVectors"
, seval cd "SHA512" "bench/data/SHA512.cry" "testVector1 ()"
]
]
-- | Evaluation options, mostly used by `trace`.
-- Since the benchmarks likely do not use base, these don't matter very much
evOpts :: E.EvalOpts
evOpts = E.EvalOpts { E.evalLogger = quietLogger
, E.evalPPOpts = E.defaultPPOpts
}
-- | Make a benchmark for parsing a Cryptol module
parser :: String -> FilePath -> Benchmark
parser name path =
env (T.readFile path) $ \(~bytes) ->
bench name $ nfIO $ do
let cfg = P.defaultConfig
{ P.cfgSource = path
, P.cfgPreProc = P.guessPreProc path
}
case P.parseModule cfg bytes of
Right pm -> return pm
Left err -> error (show err)
-- | Make a benchmark for typechecking a Cryptol module. Does parsing
-- in the setup phase in order to isolate typechecking
tc :: String -> String -> FilePath -> Benchmark
tc cd name path =
let withLib = M.withPrependedSearchPath [cd </> "lib"] in
let setup = do
bytes <- T.readFile path
let cfg = P.defaultConfig
{ P.cfgSource = path
, P.cfgPreProc = P.guessPreProc path
}
Right pm = P.parseModule cfg bytes
menv <- M.initialModuleEnv
(eres, _) <- M.runModuleM (evOpts,menv) $ withLib $ do
-- code from `loadModule` and `checkModule` in
-- `Cryptol.ModuleSystem.Base`
let pm' = M.addPrelude pm
M.loadDeps pm'
enim <- M.io (P.removeIncludesModule path pm')
nim <- either (error "Failed to remove includes") return enim
npm <- M.noPat nim
(tcEnv,declsEnv,scm) <- M.renameModule npm
prims <- if P.thing (P.mName pm) == I.preludeName
then return (M.toPrimMap declsEnv)
else M.getPrimMap
return (prims, scm, tcEnv)
case eres of
Right ((prims, scm, tcEnv), menv') ->
return (prims, scm, tcEnv, menv')
Left _ -> error $ "Failed to load " ++ name
in env setup $ \ ~(prims, scm, tcEnv, menv) ->
bench name $ whnfIO $ M.runModuleM (evOpts,menv) $ withLib $ do
let act = M.TCAction { M.tcAction = T.tcModule
, M.tcLinter = M.moduleLinter (P.thing (P.mName scm))
, M.tcPrims = prims
}
M.typecheck act scm mempty tcEnv
ceval :: String -> String -> FilePath -> T.Text -> Benchmark
ceval cd name path expr =
let withLib = M.withPrependedSearchPath [cd </> "lib"] in
let setup = do
menv <- M.initialModuleEnv
(eres, _) <- M.runModuleM (evOpts,menv) $ withLib $ do
m <- M.loadModuleByPath path
M.setFocusedModule (P.ImpTop (T.mName m))
let Right pexpr = P.parseExpr expr
(_, texpr, _) <- M.checkExpr pexpr
return texpr
case eres of
Right (texpr, menv') -> return (texpr, menv')
Left _ -> error $ "Failed to load " ++ name
in env setup $ \ ~(texpr, menv) ->
bench name $ nfIO $ E.runEval evOpts $ do
let ?evalPrim = C.evalPrim
env' <- E.evalDecls C.Concrete (M.allDeclGroups menv) mempty
(e :: C.Value) <- E.evalExpr C.Concrete env' texpr
E.forceValue e
seval :: String -> String -> FilePath -> T.Text -> Benchmark
seval cd name path expr =
let withLib = M.withPrependedSearchPath [cd </> "lib"] in
let setup = do
menv <- M.initialModuleEnv
(eres, _) <- M.runModuleM (evOpts,menv) $ withLib $ do
m <- M.loadModuleByPath path
M.setFocusedModule (P.ImpTop (T.mName m))
let Right pexpr = P.parseExpr expr
(_, texpr, _) <- M.checkExpr pexpr
return texpr
case eres of
Right (texpr, menv') -> return (texpr, menv')
Left _ -> error $ "Failed to load " ++ name
in env setup $ \ ~(texpr, menv) ->
bench name $ whnfIO $ fmap force E.runEval evOpts $ S.sbvEval $ do
let ?evalPrim = S.evalPrim
env' <- E.evalDecls S.SBV (M.allDeclGroups menv) mempty
(e :: S.Value) <- E.evalExpr S.SBV env' texpr
liftIO $ SBV.generateSMTBenchmark False $
return (E.fromVBit e)