{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE PatternSynonyms #-} module What4.Serialize.Printer ( serializeExpr , serializeExprWithConfig , serializeSymFn , serializeSymFnWithConfig , serializeBaseType , convertBaseTypes , Config(..) , Result(..) , printSExpr , defaultConfig , SExpr , Atom(..) , SomeExprSymFn(..) , S.WellFormedSExpr(..) , ident, int, string , bitvec, bool, nat, real , ppFreeVarEnv , ppFreeSymFnEnv , pattern S.L , pattern S.A ) where import Numeric.Natural import qualified Data.Foldable as F import Data.Set ( Set ) import qualified Data.Set as Set import Data.Map.Ordered (OMap) import qualified Data.Map.Ordered as OMap import qualified Data.BitVector.Sized as BV import Data.Parameterized.Some import qualified Data.Parameterized.Context as Ctx import qualified Data.Parameterized.NatRepr as NR import qualified Data.Parameterized.Nonce as Nonce import qualified Data.Parameterized.TraversableFC as FC import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.Word ( Word64 ) import qualified Control.Monad as M import Control.Monad.State.Strict (State) import qualified Control.Monad.State.Strict as MS import qualified Data.SCargot.Repr.WellFormed as S import What4.BaseTypes import qualified What4.Expr as W4 import qualified What4.Expr.ArrayUpdateMap as WAU import qualified What4.Expr.BoolMap as BooM import qualified What4.Expr.Builder as W4 import qualified What4.Expr.WeightedSum as WSum import qualified What4.IndexLit as WIL import qualified What4.Interface as W4 import qualified What4.Symbol as W4 import qualified What4.Utils.StringLiteral as W4S import What4.Serialize.SETokens ( Atom(..), printSExpr , ident, int, nat, string , bitvec, bool, real, float ) type SExpr = S.WellFormedSExpr Atom data SomeExprSymFn t = forall dom ret. SomeExprSymFn (W4.ExprSymFn t dom ret) instance Eq (SomeExprSymFn t) where (SomeExprSymFn fn1) == (SomeExprSymFn fn2) = case W4.testEquality (W4.symFnId fn1) (W4.symFnId fn2) of Just _ -> True _ -> False instance Ord (SomeExprSymFn t) where compare (SomeExprSymFn fn1) (SomeExprSymFn fn2) = compare (Nonce.indexValue $ W4.symFnId fn1) (Nonce.indexValue $ W4.symFnId fn2) instance Show (SomeExprSymFn t) where show (SomeExprSymFn f) = show f type VarNameEnv t = OMap (Some (W4.ExprBoundVar t)) Text type FnNameEnv t = OMap (SomeExprSymFn t) Text ppFreeVarEnv :: VarNameEnv t -> String ppFreeVarEnv env = show $ map toStr entries where entries = OMap.toAscList env toStr :: ((Some (W4.ExprBoundVar t)), Text) -> (String, String, String) toStr ((Some var), newName) = ( T.unpack $ W4.solverSymbolAsText $ W4.bvarName var , show $ W4.bvarType var , T.unpack newName ) ppFreeSymFnEnv :: FnNameEnv t -> String ppFreeSymFnEnv env = show $ map toStr entries where entries = OMap.toAscList env toStr :: ((SomeExprSymFn t), Text) -> (String, String, String) toStr ((SomeExprSymFn fn), newName) = ( T.unpack $ W4.solverSymbolAsText $ W4.symFnName fn , show $ W4.symFnArgTypes fn , T.unpack newName ) -- | Controls how expressions and functions are serialized. data Config = Config { cfgAllowFreeVars :: Bool -- ^ When @True@, any free What4 @ExprBoundVar@ -- encountered is simply serialized with a unique name, -- and the mapping from What4 ExprBoundVar to unique names -- is returned after serialization. When False, an error -- is raised when a "free" @ExprBoundVar@ is encountered. , cfgAllowFreeSymFns :: Bool -- ^ When @True@, any encountered What4 @ExprSymFn@ during -- serialization is simply assigned a unique name and the -- mapping from What4 ExprSymFn to unique name is returned -- after serialization. When @False, encountered -- ExprSymFns are serialized at the top level of the -- expression in a `(letfn ([f ...]) ...)`. } data Result t = Result { resSExpr :: S.WellFormedSExpr Atom -- ^ The serialized term. , resFreeVarEnv :: VarNameEnv t -- ^ The free BoundVars that were encountered during -- serialization and their associated fresh name -- that was used to generate the s-expression. , resSymFnEnv :: FnNameEnv t -- ^ The SymFns that were encountered during serialization -- and their associated fresh name that was used to -- generate the s-expression. } defaultConfig :: Config defaultConfig = Config { cfgAllowFreeVars = False, cfgAllowFreeSymFns = False} -- This file is organized top-down, i.e., from high-level to low-level. -- | Serialize a What4 Expr as a well-formed s-expression -- (i.e., one which contains no improper lists). Equivalent -- to @(resSExpr (serializeExpr' defaultConfig))@. Sharing -- in the AST is achieved via a top-level let-binding around -- the emitted expression (unless there are no terms with -- non-atomic terms which can be shared). serializeExpr :: W4.Expr t tp -> SExpr serializeExpr = resSExpr . (serializeExprWithConfig defaultConfig) -- | Serialize a What4 Expr as a well-formed s-expression -- (i.e., one which contains no improper lists) according to -- the configuration. Sharing in the AST is achieved via a -- top-level let-binding around the emitted expression -- (unless there are no terms with non-atomic terms which -- can be shared). serializeExprWithConfig :: Config -> W4.Expr t tp -> Result t serializeExprWithConfig cfg expr = serializeSomething cfg (convertExprWithLet expr) -- | Serialize a What4 ExprSymFn as a well-formed -- s-expression (i.e., one which contains no improper -- lists). Equivalent to @(resSExpr (serializeSymFn' -- defaultConfig))@. Sharing in the AST is achieved via a -- top-level let-binding around the emitted expression -- (unless there are no terms with non-atomic terms which -- can be shared). serializeSymFn :: W4.ExprSymFn t dom ret -> SExpr serializeSymFn = resSExpr . (serializeSymFnWithConfig defaultConfig) -- | Serialize a What4 ExprSymFn as a well-formed -- s-expression (i.e., one which contains no improper lists) -- according to the configuration. Sharing in the AST is -- achieved via a top-level let-binding around the emitted -- expression (unless there are no terms with non-atomic -- terms which can be shared). serializeSymFnWithConfig :: Config -> W4.ExprSymFn t dom ret -> Result t serializeSymFnWithConfig cfg fn = serializeSomething cfg (convertSymFn fn) -- | Run the given Memo computation to produce a well-formed -- s-expression (i.e., one which contains no improper lists) -- according to the configuration. Sharing in the AST is -- achieved via a top-level let-binding around the emitted -- expression (unless there are no terms with non-atomic -- terms which can be shared). serializeSomething :: Config -> Memo t SExpr -> Result t serializeSomething cfg something = let (maybeLetFn, getFreeSymFnEnv) = if cfgAllowFreeSymFns cfg then (return, envFreeSymFnEnv) else (letFn, \_ -> OMap.empty) (sexpr, menv) = runMemo cfg $ something >>= maybeLetFn letBindings = map (\(varName, boundExpr) -> S.L [ ident varName, boundExpr ]) $ map snd $ OMap.assocs $ envLetBindings menv res = mkLet letBindings sexpr in Result { resSExpr = res , resFreeVarEnv = envFreeVarEnv menv , resSymFnEnv = getFreeSymFnEnv menv } serializeBaseType :: BaseTypeRepr tp -> SExpr serializeBaseType bt = convertBaseType bt data MemoEnv t = MemoEnv { envConfig :: !Config -- ^ User provided configuration for serialization. , envIdCounter :: !Natural -- ^ Internal counter for generating fresh names , envLetBindings :: !(OMap SKey (Text, SExpr)) -- ^ Mapping from What4 expression nonces to the -- corresponding let-variable name (the @fst@) and the -- corresponding bound term (the @snd@). , envFreeVarEnv :: !(VarNameEnv t) -- ^ Mapping from What4 ExprBoundVar to the fresh names -- assigned to them for serialization purposes. , envFreeSymFnEnv :: !(FnNameEnv t) -- ^ Mapping from What4 ExprSymFn to the fresh names -- assigned to them for serialization purposes. , envBoundVars :: Set (Some (W4.ExprBoundVar t)) -- ^ Set of currently in-scope What4 ExprBoundVars (i.e., -- ExprBoundVars for whom we are serializing the body of -- their binding form). } initEnv :: forall t . Config -> MemoEnv t initEnv cfg = MemoEnv { envConfig = cfg , envIdCounter = 0 , envLetBindings = OMap.empty , envFreeVarEnv = OMap.empty , envFreeSymFnEnv = OMap.empty , envBoundVars = Set.empty } type Memo t a = State (MemoEnv t) a runMemo :: Config -> (Memo t a) -> (a, MemoEnv t) runMemo cfg m = MS.runState m $ initEnv cfg -- | Serialize the given sexpression within a @letfn@ which -- serializes and binds all of the encountered SymFns. Note: -- this recursively also discovers and then serializes -- SymFns referenced within the body of the SymFns -- encountered thus far. letFn :: SExpr -> Memo t SExpr letFn sexpr = go [] [] Set.empty where go :: [((SomeExprSymFn t), Text)] -> [(Text, SExpr)] -> Set Text -> Memo t SExpr go [] fnBindings seen = do -- Although the `todo` list is empty, we may have -- encountered some SymFns along the way, so check for -- those and serialize any previously unseen SymFns. newFns <- MS.gets (filter (\(_symFn, varName) -> not $ Set.member varName seen) . OMap.assocs . envFreeSymFnEnv) if null newFns then if null fnBindings then return sexpr else let bs = map (\(name, def) -> S.L [ident name, def]) fnBindings in return $ S.L [ident "letfn" , S.L bs, sexpr] else go newFns fnBindings seen go (((SomeExprSymFn nextFn), nextFnName):todo) fnBindings seen = do nextSExpr <- convertSymFn nextFn let fnBindings' = (nextFnName, nextSExpr):fnBindings seen' = Set.insert nextFnName seen go todo fnBindings' seen' -- | Converts the given What4 expression into an -- s-expression and clears the let-binding cache (since it -- just emitted a let binding with any necessary let-bound -- vars). convertExprWithLet :: W4.Expr t tp -> Memo t SExpr convertExprWithLet expr = do b <- convertExpr expr bs <- map (\(varName, boundExpr) -> S.L [ ident varName, boundExpr ]) <$> map snd <$> OMap.assocs <$> MS.gets envLetBindings MS.modify' (\r -> r {envLetBindings = OMap.empty}) return $ mkLet bs b mkLet :: [SExpr] -> SExpr -> SExpr mkLet [] body = body mkLet bindings body = S.L [ident "let", S.L bindings, body] -- | Converts a What4 ExprSymFn into an s-expression within -- the Memo monad (i.e., no @let@ or @letfn@s are emitted). convertSymFn :: forall t args ret . W4.ExprSymFn t args ret -> Memo t SExpr convertSymFn symFn@(W4.ExprSymFn _ symFnName symFnInfo _) = do case symFnInfo of W4.DefinedFnInfo argVars body _ -> do let sArgTs = convertBaseTypes (W4.fnArgTypes symFn) sRetT = convertBaseType (W4.fnReturnType symFn) argsWithFreshNames <- let rawArgs = FC.toListFC Some argVars in mapM getBoundVarWithFreshName rawArgs let (origBoundVars, freshArgNames) = unzip argsWithFreshNames -- Convert the body with the bound variable set and -- free-variable mapping extended to reflect being -- under the function's binders. sExpr <- MS.withState (\ms -> let boundVars = envBoundVars ms fvEnv = envFreeVarEnv ms in ms { envBoundVars = Set.union boundVars (Set.fromList origBoundVars) , envFreeVarEnv = fvEnv OMap.<>| (OMap.fromList argsWithFreshNames)}) $ convertExprWithLet body return $ S.L [ ident "definedfn" , string (Some W4.UnicodeRepr) $ W4.solverSymbolAsText symFnName , S.L ((ident "->"):sArgTs ++ [sRetT]) , S.L $ map ident freshArgNames , sExpr ] W4.UninterpFnInfo argTs retT -> let sArgTs = convertBaseTypes argTs sRetT = convertBaseType retT in return $ S.L [ ident "uninterpfn" , string (Some W4.UnicodeRepr) $ W4.solverSymbolAsText symFnName , S.L ((ident "->"):sArgTs ++ [sRetT]) ] W4.MatlabSolverFnInfo _msfn _argTs _body -> error "MatlabSolverFnInfo SymFns are not yet supported" where getBoundVarWithFreshName :: (Some (W4.ExprBoundVar t)) -> Memo t (Some (W4.ExprBoundVar t), Text) getBoundVarWithFreshName someVar@(Some var) = do nm <- freshName (W4.bvarType var) return (someVar, nm) -- | Key for sharing SExpr construction. Internally indexes are expression nonces, -- but the let-binding identifiers are based on insertion order to the OMap newtype SKey = SKey {sKeyValue :: Word64} deriving (Eq, Ord, Show) freshName :: W4.BaseTypeRepr tp -> Memo t Text freshName tp = do idCount <- MS.gets envIdCounter MS.modify' $ (\e -> e {envIdCounter = idCount + 1}) let prefix = case tp of W4.BaseBoolRepr{} -> "bool" W4.BaseIntegerRepr{} -> "int" W4.BaseRealRepr{} -> "real" W4.BaseFloatRepr{} -> "fl" W4.BaseStringRepr{} -> "str" W4.BaseComplexRepr -> "cmplx" W4.BaseBVRepr{} -> "bv" W4.BaseStructRepr{} -> "struct" W4.BaseArrayRepr{} -> "arr" return $ T.pack $ prefix++(show $ idCount) freshFnName :: W4.ExprSymFn t args ret -> Memo t Text freshFnName fn = do idCount <- MS.gets envIdCounter MS.modify' $ (\e -> e {envIdCounter = idCount + 1}) let prefix = case W4.symFnInfo fn of W4.UninterpFnInfo{} -> "ufn" W4.DefinedFnInfo{} -> "dfn" W4.MatlabSolverFnInfo{} -> "mfn" return $ T.pack $ prefix++(show $ idCount) exprSKey :: W4.Expr t tp -> Maybe SKey exprSKey x = SKey . Nonce.indexValue <$> W4.exprMaybeId x -- | Allocate a fresh variable for the given -- nonce-key/s-expression and save the variable/expression -- mapping in the Memo monad. addLetBinding :: SKey -> SExpr -> W4.BaseTypeRepr tp -> Memo t Text addLetBinding key sexp tp = do letVarName <- freshName tp curLetBindings <- MS.gets envLetBindings MS.modify' $ (\e -> e {envLetBindings = curLetBindings OMap.|> (key, (letVarName, sexp))}) return letVarName -- | Converts a What 4 expression into an s-expression -- within the Memo monad (i.e., no @let@ or @letfn@s are -- emitted in the result). convertExpr :: forall t tp . W4.Expr t tp -> Memo t SExpr convertExpr initialExpr = do case exprSKey initialExpr of Nothing -> go initialExpr Just key -> do letCache <- MS.gets envLetBindings case OMap.lookup key letCache of Just (name, _) -> return $ ident name Nothing -> do sexp <- go initialExpr case sexp of S.A _ -> return sexp -- Don't memoize atomic s-expressions - that's just silly. _ -> do letVarName <- addLetBinding key sexp (W4.exprType initialExpr) return $ ident letVarName where go :: W4.Expr t tp -> Memo t SExpr go (W4.SemiRingLiteral W4.SemiRingIntegerRepr val _) = return $ int val -- do we need/want these? go (W4.SemiRingLiteral W4.SemiRingRealRepr val _) = return $ real val go (W4.SemiRingLiteral (W4.SemiRingBVRepr _ sz) val _) = return $ bitvec (natValue sz) (BV.asUnsigned val) go (W4.StringExpr str _) = case (W4.stringLiteralInfo str) of W4.UnicodeRepr -> return $ string (Some W4.UnicodeRepr) (W4S.fromUnicodeLit str) W4.Char8Repr -> return $ string (Some W4.Char8Repr) $ T.decodeUtf8 $ W4S.fromChar8Lit str W4.Char16Repr -> error "Char16 strings are not yet supported" -- TODO - there is no `W4S.toLEByteString` currently... hmm... -- return $ string (Some W4.Char16Repr) $ T.decodeUtf16LE $ W4S.toLEByteString $ W4S.fromChar16Lit str go (W4.FloatExpr prec bf _) = return $ float prec bf go (W4.BoolExpr b _) = return $ bool b go (W4.AppExpr appExpr) = convertAppExpr' appExpr go (W4.NonceAppExpr nae) = case W4.nonceExprApp nae of W4.FnApp fn args -> convertFnApp fn args W4.Forall {} -> error "Forall NonceAppExpr not yet supported" W4.Exists {} -> error "Exists NonceAppExpr not yet supported" W4.ArrayFromFn {} -> error "ArrayFromFn NonceAppExpr not yet supported" W4.MapOverArrays {} -> error "MapOverArrays NonceAppExpr not yet supported" W4.ArrayTrueOnEntries {} -> error "ArrayTrueOnEntries NonceAppExpr not yet supported" W4.Annotation {} -> error "Annotation NonceAppExpr not yet supported" go (W4.BoundVarExpr var) = convertBoundVarExpr var -- | Serialize bound variables as the s-expression identifier `name_nonce`. This allows us to -- preserve their human-readable name while ensuring they are globally unique w/ the nonce suffix. convertBoundVarExpr :: forall t tp. W4.ExprBoundVar t tp -> Memo t SExpr convertBoundVarExpr x = do fvsAllowed <- MS.gets (cfgAllowFreeVars . envConfig) bvs <- MS.gets envBoundVars -- If this variable is not bound (in the standard syntactic sense) -- and free variables are not explicitly permitted, raise an error. M.when ((not $ Set.member (Some x) bvs) && (not fvsAllowed)) $ error $ "encountered the free What4 ExprBoundVar `" ++ (T.unpack (W4.solverSymbolAsText (W4.bvarName x))) ++ "`, but the user-specified configuration dissallows free variables." -- Get the renaming cache and either use the name already generated -- or generate a fresh name and record it. varEnv <- MS.gets envFreeVarEnv case OMap.lookup (Some x) varEnv of Just var -> return $ ident var Nothing -> do varName <- freshName $ W4.bvarType x MS.modify' $ (\e -> e {envFreeVarEnv = varEnv OMap.|> ((Some x), varName)}) return $ ident varName convertAppExpr' :: forall t tp . W4.AppExpr t tp -> Memo t SExpr convertAppExpr' = go . W4.appExprApp where go :: forall tp' . W4.App (W4.Expr t) tp' -> Memo t SExpr go (W4.BaseIte _bt _ e1 e2 e3) = do s1 <- goE e1 s2 <- goE e2 s3 <- goE e3 return $ S.L [ident "ite", s1, s2, s3] go (W4.BaseEq _bt e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "=", s1, s2] go (W4.NotPred e) = do s <- goE e return $ S.L [ident "notp", s] go (W4.ConjPred bm) = convertBoolMap "andp" True bm go (W4.BVSlt e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvslt", s1, s2] go (W4.BVUlt e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvult", s1, s2] go (W4.BVConcat _ e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "concat", s1, s2] go (W4.BVSelect idx n bv) = extract i j bv -- See SemMC.Formula.Parser.readExtract for the explanation behind -- these values. where i = intValue n + j - 1 j = intValue idx -- Note that because the SemiRing has an identity element that -- always gets applied, resulting in lots of additional, -- unnecessary elements like: "(bvand #xffffffff TERM)". -- These will get manifested in the stored form (but generally -- _not_ via DSL-generated versions since they don't output -- via Printer) and result in longer stored forms. They could -- be eliminated by checking for the identity (e.g. "if mul == -- SR.one (WSum.sumRepr sm)") but the re-loaded representation -- will still use the SemiRing, so it's probably not worth the -- effort to reduce these. go (W4.SemiRingSum sm) = case WSum.sumRepr sm of W4.SemiRingBVRepr W4.BVArithRepr w -> let smul mul e = do s <- goE e return $ S.L [ ident "bvmul", bitvec (natValue w) (BV.asUnsigned mul), s] sval v = return $ bitvec (natValue w) (BV.asUnsigned v) add x y = return $ S.L [ ident "bvadd", x, y ] in WSum.evalM add smul sval sm W4.SemiRingBVRepr W4.BVBitsRepr w -> let smul mul e = do s <- goE e return $ S.L [ ident "bvand", bitvec (natValue w) (BV.asUnsigned mul), s] sval v = return $ bitvec (natValue w) (BV.asUnsigned v) add x y = let op = ident "bvxor" in return $ S.L [ op, x, y ] in WSum.evalM add smul sval sm W4.SemiRingIntegerRepr -> let smul mul e = do s <- goE e return $ S.L [ ident "intmul", int mul, s] sval v = return $ int v add x y = return $ S.L [ ident "intadd", x, y ] in WSum.evalM add smul sval sm W4.SemiRingRealRepr -> error "SemiRingSum RealRepr not supported" go (W4.SemiRingProd pd) = case WSum.prodRepr pd of W4.SemiRingBVRepr W4.BVArithRepr w -> do let pmul x y = return $ S.L [ ident "bvmul", x, y ] maybeS <- WSum.prodEvalM pmul goE pd case maybeS of Just s -> return s Nothing -> return $ bitvec (natValue w) 1 W4.SemiRingBVRepr W4.BVBitsRepr w -> do let pmul x y = return $ S.L [ ident "bvand", x, y ] maybeS <- WSum.prodEvalM pmul goE pd case maybeS of Just s -> return s Nothing -> return $ bitvec (natValue w) 1 W4.SemiRingIntegerRepr -> do let pmul x y = return $ S.L [ ident "intmul", x, y ] maybeS <- WSum.prodEvalM pmul goE pd case maybeS of Just s -> return s Nothing -> return $ int 1 W4.SemiRingRealRepr -> error "convertApp W4.SemiRingProd Real unsupported" go (W4.SemiRingLe sr e1 e2) = do s1 <- goE e1 s2 <- goE e2 case sr of W4.OrderedSemiRingIntegerRepr -> do return $ S.L [ ident "intle", s1, s2] W4.OrderedSemiRingRealRepr -> error $ "Printer: SemiRingLe is not supported for reals" go (W4.BVOrBits width bs) = do let op = ident "bvor" case W4.bvOrToList bs of [] -> return $ bitvec (NR.natValue width) 0 (x:xs) -> do e <- goE x let f = (\acc b -> do b' <- goE b return $ S.L [op, b', acc]) M.foldM f e xs go (W4.BVUdiv _ e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvudiv", s1, s2] go (W4.BVUrem _ e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvurem", s1, s2] go (W4.BVSdiv _ e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvsdiv", s1, s2] go (W4.BVSrem _ e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvsrem", s1, s2] go (W4.BVShl _ e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvshl", s1, s2] go (W4.BVLshr _ e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvlshr", s1, s2] go (W4.BVAshr _ e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "bvashr", s1, s2] go (W4.BVZext r e) = extend "zero" (intValue r) e go (W4.BVSext r e) = extend "sign" (intValue r) e go (W4.BVFill r e) = do s <- goE e return $ S.L [ S.L [ident "_", ident "bvfill", int (intValue r)] , s ] go (W4.BVToInteger e) = do s <- goE e return $ S.L [ident "bvToInteger", s] go (W4.SBVToInteger e) = do s <- goE e return $ S.L [ident "sbvToInteger", s] go (W4.FloatNeg _repr e) = do s <- goE e return $ S.L [ident "floatneg", s] go (W4.FloatAbs _repr e) = do s <- goE e return $ S.L [ident "floatabs", s] go (W4.IntDiv e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "intdiv", s1, s2] go (W4.IntMod e1 e2) = do s1 <- goE e1 s2 <- goE e2 return $ S.L [ident "intmod", s1, s2] go (W4.IntAbs e1) = do s1 <- goE e1 return $ S.L [ident "intabs", s1] go (W4.IntegerToBV e wRepr) = do s <- goE e return $ S.L [ident "integerToBV" , nat $ natValue wRepr , s] go (W4.StructCtor _tps es) = do ss <- convertExprAssignment es return $ S.L [ident "struct", S.L ss] go (W4.StructField e ix _fieldTp) = do s <- goE e return $ S.L [ident "field" , s , int $ toInteger $ Ctx.indexVal ix ] go (W4.UpdateArray _ _ e1 es e2) = do s1 <- goE e1 ss <- convertExprAssignment es s2 <- goE e2 case ss of [idx] -> return $ S.L [ ident "updateArray", s1, idx, s2] _ -> error $ "multidimensional arrays not supported" go (W4.SelectArray _ e es) = do s <- goE e ss <- convertExprAssignment es case ss of [idx] -> return $ S.L [ ident "select", s, idx] _ -> error $ "multidimensional arrays not supported" go (W4.ArrayMap _idxReprs _resRepr updateMap arr) = do updates <- mapM convertArrayUpdate (WAU.toList updateMap) arr' <- goE arr return $ S.L [ ident "arrayMap" , S.L updates , arr' ] go app = error $ "unhandled App: " ++ show app convertArrayUpdate :: forall tp1 ctx . (Ctx.Assignment WIL.IndexLit ctx, W4.Expr t tp1) -> Memo t SExpr convertArrayUpdate (idxLits, e) = do e' <- goE e return $ S.L [ S.L (FC.toListFC convertIndexLit idxLits) , e' ] -- -- -- -- Helper functions! -- -- -- -- goE :: forall tp' . W4.Expr t tp' -> Memo t SExpr goE = convertExpr extend :: forall w. Text -> Integer -> W4.Expr t (BaseBVType w) -> Memo t SExpr extend op r e = do let w = case W4.exprType e of BaseBVRepr len -> intValue len extension = r - w s <- goE e return $ S.L [ S.L [ ident "_", ident $ op <> "_extend", int extension ] , s ] extract :: forall tp'. Integer -> Integer -> W4.Expr t tp' -> Memo t SExpr extract i j bv = do s <- goE bv return $ S.L [ S.L [ ident "_", ident "extract", int i, int j ] , s ] convertBoolMap :: Text -> Bool -> BooM.BoolMap (W4.Expr t) -> Memo t SExpr convertBoolMap op base bm = let strBase b = if b then S.L [ident "=", bitvec 1 0, bitvec 1 0] -- true else S.L [ident "=", bitvec 1 0, bitvec 1 1] -- false strNotBase = strBase . not in case BooM.viewBoolMap bm of BooM.BoolMapUnit -> return $ strBase base BooM.BoolMapDualUnit -> return $ strNotBase base BooM.BoolMapTerms ts -> let onEach e r = do s <- arg e return $ S.L [ident op, s, r] arg (t, BooM.Positive) = goE t arg (t, BooM.Negative) = do s <- goE t return $ S.L [ident "notp", s] in F.foldrM onEach (strBase base) ts convertIndexLit :: WIL.IndexLit tp -> SExpr convertIndexLit il = case il of WIL.IntIndexLit iidx -> int iidx WIL.BVIndexLit irep bvidx -> bitvec (natValue irep) (BV.asUnsigned bvidx) convertExprAssignment :: Ctx.Assignment (W4.Expr t) sh -> Memo t [SExpr] convertExprAssignment es = mapM (\(Some e) -> convertExpr e) (FC.toListFC Some es) convertFnApp :: W4.ExprSymFn t args ret -> Ctx.Assignment (W4.Expr t) args -> Memo t SExpr convertFnApp fn args = do argSExprs <- convertExprAssignment args fnEnv <- MS.gets envFreeSymFnEnv case OMap.lookup (SomeExprSymFn fn) fnEnv of Just fnName -> return $ S.L $ (ident "call"):(ident fnName):argSExprs Nothing -> do varName <- freshFnName fn MS.modify' $ (\e -> e {envFreeSymFnEnv = fnEnv OMap.|> ((SomeExprSymFn fn), varName)}) return $ S.L $ (ident "call"):(ident varName):argSExprs convertBaseType :: BaseTypeRepr tp -> SExpr convertBaseType tp = case tp of W4.BaseBoolRepr -> S.A $ AId "Bool" W4.BaseIntegerRepr -> S.A $ AId "Int" W4.BaseRealRepr -> S.A $ AId "Real" W4.BaseStringRepr si -> S.L [S.A $ AId "String", convertStringInfo si] W4.BaseComplexRepr -> S.A $ AId "Complex" W4.BaseBVRepr wRepr -> S.L [S.A (AId "BV"), S.A (AInt (NR.intValue wRepr)) ] W4.BaseStructRepr tps -> S.L [ S.A (AId "Struct"), S.L (convertBaseTypes tps) ] W4.BaseArrayRepr ixs repr -> S.L [S.A (AId "Array"), S.L $ convertBaseTypes ixs , convertBaseType repr] W4.BaseFloatRepr (W4.FloatingPointPrecisionRepr eRepr sRepr) -> S.L [ S.A (AId "Float"), S.A (AInt (NR.intValue eRepr)), S.A (AInt (NR.intValue sRepr)) ] convertStringInfo :: StringInfoRepr si -> SExpr convertStringInfo W4.Char8Repr = ident "Char8" convertStringInfo W4.Char16Repr = ident "Char16" convertStringInfo W4.UnicodeRepr = ident "Unicode" -- | Convert an Assignment of base types into a list of base -- types SExpr, where the left-to-right syntactic ordering -- of the types is maintained. convertBaseTypes :: Ctx.Assignment BaseTypeRepr tps -> [SExpr] convertBaseTypes asn = FC.toListFC convertBaseType asn