module Cryptol.ModuleSystem.Base where
import Cryptol.ModuleSystem.Env (DynamicEnv(..), deIfaceDecls)
import Cryptol.ModuleSystem.Interface
import Cryptol.ModuleSystem.Monad
import Cryptol.ModuleSystem.Env (lookupModule, LoadedModule(..))
import qualified Cryptol.Eval as E
import qualified Cryptol.Eval.Value as E
import qualified Cryptol.ModuleSystem.Renamer as R
import qualified Cryptol.Parser as P
import qualified Cryptol.Parser.Unlit as P
import Cryptol.Parser.AST as P
import Cryptol.Parser.NoPat (RemovePatterns(removePatterns))
import Cryptol.Parser.NoInclude (removeIncludesModule)
import Cryptol.Parser.Position (HasLoc(..), Range, emptyRange)
import qualified Cryptol.TypeCheck as T
import qualified Cryptol.TypeCheck.AST as T
import qualified Cryptol.TypeCheck.Depends as T
import Cryptol.Utils.PP (pretty)
import Cryptol.Prelude (writePreludeContents)
import Cryptol.Transform.MonoValues
import Control.DeepSeq
import qualified Control.Exception as X
import Control.Monad (unless)
import Data.Foldable (foldMap)
import Data.Function (on)
import Data.List (nubBy)
import Data.Maybe (mapMaybe,fromMaybe)
import Data.Monoid ((<>))
import System.Directory (doesFileExist)
import System.FilePath ( addExtension
, isAbsolute
, joinPath
, (</>)
, takeDirectory
, takeFileName
)
import qualified System.IO.Error as IOE
import qualified Data.Map as Map
rename :: R.Rename a => R.NamingEnv -> a -> ModuleM a
rename env a = do
renamerWarnings ws
case res of
Right r -> return r
Left errs -> renamerErrors errs
where
(res,ws) = R.runRenamer env (R.rename a)
renameModule :: P.Module -> ModuleM P.Module
renameModule m = do
iface <- importIfaces (map thing (P.mImports m))
let menv = R.namingEnv m
(es,ws) = R.checkNamingEnv menv
renamerWarnings ws
unless (null es) (renamerErrors es)
rename (menv `R.shadowing` R.namingEnv iface) m
renameExpr :: P.Expr -> ModuleM P.Expr
renameExpr e = do
env <- getFocusedEnv
denv <- getDynEnv
rename (deNames denv `R.shadowing` R.namingEnv env) e
renameDecls :: (R.Rename d, T.FromDecl d) => [d] -> ModuleM [d]
renameDecls ds = do
env <- getFocusedEnv
denv <- getDynEnv
rename (deNames denv `R.shadowing` R.namingEnv env) ds
noPat :: RemovePatterns a => a -> ModuleM a
noPat a = do
let (a',errs) = removePatterns a
unless (null errs) (noPatErrors errs)
return a'
parseModule :: FilePath -> ModuleM P.Module
parseModule path = do
e <- io $ X.try $ do
bytes <- readFile path
return $!! bytes
bytes <- case (e :: Either X.IOException String) of
Right bytes -> return bytes
Left exn | IOE.isDoesNotExistError exn -> cantFindFile path
| otherwise -> otherIOError path exn
let cfg = P.defaultConfig
{ P.cfgSource = path
, P.cfgPreProc = P.guessPreProc path
}
case P.parseModule cfg bytes of
Right pm -> return pm
Left err -> moduleParseError path err
loadModuleByPath :: FilePath -> ModuleM T.Module
loadModuleByPath path = withPrependedSearchPath [ takeDirectory path ] $ do
let fileName = takeFileName path
path' <- findFile fileName
pm <- parseModule path'
let n = thing (P.mName pm)
env <- getModuleEnv
case lookupModule n env of
Nothing -> loadingModule n (loadModule path' pm)
Just lm
| path' == loaded -> return (lmModule lm)
| otherwise -> duplicateModuleName n path' loaded
where loaded = lmFilePath lm
loadImport :: Located P.Import -> ModuleM ()
loadImport li = do
let i = thing li
n = P.iModule i
alreadyLoaded <- isLoaded n
unless alreadyLoaded $
do path <- findModule n
pm <- parseModule path
loadingImport li $ do
unless (n == thing (P.mName pm)) (moduleNameMismatch n (mName pm))
_ <- loadModule path pm
return ()
loadModule :: FilePath -> P.Module -> ModuleM T.Module
loadModule path pm = do
let pm' = addPrelude pm
loadDeps pm'
io (putStrLn ("Loading module " ++ pretty (P.thing (P.mName pm'))))
tcm <- checkModule pm'
modifyEvalEnv (E.moduleEnv tcm)
loadedModule path tcm
return tcm
fullyQualified :: P.Import -> P.Import
fullyQualified i = i { iAs = Just (iModule i) }
importIface :: P.Import -> ModuleM Iface
importIface i = interpImport i `fmap` getIface (T.iModule i)
importIfaces :: [P.Import] -> ModuleM IfaceDecls
importIfaces is = foldMap ifPublic `fmap` mapM importIface is
moduleFile :: ModName -> String -> FilePath
moduleFile (ModName ns) = addExtension (joinPath ns)
findModule :: ModName -> ModuleM FilePath
findModule n = do
paths <- getSearchPath
loop (possibleFiles paths)
where
loop paths = case paths of
path:rest -> do
b <- io (doesFileExist path)
if b then return path else loop rest
[] -> handleNotFound
handleNotFound =
case n of
m | m == preludeName -> writePreludeContents
_ -> moduleNotFound n =<< getSearchPath
possibleFiles paths = do
path <- paths
ext <- P.knownExts
return (path </> moduleFile n ext)
findFile :: FilePath -> ModuleM FilePath
findFile path | isAbsolute path = do
b <- io (doesFileExist path)
if b then return path else cantFindFile path
findFile path = do
paths <- getSearchPath
loop (possibleFiles paths)
where
loop paths = case paths of
path':rest -> do
b <- io (doesFileExist path')
if b then return path' else loop rest
[] -> cantFindFile path
possibleFiles paths = map (</> path) paths
preludeName :: P.ModName
preludeName = P.ModName ["Cryptol"]
addPrelude :: P.Module -> P.Module
addPrelude m
| preludeName == P.thing (P.mName m) = m
| preludeName `elem` importedMods = m
| otherwise = m { mImports = importPrelude : mImports m }
where
importedMods = map (P.iModule . P.thing) (P.mImports m)
importPrelude = P.Located
{ P.srcRange = emptyRange
, P.thing = P.Import
{ iModule = preludeName
, iAs = Nothing
, iSpec = Nothing
}
}
loadDeps :: Module -> ModuleM ()
loadDeps m
| null needed = return ()
| otherwise = mapM_ load needed
where
needed = nubBy ((==) `on` P.iModule . thing) (P.mImports m)
load mn = loadImport mn
checkExpr :: P.Expr -> ModuleM (T.Expr,T.Schema)
checkExpr e = do
npe <- noPat e
denv <- getDynEnv
re <- renameExpr npe
env <- getQualifiedEnv
let env' = env <> deIfaceDecls denv
typecheck T.tcExpr re env'
checkDecls :: (HasLoc d, R.Rename d, T.FromDecl d) => [d] -> ModuleM [T.DeclGroup]
checkDecls ds = do
rds <- renameDecls ds
denv <- getDynEnv
env <- getQualifiedEnv
let env' = env <> deIfaceDecls denv
typecheck T.tcDecls rds env'
checkModule :: P.Module -> ModuleM T.Module
checkModule m = do
e <- io (removeIncludesModule m)
nim <- case e of
Right nim -> return nim
Left ierrs -> noIncludeErrors ierrs
npm <- noPat nim
scm <- renameModule npm
tcm <- typecheck T.tcModule scm =<< importIfacesTc (map thing (P.mImports scm))
return (Cryptol.Transform.MonoValues.rewModule tcm)
type TCAction i o = i -> T.InferInput -> IO (T.InferOutput o)
typecheck :: HasLoc i => TCAction i o -> i -> IfaceDecls -> ModuleM o
typecheck action i env = do
let range = fromMaybe emptyRange (getLoc i)
input <- genInferInput range env
out <- io (action i input)
case out of
T.InferOK warns seeds o ->
do setNameSeeds seeds
typeCheckWarnings warns
return o
T.InferFailed warns errs ->
do typeCheckWarnings warns
typeCheckingFailed errs
importIfacesTc :: [P.Import] -> ModuleM IfaceDecls
importIfacesTc is =
mergePublic `fmap` mapM (importIface . fullyQualified) is
where
mergePublic = foldMap ifPublic
genInferInput :: Range -> IfaceDecls -> ModuleM T.InferInput
genInferInput r env = do
seeds <- getNameSeeds
monoBinds <- getMonoBinds
return T.InferInput
{ T.inpRange = r
, T.inpVars = Map.map ifDeclSig (filterEnv ifDecls)
, T.inpTSyns = filterEnv ifTySyns
, T.inpNewtypes = filterEnv ifNewtypes
, T.inpNameSeeds = seeds
, T.inpMonoBinds = monoBinds
}
where
keepOne :: (QName,[a]) -> Maybe (QName,a)
keepOne (qn,syns) = case syns of
[syn] -> Just (qn,syn)
_ -> Nothing
filterEnv p = Map.fromList (mapMaybe keepOne (Map.toList (p env)))
evalExpr :: T.Expr -> ModuleM E.Value
evalExpr e = do
env <- getEvalEnv
denv <- getDynEnv
return (E.evalExpr (env <> deEnv denv) e)
evalDecls :: [T.DeclGroup] -> ModuleM ()
evalDecls dgs = do
env <- getEvalEnv
denv <- getDynEnv
let env' = env <> deEnv denv
denv' = denv { deDecls = deDecls denv ++ dgs
, deEnv = E.evalDecls dgs env'
}
setDynEnv denv'