Merge branch 'master' into wip/solver

This commit is contained in:
Iavor S. Diatchki 2017-02-08 16:24:46 -08:00
commit 3c15d086d1
11 changed files with 814 additions and 27 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "external-libs/sbv"]
path = external-libs/sbv
url = https://github.com/yav/sbv.git

View File

@ -91,7 +91,6 @@ msi: ${PKG}.msi
# during initial setup, you have to invoke this target again manually
${CS}:
$(CABAL) sandbox init
$(CABAL) sandbox add-source external-libs/sbv
${CS_BIN}/alex: | ${CS}
$(CABAL_INSTALL) alex

View File

@ -152,6 +152,7 @@ library
Cryptol.Eval.Arch,
Cryptol.Eval.Env,
Cryptol.Eval.Monad,
Cryptol.Eval.Reference,
Cryptol.Eval.Type,
Cryptol.Eval.Value,

View File

@ -53,6 +53,7 @@ data Options = Options
, optColorMode :: ColorMode
, optCryptolrc :: Cryptolrc
, optCryptolPathOnly :: Bool
, optStopOnError :: Bool
} deriving (Show)
defaultOptions :: Options
@ -65,6 +66,7 @@ defaultOptions = Options
, optColorMode = AutoColor
, optCryptolrc = CryrcDefault
, optCryptolPathOnly = False
, optStopOnError = False
}
options :: [OptDescr (OptParser Options)]
@ -72,6 +74,9 @@ options =
[ Option "b" ["batch"] (ReqArg setBatchScript "FILE")
"run the script provided and exit"
, Option "e" ["stop-on-error"] (NoArg setStopOnError)
"stop script execution as soon as an error occurs."
, Option "c" ["command"] (ReqArg addCommand "COMMAND")
(concat [ "run the given command and then exit; if multiple --command "
, "arguments are given, run them in the order they appear "
@ -109,6 +114,10 @@ addCommand :: String -> OptParser Options
addCommand cmd =
modify $ \ opts -> opts { optCommands = cmd : optCommands opts }
-- | Stop script (batch mode) execution on first error.
setStopOnError :: OptParser Options
setStopOnError = modify $ \opts -> opts { optStopOnError = True }
-- | Set a batch script to be run.
setBatchScript :: String -> OptParser Options
setBatchScript path = modify $ \ opts -> opts { optBatch = Just path }
@ -198,8 +207,9 @@ main = do
| otherwise -> do
(opts', mCleanup) <- setupCmdScript opts
status <- repl (optCryptolrc opts')
(optBatch opts')
(setupREPL opts')
(optBatch opts')
(optStopOnError opts')
(setupREPL opts')
case mCleanup of
Nothing -> return ()
Just cmdFile -> removeFile cmdFile

View File

@ -38,8 +38,8 @@ import Prelude.Compat
-- | One REPL invocation, either form a file or from the terminal.
crySession :: Maybe FilePath -> REPL CommandExitCode
crySession mbBatch =
crySession :: Maybe FilePath -> Bool -> REPL CommandExitCode
crySession mbBatch stopOnError =
do settings <- io (setHistoryFile (replSettings isBatch))
let act = runInputTBehavior behavior settings (withInterrupt loop)
if isBatch then asBatch act else act
@ -54,7 +54,7 @@ crySession mbBatch =
case ln of
NoMoreLines -> return CommandOk
Interrupted
| isBatch -> return CommandError
| isBatch && stopOnError -> return CommandError
| otherwise -> loop
NextLine line
| all isSpace line -> loop
@ -62,12 +62,12 @@ crySession mbBatch =
doCommand txt =
case parseCommand findCommandExact txt of
Nothing | isBatch -> return CommandError
Nothing | isBatch && stopOnError -> return CommandError
| otherwise -> loop -- say somtething?
Just cmd -> join $ MTL.lift $
do status <- handleInterrupt (handleCtrlC CommandError) (runCommand cmd)
case status of
CommandError | isBatch -> return (return status)
CommandError | isBatch && stopOnError -> return (return status)
_ -> do goOn <- shouldContinue
return (if goOn then loop else return status)
@ -99,22 +99,22 @@ loadCryRC cryrc =
let file = dir </> ".cryptolrc"
present <- io (doesFileExist file)
if present
then crySession (Just file)
then crySession (Just file) True
else check others
loadMany [] = return CommandOk
loadMany (f : fs) = do status <- crySession (Just f)
loadMany (f : fs) = do status <- crySession (Just f) True
case status of
CommandOk -> loadMany fs
_ -> return status
-- | Haskeline-specific repl implementation.
repl :: Cryptolrc -> Maybe FilePath -> REPL () -> IO CommandExitCode
repl cryrc mbBatch begin =
repl :: Cryptolrc -> Maybe FilePath -> Bool -> REPL () -> IO CommandExitCode
repl cryrc mbBatch stopOnError begin =
runREPL (isJust mbBatch) $
do status <- loadCryRC cryrc
case status of
CommandOk -> begin >> crySession mbBatch
CommandOk -> begin >> crySession mbBatch stopOnError
_ -> return status

View File

@ -205,7 +205,7 @@ evalDeclGroup env dg = do
env'' <- foldM (evalDecl env') env ds
-- now backfill the holes we declared earlier using the definitions
-- calculcated in the previous step
-- calculated in the previous step
mapM_ (fillHole env'') holes
-- return the map containing the holes
@ -378,12 +378,12 @@ declHole d =
-- Two input environments are given: the first is an environment
-- to use when evaluating the body of the declaration; the second
-- is the environment to extend. There are two environments to
-- handle the subtle name-binding issues that arise from recurisve
-- handle the subtle name-binding issues that arise from recursive
-- definitions. The 'read only' environment is used to bring recursive
-- names into scope while we are still defining them.
evalDecl :: EvalPrims b w
=> GenEvalEnv b w -- ^ A 'read only' environment for use in declaration bodies
-> GenEvalEnv b w -- ^ An evalaution environment to extend with the given declaration
-> GenEvalEnv b w -- ^ An evaluation environment to extend with the given declaration
-> Decl -- ^ The declaration to evaluate
-> Eval (GenEvalEnv b w)
evalDecl renv env d =

View File

@ -0,0 +1,757 @@
-- |
-- Module : $Header$
-- Copyright : (c) 2013-2016 Galois, Inc.
-- License : BSD3
-- Maintainer : cryptol@galois.com
-- Stability : provisional
-- Portability : portable
{-# LANGUAGE PatternGuards #-}
-- | The reference implementation of the Cryptol evaluation semantics.
module Cryptol.Eval.Reference where
import qualified Control.Exception as X (throw)
import Control.Monad (foldM)
import Data.Bits
import Data.List
(genericDrop, genericIndex, genericLength, genericReplicate, genericSplitAt,
genericTake, sortBy, transpose)
import Data.Ord (comparing)
import qualified Data.Map as Map
import qualified Data.Text as T (pack)
import Cryptol.ModuleSystem.Name (asPrim)
import Cryptol.TypeCheck.Solver.InfNat (Nat'(..))
import Cryptol.TypeCheck.AST
import Cryptol.Eval.Monad (EvalError(..))
import Cryptol.Eval.Type (TypeEnv, TValue(..), isTBit, evalValType, evalNumType)
import Cryptol.Prims.Eval (divWrap, modWrap, lg2, divModPoly)
import Cryptol.Utils.Ident (Ident, mkIdent)
import Cryptol.Utils.Panic (panic)
import Cryptol.Utils.PP
import qualified Cryptol.ModuleSystem as M
import qualified Cryptol.ModuleSystem.Env as M (loadedModules)
--import qualified Cryptol.ModuleSystem.Monad as M
-- Module Command --------------------------------------------------------------
evaluate :: Expr -> M.ModuleCmd Value
evaluate expr modEnv = return (Right (evalExpr env expr, modEnv), [])
where
extDgs = concatMap mDecls (M.loadedModules modEnv)
env = foldl evalDeclGroup mempty extDgs
-- Values ----------------------------------------------------------------------
-- | Value type for the reference evaluator. Invariant: Undefinedness
-- and run-time exceptions are only allowed inside a @Bool@ argument
-- of a @VBit@ constructor. All other @Value@ and list constructors
-- should evaluate without error.
data Value
= VRecord [(Ident, Value)] -- ^ @ { .. } @
| VTuple [Value] -- ^ @ ( .. ) @
| VBit Bool -- ^ @ Bit @
| VList [Value] -- ^ @ [n]a @ (either finite or infinite)
| VFun (Value -> Value) -- ^ functions
| VPoly (TValue -> Value) -- ^ polymorphic values (kind *)
| VNumPoly (Nat' -> Value) -- ^ polymorphic values (kind #)
-- | Destructor for @VBit@.
fromVBit :: Value -> Bool
fromVBit (VBit b) = b
fromVBit _ = evalPanic "fromVBit" ["Expected a bit"]
-- | Destructor for @VList@.
fromVList :: Value -> [Value]
fromVList (VList vs) = vs
fromVList _ = evalPanic "fromVList" ["Expected a list"]
-- | Destructor for @VTuple@.
fromVTuple :: Value -> [Value]
fromVTuple (VTuple vs) = vs
fromVTuple _ = evalPanic "fromVTuple" ["Expected a tuple"]
-- | Destructor for @VFun@.
fromVFun :: Value -> (Value -> Value)
fromVFun (VFun f) = f
fromVFun _ = evalPanic "fromVFun" ["Expected a function"]
-- | Destructor for @VRecord@.
fromVRecord :: Value -> [(Ident, Value)]
fromVRecord (VRecord fs) = fs
fromVRecord _ = evalPanic "fromVRecord" ["Expected a record"]
-- | Look up a field in a record.
lookupRecord :: Ident -> Value -> Value
lookupRecord f v =
case lookup f (fromVRecord v) of
Just val -> val
Nothing -> evalPanic "lookupRecord" ["Malformed record"]
-- | Convert a list of booleans in big-endian format to an integer.
bitsToInteger :: [Bool] -> Integer
bitsToInteger bs = foldl f 0 bs
where f x b = if b then 2 * x + 1 else 2 * x
-- | Convert an integer to a big-endian format of the specified width.
integerToBits :: Integer -> Integer -> [Bool]
integerToBits w x = go [] w x
where go bs 0 _ = bs
go bs n a = go (odd a : bs) (n - 1) $! (a `div` 2)
-- | Convert a value from a big-endian binary format to an integer.
fromVWord :: Value -> Integer
fromVWord v = bitsToInteger (map fromVBit (fromVList v))
-- | Convert an integer to big-endian binary value of the specified width.
vWord :: Integer -> Integer -> Value
vWord w x = VList (map VBit (integerToBits w x))
vFinPoly :: (Integer -> Value) -> Value
vFinPoly f = VNumPoly g
where
g (Nat n) = f n
g Inf = evalPanic "vFinPoly" ["Expected finite numeric type"]
-- Environments ----------------------------------------------------------------
-- | Evaluation environment.
data Env = Env
{ envVars :: !(Map.Map Name Value)
, envTypes :: !TypeEnv
}
instance Monoid Env where
mempty = Env
{ envVars = Map.empty
, envTypes = Map.empty
}
mappend l r = Env
{ envVars = Map.union (envVars l) (envVars r)
, envTypes = Map.union (envTypes l) (envTypes r)
}
-- | Bind a variale in the evaluation environment.
bindVar :: (Name, Value) -> Env -> Env
bindVar (n, val) env = env { envVars = Map.insert n val (envVars env) }
-- | Bind a type variable of kind # or *.
bindType :: TVar -> Either Nat' TValue -> Env -> Env
bindType p ty env = env { envTypes = Map.insert p ty (envTypes env) }
-- Evaluation ----------------------------------------------------------------
-- | Evaluate a Cryptol expression to a value.
evalExpr :: Env -- ^ Evaluation environment
-> Expr -- ^ Expression to evaluate
-> Value
evalExpr env expr =
case expr of
EList es _ty -> VList [ evalExpr env e | e <- es ]
ETuple es -> VTuple [ evalExpr env e | e <- es ]
ERec fields -> VRecord [ (f, evalExpr env e) | (f, e) <- fields ]
ESel e sel -> evalSel (evalExpr env e) sel
EIf c t f -> evalExpr env (if fromVBit (evalExpr env c) then t else f)
-- FIXME: this produces an invalid result if evaluation of the
-- condition yields a run-time error or fails to terminate. We
-- should use a zip-like function to push the conditionals down
-- into the leaf bits.
EComp _n _ty h gs ->
evalComp env h gs
EVar n ->
case Map.lookup n (envVars env) of
Just val -> val
Nothing -> evalPanic "evalExpr" ["var `" ++ show (pp n) ++ "` is not defined" ]
ETAbs tv b ->
case tpKind tv of
KType -> VPoly $ \ty -> evalExpr (bindType (tpVar tv) (Right ty) env) b
KNum -> VNumPoly $ \n -> evalExpr (bindType (tpVar tv) (Left n) env) b
k -> evalPanic "evalExpr" ["Invalid kind on type abstraction", show k]
ETApp e ty ->
case evalExpr env e of
VPoly f -> f $! (evalValType (envTypes env) ty)
VNumPoly f -> f $! (evalNumType (envTypes env) ty)
_ -> evalPanic "evalExpr" ["Expected a polymorphic value"]
EApp e1 e2 -> fromVFun (evalExpr env e1) (evalExpr env e2)
EAbs n _ty b -> VFun (\v -> evalExpr (bindVar (n, v) env) b)
EProofAbs _ e -> evalExpr env e
EProofApp e -> evalExpr env e
ECast e _ty -> evalExpr env e
EWhere e dgs -> evalExpr (foldl evalDeclGroup env dgs) e
-- | Apply the the given "selector" form to the given value. This function pushes
-- tuple and record selections pointwise down into other value constructs
-- (e.g., streams and functions).
evalSel :: Value -> Selector -> Value
evalSel val sel =
case sel of
TupleSel n _ -> tupleSel n val
RecordSel n _ -> recordSel n val
ListSel n _ -> listSel n val
where
tupleSel n v =
case v of
VTuple vs -> vs !! n
VList vs -> VList (map (tupleSel n) vs)
VFun f -> VFun (\x -> tupleSel n (f x))
_ -> evalPanic "evalSel"
["Unexpected value in tuple selection."]
recordSel n v =
case v of
VRecord _ -> lookupRecord n v
VList vs -> VList (map (recordSel n) vs)
VFun f -> VFun (\x -> recordSel n (f x))
_ -> evalPanic "evalSel"
["Unexpected value in record selection."]
listSel n v =
case v of
VList vs -> vs !! n
_ -> evalPanic "evalSel"
[ "Unexpected value in list selection" ]
-- List Comprehensions ---------------------------------------------------------
-- | Evaluate a comprehension.
evalComp :: Env -- ^ Starting evaluation environment
-> Expr -- ^ Head expression of the comprehension
-> [[Match]] -- ^ List of parallel comprehension branches
-> Value
evalComp env body ms = VList [ evalExpr e body | e <- envs ]
where
-- | Generate a new environment for each iteration of each parallel branch
benvs :: [[Env]]
benvs = map (branchEnvs env) ms
-- | Take parallel slices of each environment. When the length of the list
-- drops below the number of branches, one branch has terminated.
slices :: [[Env]]
slices = takeWhile allBranches (transpose benvs)
where allBranches es = length es == length ms
-- | Join environments to produce environments at each step through the process.
envs :: [Env]
envs = map mconcat slices
-- | Turn a list of matches into the final environments for each iteration of
-- the branch.
branchEnvs :: Env -> [Match] -> [Env]
branchEnvs env matches = foldM evalMatch env matches
-- | Turn a match into the list of environments it represents.
evalMatch :: Env -> Match -> [Env]
evalMatch env m =
case m of
Let d ->
[ bindVar (evalDecl env d) env ]
From n _l _ty expr ->
[ bindVar (n, v) env | v <- fromVList (evalExpr env expr) ]
-- Declarations ----------------------------------------------------------------
-- | Extend the given evaluation environment with the result of
-- evaluating the given declaration group.
evalDeclGroup :: Env -> DeclGroup -> Env
evalDeclGroup env dg = do
case dg of
NonRecursive d ->
bindVar (evalDecl env d) env
Recursive ds ->
let env' = foldr bindVar env bindings
bindings = map (evalDeclRecursive env') ds
in env'
-- | Evaluate a declaration.
evalDecl :: Env -> Decl -> (Name, Value)
evalDecl env d =
case dDefinition d of
DPrim -> (dName d, evalPrim (dName d))
DExpr e -> (dName d, evalExpr env e)
-- | Evaluate a declaration in a recursive context, performing a
-- type-directed copy to build the spine of the value.
evalDeclRecursive :: Env -> Decl -> (Name, Value)
evalDeclRecursive env d =
case dDefinition d of
DPrim -> (dName d, evalPrim (dName d))
DExpr e -> (dName d, copyBySchema env (dSignature d) (evalExpr env e))
-- | Make a copy of the given value, building the spine based only on
-- the type without forcing the value argument. This ensures that
-- undefinedness appears in @Bool@ values, and never on any
-- constructors of the @Value@ type. In turn, this gives the
-- appropriate semantics to recursive definitions: The bottom value
-- for a compound type is equal to a value of the same type where
-- every individual bit is bottom.
copyBySchema :: Env -> Schema -> Value -> Value
copyBySchema env0 (Forall params _props ty) = go params env0
where
go [] env v = copyByTValue (evalValType (envTypes env) ty) v
go (p : ps) env v =
case v of
VPoly f -> VPoly $ \t -> go ps (bindType (tpVar p) (Right t) env) (f t)
VNumPoly f -> VNumPoly $ \n -> go ps (bindType (tpVar p) (Left n) env) (f n)
_ -> evalPanic "copyBySchema" ["Expected polymorphic value"]
copyByTValue :: TValue -> Value -> Value
copyByTValue = go
where
go :: TValue -> Value -> Value
go ty val =
case ty of
TVBit -> VBit (fromVBit val)
TVSeq w ety -> VList (map (go ety) (copyList w (fromVList val)))
TVStream ety -> VList (map (go ety) (copyStream (fromVList val)))
TVTuple etys -> VTuple (zipWith go etys (copyList (genericLength etys) (fromVTuple val)))
TVFun _ bty -> VFun (\v -> go bty (fromVFun val v))
TVRec fields -> VRecord [ (f, go fty (lookupRecord f val)) | (f, fty) <- fields ]
copyStream :: [a] -> [a]
copyStream xs = head xs : copyStream (tail xs)
copyList :: Integer -> [a] -> [a]
copyList 0 _ = []
copyList n xs = head xs : copyList (n - 1) (tail xs)
-- Primitives ------------------------------------------------------------------
evalPrim :: Name -> Value
evalPrim n
| Just i <- asPrim n, Just v <- Map.lookup i primTable = v
| otherwise = evalPanic "evalPrim" ["Unimplemented primitive", show n]
primTable :: Map.Map Ident Value
primTable = Map.fromList $ map (\(n, v) -> (mkIdent (T.pack n), v))
[ ("+" , binary (arithBinary (const (+))))
, ("-" , binary (arithBinary (const (-))))
, ("*" , binary (arithBinary (const (*))))
, ("/" , binary (arithBinary (const divWrap)))
, ("%" , binary (arithBinary (const modWrap)))
-- , ("^^" , binary (arithBinary modExp))
, ("lg2" , unary (arithUnary (const lg2)))
, ("negate" , unary (arithUnary (const negate)))
, ("<" , binary (cmpOrder (\o -> o == LT)))
, (">" , binary (cmpOrder (\o -> o == GT)))
, ("<=" , binary (cmpOrder (\o -> o /= GT)))
, (">=" , binary (cmpOrder (\o -> o /= LT)))
, ("==" , binary (cmpOrder (\o -> o == EQ)))
, ("!=" , binary (cmpOrder (\o -> o /= EQ)))
, ("&&" , binary (logicBinary (&&)))
, ("||" , binary (logicBinary (||)))
, ("^" , binary (logicBinary (/=)))
, ("complement" , unary (logicUnary not))
, ("<<" , shiftV shiftLV)
, (">>" , shiftV shiftRV)
, ("<<<" , rotateV rotateLV)
, (">>>" , rotateV rotateRV)
, ("True" , VBit True)
, ("False" , VBit False)
, ("demote" , vFinPoly $ \val ->
vFinPoly $ \bits ->
vWord bits val)
, ("#" , VNumPoly $ \_front ->
VNumPoly $ \_back ->
VPoly $ \_elty ->
VFun $ \l ->
VFun $ \r -> VList (fromVList l ++ fromVList r))
, ("@" , indexPrimOne indexFront)
, ("@@" , indexPrimMany indexFront)
, ("!" , indexPrimOne indexBack)
, ("!!" , indexPrimMany indexBack)
, ("update" , updatePrim updateFront)
, ("updateEnd" , updatePrim updateBack)
, ("zero" , VPoly (logicNullary False))
, ("join" , VNumPoly $ \_parts ->
VNumPoly $ \_each ->
VPoly $ \_a ->
VFun $ \xss ->
VList (concat (map fromVList (fromVList xss))))
-- FIXME: this doesn't handle the case [inf][0] -> [0]
, ("split" , VNumPoly $ \parts ->
vFinPoly $ \each ->
VPoly $ \_a ->
VFun $ \val -> VList (splitV parts each (fromVList val)))
, ("splitAt" , vFinPoly $ \front ->
VNumPoly $ \_back ->
VPoly $ \_a ->
VFun $ \v ->
let (xs, ys) = genericSplitAt front (fromVList v)
in VTuple [VList xs, VList ys])
, ("fromThen" , vFinPoly $ \first ->
vFinPoly $ \next ->
vFinPoly $ \bits ->
vFinPoly $ \len ->
VList (map (vWord bits) (genericTake len [first, next ..])))
, ("fromTo" , vFinPoly $ \first ->
vFinPoly $ \lst ->
vFinPoly $ \bits ->
VList (map (vWord bits) [first .. lst]))
, ("fromThenTo" , vFinPoly $ \first ->
vFinPoly $ \next ->
vFinPoly $ \_lst ->
vFinPoly $ \bits ->
vFinPoly $ \len ->
VList (map (vWord bits) (genericTake len [first, next ..])))
, ("infFrom" , vFinPoly $ \bits ->
VFun $ \first ->
VList (map (vWord bits) [fromVWord first ..]))
, ("infFromThen", vFinPoly $ \bits ->
VFun $ \first ->
VFun $ \next ->
VList (map (vWord bits) [fromVWord first, fromVWord next ..]))
, ("error" , VPoly $ \a ->
VNumPoly $ \_ ->
VFun $ \_s -> logicNullary (error "error") a)
, ("reverse" , VNumPoly $ \_a ->
VPoly $ \_b ->
VFun $ \(VList vs) -> VList (reverse vs))
, ("transpose" , VNumPoly $ \_a ->
VNumPoly $ \b ->
VPoly $ \_c ->
VFun $ \v ->
VList (map VList (transposeV b (map fromVList (fromVList v)))))
, ("pmult" , let mul res _ _ 0 = res
mul res bs as n = mul (if even as then res else xor res bs)
(bs `shiftL` 1) (as `shiftR` 1) (n-1)
in vFinPoly $ \a ->
vFinPoly $ \b ->
VFun $ \x ->
VFun $ \y -> vWord (1 + a + b) (mul 0 (fromVWord x) (fromVWord y) (1+b)))
, ("pdiv" , vFinPoly $ \a ->
vFinPoly $ \b ->
VFun $ \x ->
VFun $ \y ->
vWord a (fst (divModPoly (fromVWord x) (fromInteger a) (fromVWord y) (fromInteger b))))
, ("pmod" , vFinPoly $ \a ->
vFinPoly $ \b ->
VFun $ \x ->
VFun $ \y ->
vWord b (snd (divModPoly (fromVWord x) (fromInteger a) (fromVWord y) (fromInteger b + 1))))
{-
, ("random" , VPoly $ \a ->
wVFun $ \(bvVal -> x) -> return $ randomV a x)
, ("trace" , VNumPoly $ \_n ->
VPoly $ \_a ->
VPoly $ \_b ->
VFun $ \s ->
VFun $ \x ->
VFun $ \y ->
msg <- fromStr =<< s
doc <- ppValue defaultPPOpts =<< x
yv <- y
io $ putStrLn $ show $ if null msg then
doc
else
text msg <+> doc
return yv)
-}
]
unary :: (TValue -> Value -> Value) -> Value
unary f = VPoly $ \ty -> VFun $ \x -> f ty x
binary :: (TValue -> Value -> Value -> Value) -> Value
binary f = VPoly $ \ty -> VFun $ \x -> VFun $ \y -> f ty x y
type Unary = TValue -> Value -> Value
type Binary = TValue -> Value -> Value -> Value
-- Arith -----------------------------------------------------------------------
arithUnary :: (Integer -> Integer -> Integer)
-> TValue -> Value -> Value
arithUnary op = go
where
go :: TValue -> Value -> Value
go ty val =
case ty of
TVBit ->
evalPanic "arithUnary" ["Bit not in class Arith"]
TVSeq w a
| isTBit a -> vWord w (op w (fromVWord val))
| otherwise -> VList (map (go a) (fromVList val))
TVStream a ->
VList (map (go a) (fromVList val))
TVFun _ ety ->
VFun (\x -> go ety (fromVFun val x))
TVTuple tys ->
VTuple (zipWith go tys (fromVTuple val))
TVRec fs ->
VRecord [ (f, go fty (lookupRecord f val)) | (f, fty) <- fs ]
arithBinary :: (Integer -> Integer -> Integer -> Integer)
-> TValue -> Value -> Value -> Value
arithBinary op = go
where
go :: TValue -> Value -> Value -> Value
go ty l r =
case ty of
TVBit ->
evalPanic "arithBinary" ["Bit not in class Arith"]
TVSeq w a
| isTBit a -> vWord w (op w (fromVWord l) (fromVWord r))
| otherwise -> VList (zipWith (go a) (fromVList l) (fromVList r))
TVStream a ->
VList (zipWith (go a) (fromVList l) (fromVList r))
TVFun _ ety ->
VFun (\x -> go ety (fromVFun l x) (fromVFun r x))
TVTuple tys ->
VTuple (zipWith3 go tys (fromVTuple l) (fromVTuple r))
TVRec fs ->
VRecord [ (f, go fty (lookupRecord f l) (lookupRecord f r)) | (f, fty) <- fs ]
-- Cmp -------------------------------------------------------------------------
-- | Process two elements based on their lexicographic ordering.
cmpOrder :: (Ordering -> Bool) -> TValue -> Value -> Value -> Value
cmpOrder p ty l r = VBit (p (lexCompare ty l r))
-- | Lexicographic ordering on two values.
lexCompare :: TValue -> Value -> Value -> Ordering
lexCompare ty l r =
case ty of
TVBit ->
compare (fromVBit l) (fromVBit r)
TVSeq _w ety ->
lexList (zipWith (lexCompare ety) (fromVList l) (fromVList r))
TVStream _ ->
evalPanic "lexCompare" ["invalid type"]
TVFun _ _ ->
evalPanic "lexCompare" ["invalid type"]
TVTuple etys ->
lexList (zipWith3 lexCompare etys (fromVList l) (fromVList r))
TVRec fields ->
let tys = map snd (sortBy (comparing fst) fields)
ls = map snd (sortBy (comparing fst) (fromVRecord l))
rs = map snd (sortBy (comparing fst) (fromVRecord r))
in lexList (zipWith3 lexCompare tys ls rs)
-- TODO: should we make this strict in both arguments?
lexOrdering :: Ordering -> Ordering -> Ordering
lexOrdering LT _ = LT
lexOrdering EQ y = y
lexOrdering GT _ = GT
lexList :: [Ordering] -> Ordering
lexList = foldr lexOrdering EQ
-- Logic -----------------------------------------------------------------------
logicNullary :: Bool -> TValue -> Value
logicNullary b = go
where
go TVBit = VBit b
go (TVSeq n ety) = VList (genericReplicate n (go ety))
go (TVStream ety) = VList (repeat (go ety))
go (TVFun _ bty) = VFun (\_ -> go bty)
go (TVTuple tys) = VTuple (map go tys)
go (TVRec fields) = VRecord [ (f, go fty) | (f, fty) <- fields ]
logicUnary :: (Bool -> Bool) -> TValue -> Value -> Value
logicUnary op = go
where
go :: TValue -> Value -> Value
go ty val =
case ty of
TVBit -> VBit (op (fromVBit val))
TVSeq _w ety -> VList (map (go ety) (fromVList val))
TVStream ety -> VList (map (go ety) (fromVList val))
TVTuple etys -> VTuple (zipWith go etys (fromVTuple val))
TVFun _ bty -> VFun (\v -> go bty (fromVFun val v))
TVRec fields -> VRecord [ (f, go fty (lookupRecord f val)) | (f, fty) <- fields ]
logicBinary :: (Bool -> Bool -> Bool) -> TValue -> Value -> Value -> Value
logicBinary op = go
where
go :: TValue -> Value -> Value -> Value
go ty l r =
case ty of
TVBit -> VBit (op (fromVBit l) (fromVBit r))
TVSeq _w ety -> VList (zipWith (go ety) (fromVList l) (fromVList r))
TVStream ety -> VList (zipWith (go ety) (fromVList l) (fromVList r))
TVTuple etys -> VTuple (zipWith3 go etys (fromVTuple l) (fromVTuple r))
TVFun _ bty -> VFun (\v -> go bty (fromVFun l v) (fromVFun r v))
TVRec fields -> VRecord [ (f, go fty (lookupRecord f l) (lookupRecord f r))
| (f, fty) <- fields ]
-- Sequence Primitives ---------------------------------------------------------
shiftV :: (Nat' -> Value -> [Value] -> Integer -> [Value]) -> Value
shiftV op =
VNumPoly $ \a ->
VNumPoly $ \_b ->
VPoly $ \c ->
VFun $ \v ->
VFun $ \i ->
VList (op a (logicNullary False c) (fromVList v) (fromVWord i))
shiftLV :: Nat' -> Value -> [Value] -> Integer -> [Value]
shiftLV w z vs i =
case w of
Nat n -> genericDrop (min n i) vs ++ genericReplicate (min n i) z
Inf -> genericDrop i vs
shiftRV :: Nat' -> Value -> [Value] -> Integer -> [Value]
shiftRV w z vs i =
case w of
Nat n -> genericReplicate (min n i) z ++ genericTake (n - min n i) vs
Inf -> genericReplicate i z ++ vs
rotateV :: (Integer -> [Value] -> Integer -> [Value]) -> Value
rotateV op =
vFinPoly $ \a ->
VNumPoly $ \_b ->
VPoly $ \_c ->
VFun $ \v ->
VFun $ \i ->
VList (op a (fromVList v) (fromVWord i))
rotateLV :: Integer -> [Value] -> Integer -> [Value]
rotateLV 0 vs _ = vs
rotateLV w vs i = ys ++ xs
where (xs, ys) = genericSplitAt (i `mod` w) vs
rotateRV :: Integer -> [Value] -> Integer -> [Value]
rotateRV 0 vs _ = vs
rotateRV w vs i = ys ++ xs
where (xs, ys) = genericSplitAt ((w - i) `mod` w) vs
splitV :: Nat' -> Integer -> [Value] -> [Value]
splitV w k xs =
case w of
Nat 0 -> []
Nat n -> VList ys : splitV (Nat (n - 1)) k zs
Inf -> VList ys : splitV Inf k zs
where
(ys, zs) = genericSplitAt k xs
transposeV :: Nat' -> [[Value]] -> [[Value]]
transposeV w xss =
case w of
Nat 0 -> []
Nat n -> xs : transposeV (Nat (n - 1)) xss'
Inf -> xs : transposeV Inf xss'
where
(xs, xss') = dest xss
dest :: [[Value]] -> ([Value], [[Value]])
dest [] = ([], [])
dest ([] : _) = evalPanic "transposeV" ["Expected non-empty list"]
dest ((y : ys) : yss) = (y : zs, ys : zss)
where (zs, zss) = dest yss
-- | Indexing operations that return one element.
indexPrimOne :: (Nat' -> TValue -> [Value] -> Integer -> Value) -> Value
indexPrimOne op =
VNumPoly $ \n ->
VPoly $ \a ->
VNumPoly $ \_i ->
VFun $ \l ->
VFun $ \r -> op n a (fromVList l) (fromVWord r)
-- | Indexing operations that return many elements.
indexPrimMany :: (Nat' -> TValue -> [Value] -> Integer -> Value) -> Value
indexPrimMany op =
VNumPoly $ \n ->
VPoly $ \a ->
VNumPoly $ \_m ->
VNumPoly $ \_i ->
VFun $ \l ->
VFun $ \r -> VList [ op n a xs (fromVWord y) | let xs = fromVList l, y <- fromVList r ]
indexFront :: Nat' -> TValue -> [Value] -> Integer -> Value
indexFront w a vs ix =
case w of
Nat n | n <= ix -> logicNullary (invalidIndex ix) a
_ -> genericIndex vs ix
indexBack :: Nat' -> TValue -> [Value] -> Integer -> Value
indexBack w a vs ix =
case w of
Nat n | n > ix -> genericIndex vs (n - ix - 1)
| otherwise -> logicNullary (invalidIndex ix) a
Inf -> evalPanic "indexBack" ["unexpected infinite sequence"]
updatePrim :: (Nat' -> [Value] -> Integer -> Value -> [Value]) -> Value
updatePrim op =
VNumPoly $ \len ->
VPoly $ \_eltTy ->
VNumPoly $ \_idxLen ->
VFun $ \xs ->
VFun $ \idx ->
VFun $ \val -> VList (op len (fromVList xs) (fromVWord idx) val)
updateFront :: Nat' -> [Value] -> Integer -> Value -> [Value]
updateFront _ vs i x = updateAt vs i x
updateBack :: Nat' -> [Value] -> Integer -> Value -> [Value]
updateBack Inf _vs _i _x = evalPanic "Unexpected infinite sequence in updateEnd" []
updateBack (Nat n) vs i x = updateAt vs (n - i - 1) x
updateAt :: [a] -> Integer -> a -> [a]
updateAt [] _ _ = []
updateAt (_ : xs) 0 y = y : xs
updateAt (x : xs) i y = x : updateAt xs (i - 1) y
-- Pretty Printing -------------------------------------------------------------
ppValue :: Value -> Doc
ppValue val =
case val of
VRecord fs -> braces (sep (punctuate comma (map ppField fs)))
where ppField (f,r) = pp f <+> char '=' <+> ppValue r
VTuple vs -> parens (sep (punctuate comma (map ppValue vs)))
VBit b -> text (show b)
VList vs -> brackets (fsep (punctuate comma (map ppValue vs)))
VFun _ -> text "<function>"
VPoly _ -> text "<polymorphic value>"
VNumPoly _ -> text "<polymorphic value>"
-- Error Handling --------------------------------------------------------------
evalPanic :: String -> [String] -> a
evalPanic cxt = panic ("[Reference Evaluator]" ++ cxt)
invalidIndex :: Integer -> Bool
invalidIndex i = X.throw (InvalidIndex i)

View File

@ -58,6 +58,7 @@ import qualified Cryptol.Utils.Ident as M
import qualified Cryptol.Eval.Monad as E
import qualified Cryptol.Eval.Value as E
import qualified Cryptol.Eval.Reference as R
import Cryptol.Testing.Concrete
import qualified Cryptol.Testing.Random as TestR
import Cryptol.Parser
@ -180,6 +181,8 @@ nbCommandList =
"use a solver to find a satisfying assignment for which the argument returns true (if no argument, find an assignment for all properties)"
, CommandDescr [ ":debug_specialize" ] (ExprArg specializeCmd)
"do type specialization on a closed expression"
, CommandDescr [ ":eval" ] (ExprArg refEvalCmd)
"evaluate an expression with the reference evaluator"
]
commandList :: [CommandDescr]
@ -485,7 +488,9 @@ cmdProveSat isSat str = do
case (ty, exprs) of
(t, [e]) -> bindItVariable t e
(t, es ) -> bindItVariables t es
showProverStats stats
seeStats <- getUserShowProverStats
when seeStats (showProverStats stats)
onlineProveSat :: Bool
-> String -> Maybe FilePath -> REPL (Symbolic.ProverResult,ProverStats)
@ -574,6 +579,13 @@ specializeCmd str = do
rPutStrLn "Specialized expression:"
rPutStrLn $ dump spexpr
refEvalCmd :: String -> REPL ()
refEvalCmd str = do
parseExpr <- replParseExpr str
(_, expr, _schema) <- replCheckExpr parseExpr
val <- liftModuleCmd (rethrowEvalError . R.evaluate expr)
rPrint $ R.ppValue val
typeOfCmd :: String -> REPL ()
typeOfCmd str = do

View File

@ -57,6 +57,7 @@ module Cryptol.REPL.Monad (
, setUser, getUser, tryGetUser
, userOptions
, getUserSatNum
, getUserShowProverStats
-- ** Configurable Output
, getPutStr
@ -566,6 +567,11 @@ getUser name = do
Just ev -> return ev
Nothing -> panic "[REPL] getUser" ["option `" ++ name ++ "` does not exist"]
getUserShowProverStats :: REPL Bool
getUserShowProverStats =
do EnvBool yes <- getUser "prover-stats"
return yes
-- Environment Options ---------------------------------------------------------
type OptionMap = Trie OptionDescr
@ -641,8 +647,12 @@ userOptions = mkOptionMap
in \case EnvBool True -> setIt M.CoreLint
EnvBool False -> setIt M.NoCoreLint
_ -> return ()
, simpleOpt "prover-stats" (EnvBool True) (const (return Nothing))
"Enable prover timing statistics."
]
-- | Check the value to the `base` option.
checkBase :: EnvVal -> IO (Maybe String)
checkBase val = case val of

View File

@ -239,14 +239,13 @@ iteWord c x y = mergeWord True c <$> x <*> y
-- | Barrel-shifter algorithm. Takes a list of bits in big-endian order.
shifter :: Monad m => (SBool -> a -> a -> m a) -> (a -> Integer -> m a) -> a -> [SBool] -> m a
shifter :: Monad m => (SBool -> a -> a -> a) -> (a -> Integer -> m a) -> a -> [SBool] -> m a
shifter mux op = go
where
go x [] = return x
go x (b : bs) = do
x' <- op x (2 ^ length bs)
y <- mux b x' x
go y bs
go (mux b x' x) bs
logicShift :: String
-> (SWord -> SWord -> SWord)
@ -254,7 +253,7 @@ logicShift :: String
-> Value
logicShift nm wop reindex =
nlam $ \_m ->
nlam $ \n ->
nlam $ \_n ->
tlam $ \a ->
VFun $ \xs -> return $
VFun $ \y -> do
@ -271,7 +270,7 @@ logicShift nm wop reindex =
case reindex (Nat w) (toInteger i) shft of
Nothing -> return $ bitLit False
Just i' -> Seq.index bs (fromInteger i')
BitsVal <$> shifter (\c x y -> return $ mergeBits True c x y) op (asBitsVal wv) idx_bits
BitsVal <$> shifter (mergeBits True) op (asBitsVal wv) idx_bits
VSeq w vs0 ->
do idx_bits <- sequence $ Fold.toList $ asBitsVal idx
@ -279,7 +278,7 @@ logicShift nm wop reindex =
case reindex (Nat w) i shft of
Nothing -> return $ zeroV a
Just i' -> lookupSeqMap vs i'
VSeq w <$> shifter (\c x y -> return $ mergeSeqMap True c x y) op vs0 idx_bits
VSeq w <$> shifter (mergeSeqMap True) op vs0 idx_bits
VStream vs0 ->
do idx_bits <- sequence $ Fold.toList $ asBitsVal idx
@ -287,7 +286,7 @@ logicShift nm wop reindex =
case reindex Inf i shft of
Nothing -> return $ zeroV a
Just i' -> lookupSeqMap vs i'
VStream <$> shifter (\c x y -> return $ mergeSeqMap True c x y) op vs0 idx_bits
VStream <$> shifter (mergeSeqMap True) op vs0 idx_bits
_ -> evalPanic "expected sequence value in shift operation" [nm]

View File

@ -24,5 +24,7 @@ Test that doc strings work on property declarations
(++) : {a} a -> a -> a
Precedence 5, associates to the left.
Test that doc strings work on fixity declarations