{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RecordWildCards #-}
module Cryptol.ModuleSystem.Env where
#ifndef RELOCATABLE
import Paths_cryptol (getDataDir)
#endif
import Cryptol.Eval (EvalEnv)
import Cryptol.ModuleSystem.Interface
import Cryptol.ModuleSystem.Name (Supply,emptySupply)
import qualified Cryptol.ModuleSystem.NamingEnv as R
import Cryptol.Parser.AST
import qualified Cryptol.TypeCheck as T
import qualified Cryptol.TypeCheck.AST as T
import Cryptol.Utils.PP (NameDisp)
import Control.Monad (guard,mplus)
import qualified Control.Exception as X
import Data.Function (on)
import qualified Data.Map as Map
import Data.Maybe(fromMaybe)
import Data.Semigroup
import System.Directory (getAppUserDataDirectory, getCurrentDirectory)
import System.Environment(getExecutablePath)
import System.FilePath ((</>), normalise, joinPath, splitPath, takeDirectory)
import qualified Data.List as List
import GHC.Generics (Generic)
import Control.DeepSeq
import Prelude ()
import Prelude.Compat
data ModuleEnv = ModuleEnv
{ meLoadedModules :: LoadedModules
, meNameSeeds :: T.NameSeeds
, meSolverConfig :: T.SolverConfig
, meEvalEnv :: EvalEnv
, meCoreLint :: CoreLint
, meMonoBinds :: !Bool
, meFocusedModule :: Maybe ModName
, meSearchPath :: [FilePath]
, meDynEnv :: DynamicEnv
, meSupply :: !Supply
} deriving Generic
instance NFData ModuleEnv
data CoreLint = NoCoreLint
| CoreLint
deriving (Generic, NFData)
resetModuleEnv :: ModuleEnv -> ModuleEnv
resetModuleEnv env = env
{ meLoadedModules = mempty
, meNameSeeds = T.nameSeeds
, meEvalEnv = mempty
, meFocusedModule = Nothing
, meDynEnv = mempty
}
initialModuleEnv :: IO ModuleEnv
initialModuleEnv = do
curDir <- getCurrentDirectory
#ifndef RELOCATABLE
dataDir <- getDataDir
#endif
binDir <- takeDirectory `fmap` getExecutablePath
let instDir = normalise . joinPath . init . splitPath $ binDir
let handle :: X.IOException -> IO String
handle _e = return ""
userDir <- X.catch (getAppUserDataDirectory "cryptol") handle
let searchPath = [ curDir
, userDir
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
, instDir </> "cryptol"
#else
, instDir </> "share" </> "cryptol"
#endif
#ifndef RELOCATABLE
, dataDir
#endif
]
return ModuleEnv
{ meLoadedModules = mempty
, meNameSeeds = T.nameSeeds
, meEvalEnv = mempty
, meFocusedModule = Nothing
, meSearchPath = searchPath
, meDynEnv = mempty
, meMonoBinds = True
, meSolverConfig = T.SolverConfig
{ T.solverPath = "z3"
, T.solverArgs = [ "-smt2", "-in" ]
, T.solverVerbose = 0
, T.solverPreludePath = searchPath
}
, meCoreLint = NoCoreLint
, meSupply = emptySupply
}
focusModule :: ModName -> ModuleEnv -> Maybe ModuleEnv
focusModule n me = do
guard (isLoaded n (meLoadedModules me))
return me { meFocusedModule = Just n }
loadedModules :: ModuleEnv -> [T.Module]
loadedModules = map lmModule . getLoadedModules . meLoadedModules
focusedEnv :: ModuleEnv -> (IfaceParams,IfaceDecls,R.NamingEnv,NameDisp)
focusedEnv me =
fromMaybe (noIfaceParams, mempty, mempty, mempty) $
do fm <- meFocusedModule me
lm <- lookupModule fm me
deps <- mapM loadImport (T.mImports (lmModule lm))
let (ifaces,names) = unzip deps
Iface { .. } = lmInterface lm
localDecls = ifPublic `mappend` ifPrivate
localNames = R.unqualifiedEnv localDecls `mappend`
R.modParamsNamingEnv ifParams
namingEnv = localNames `R.shadowing` mconcat names
return ( ifParams
, mconcat (localDecls:ifaces)
, namingEnv
, R.toNameDisp namingEnv)
where
loadImport imp =
do lm <- lookupModule (iModule imp) me
let decls = ifPublic (lmInterface lm)
return (decls,R.interpImport imp decls)
dynamicEnv :: ModuleEnv -> (IfaceDecls,R.NamingEnv,NameDisp)
dynamicEnv me = (decls,names,R.toNameDisp names)
where
decls = deIfaceDecls (meDynEnv me)
names = R.unqualifiedEnv decls
data LoadedModules = LoadedModules
{ lmLoadedModules :: [LoadedModule]
, lmLoadedParamModules :: [LoadedModule]
} deriving (Show, Generic, NFData)
getLoadedModules :: LoadedModules -> [LoadedModule]
getLoadedModules x = lmLoadedParamModules x ++ lmLoadedModules x
instance Semigroup LoadedModules where
l <> r = LoadedModules
{ lmLoadedModules = List.unionBy ((==) `on` lmName)
(lmLoadedModules l) (lmLoadedModules r)
, lmLoadedParamModules = lmLoadedParamModules l ++ lmLoadedParamModules r }
instance Monoid LoadedModules where
mempty = LoadedModules { lmLoadedModules = []
, lmLoadedParamModules = []
}
mappend l r = l <> r
data LoadedModule = LoadedModule
{ lmName :: ModName
, lmFilePath :: FilePath
, lmCanonicalPath :: FilePath
, lmInterface :: Iface
, lmModule :: T.Module
} deriving (Show, Generic, NFData)
isLoaded :: ModName -> LoadedModules -> Bool
isLoaded mn lm = any ((mn ==) . lmName) (getLoadedModules lm)
lookupModule :: ModName -> ModuleEnv -> Maybe LoadedModule
lookupModule mn me = search lmLoadedModules `mplus` search lmLoadedParamModules
where
search how = List.find ((mn ==) . lmName) (how (meLoadedModules me))
addLoadedModule ::
FilePath -> FilePath -> T.Module -> LoadedModules -> LoadedModules
addLoadedModule path canonicalPath tm lm
| isLoaded (T.mName tm) lm = lm
| T.isParametrizedModule tm = lm { lmLoadedParamModules = loaded :
lmLoadedParamModules lm }
| otherwise = lm { lmLoadedModules =
lmLoadedModules lm ++ [loaded] }
where
loaded = LoadedModule
{ lmName = T.mName tm
, lmFilePath = path
, lmCanonicalPath = canonicalPath
, lmInterface = genIface tm
, lmModule = tm
}
removeLoadedModule :: (LoadedModule -> Bool) -> LoadedModules -> LoadedModules
removeLoadedModule rm lm =
LoadedModules
{ lmLoadedModules = filter (not . rm) (lmLoadedModules lm)
, lmLoadedParamModules = filter (not . rm) (lmLoadedParamModules lm)
}
data DynamicEnv = DEnv
{ deNames :: R.NamingEnv
, deDecls :: [T.DeclGroup]
, deEnv :: EvalEnv
} deriving (Generic, NFData)
instance Semigroup DynamicEnv where
de1 <> de2 = DEnv
{ deNames = deNames de1 <> deNames de2
, deDecls = deDecls de1 <> deDecls de2
, deEnv = deEnv de1 <> deEnv de2
}
instance Monoid DynamicEnv where
mempty = DEnv
{ deNames = mempty
, deDecls = mempty
, deEnv = mempty
}
mappend de1 de2 = de1 <> de2
deIfaceDecls :: DynamicEnv -> IfaceDecls
deIfaceDecls DEnv { deDecls = dgs } =
mconcat [ IfaceDecls
{ ifTySyns = Map.empty
, ifNewtypes = Map.empty
, ifDecls = Map.singleton (ifDeclName ifd) ifd
}
| decl <- concatMap T.groupDecls dgs
, let ifd = mkIfaceDecl decl
]