module Language.PureScript.Sugar.Names.Imports
( ImportDef
, resolveImports
, resolveModuleImport
, findImports
) where
import Prelude.Compat
import Control.Monad
import Control.Monad.Error.Class (MonadError(..))
import Data.Foldable (for_, traverse_)
import Data.Maybe (fromMaybe)
import qualified Data.Map as M
import qualified Data.Set as S
import Language.PureScript.AST
import Language.PureScript.Crash
import Language.PureScript.Errors
import Language.PureScript.Names
import Language.PureScript.Sugar.Names.Env
type ImportDef = (SourceSpan, ImportDeclarationType, Maybe ModuleName)
findImports
:: [Declaration]
-> M.Map ModuleName [ImportDef]
findImports = foldr go M.empty
where
go (ImportDeclaration (pos, _) mn typ qual) result =
let imp = (pos, typ, qual)
in M.insert mn (maybe [imp] (imp :) (mn `M.lookup` result)) result
go _ result = result
resolveImports
:: forall m
. MonadError MultipleErrors m
=> Env
-> Module
-> m (Module, Imports)
resolveImports env (Module ss coms currentModule decls exps) =
rethrow (addHint (ErrorInModule currentModule)) $ do
let imports = findImports decls
imports' = M.map (map (\(ss', dt, mmn) -> (ss', Just dt, mmn))) imports
scope = M.insert currentModule [(internalModuleSourceSpan "<module>", Nothing, Nothing)] imports'
(Module ss coms currentModule decls exps,) <$>
foldM (resolveModuleImport env) nullImports (M.toList scope)
resolveModuleImport
:: forall m
. MonadError MultipleErrors m
=> Env
-> Imports
-> (ModuleName, [(SourceSpan, Maybe ImportDeclarationType, Maybe ModuleName)])
-> m Imports
resolveModuleImport env ie (mn, imps) = foldM go ie imps
where
go :: Imports
-> (SourceSpan, Maybe ImportDeclarationType, Maybe ModuleName)
-> m Imports
go ie' (ss, typ, impQual) = do
modExports <-
maybe
(throwError . errorMessage' ss . UnknownName . Qualified Nothing $ ModName mn)
(return . envModuleExports)
(mn `M.lookup` env)
let impModules = importedModules ie'
qualModules = importedQualModules ie'
ie'' = ie' { importedModules = maybe (S.insert mn impModules) (const impModules) impQual
, importedQualModules = maybe qualModules (`S.insert` qualModules) impQual
}
resolveImport mn modExports ie'' impQual ss typ
resolveImport
:: forall m
. MonadError MultipleErrors m
=> ModuleName
-> Exports
-> Imports
-> Maybe ModuleName
-> SourceSpan
-> Maybe ImportDeclarationType
-> m Imports
resolveImport importModule exps imps impQual = resolveByType
where
resolveByType :: SourceSpan -> Maybe ImportDeclarationType -> m Imports
resolveByType ss Nothing =
importAll ss (importRef Local)
resolveByType ss (Just Implicit) =
importAll ss (importRef FromImplicit)
resolveByType _ (Just (Explicit refs)) =
checkRefs False refs >> foldM (importRef FromExplicit) imps refs
resolveByType ss (Just (Hiding refs)) =
checkRefs True refs >> importAll ss (importNonHidden refs)
checkRefs :: Bool -> [DeclarationRef] -> m ()
checkRefs isHiding = traverse_ check
where
check (ValueRef ss name) =
checkImportExists ss IdentName (exportedValues exps) name
check (ValueOpRef ss op) =
checkImportExists ss ValOpName (exportedValueOps exps) op
check (TypeRef ss name dctors) = do
checkImportExists ss TyName (exportedTypes exps) name
let (allDctors, _) = allExportedDataConstructors name
for_ dctors $ traverse_ (checkDctorExists ss name allDctors)
check (TypeOpRef ss name) =
checkImportExists ss TyOpName (exportedTypeOps exps) name
check (TypeClassRef ss name) =
checkImportExists ss TyClassName (exportedTypeClasses exps) name
check (ModuleRef ss name) | isHiding =
throwError . errorMessage' ss $ ImportHidingModule name
check (KindRef ss name) =
checkImportExists ss KiName (exportedKinds exps) name
check r = internalError $ "Invalid argument to checkRefs: " ++ show r
checkImportExists
:: Ord a
=> SourceSpan
-> (a -> Name)
-> M.Map a b
-> a
-> m ()
checkImportExists ss toName exports item
= when (item `M.notMember` exports)
. throwError . errorMessage' ss
$ UnknownImport importModule (toName item)
checkDctorExists
:: SourceSpan
-> ProperName 'TypeName
-> [ProperName 'ConstructorName]
-> ProperName 'ConstructorName
-> m ()
checkDctorExists ss tcon exports dctor
= when (dctor `notElem` exports)
. throwError . errorMessage' ss
$ UnknownImportDataConstructor importModule tcon dctor
importNonHidden :: [DeclarationRef] -> Imports -> DeclarationRef -> m Imports
importNonHidden hidden m ref | isHidden ref = return m
| otherwise = importRef FromImplicit m ref
where
isHidden :: DeclarationRef -> Bool
isHidden ref'@TypeRef{} = foldl (checkTypeRef ref') False hidden
isHidden ref' = ref' `elem` hidden
checkTypeRef :: DeclarationRef -> Bool -> DeclarationRef -> Bool
checkTypeRef _ True _ = True
checkTypeRef (TypeRef _ _ Nothing) acc (TypeRef _ _ (Just _)) = acc
checkTypeRef (TypeRef _ name (Just dctor)) _ (TypeRef _ name' (Just dctor')) = name == name' && dctor == dctor'
checkTypeRef (TypeRef _ name _) _ (TypeRef _ name' Nothing) = name == name'
checkTypeRef _ acc _ = acc
importAll :: SourceSpan -> (Imports -> DeclarationRef -> m Imports) -> m Imports
importAll ss importer =
foldM (\m (name, (dctors, _)) -> importer m (TypeRef ss name (Just dctors))) imps (M.toList (exportedTypes exps))
>>= flip (foldM (\m (name, _) -> importer m (TypeOpRef ss name))) (M.toList (exportedTypeOps exps))
>>= flip (foldM (\m (name, _) -> importer m (ValueRef ss name))) (M.toList (exportedValues exps))
>>= flip (foldM (\m (name, _) -> importer m (ValueOpRef ss name))) (M.toList (exportedValueOps exps))
>>= flip (foldM (\m (name, _) -> importer m (TypeClassRef ss name))) (M.toList (exportedTypeClasses exps))
>>= flip (foldM (\m (name, _) -> importer m (KindRef ss name))) (M.toList (exportedKinds exps))
importRef :: ImportProvenance -> Imports -> DeclarationRef -> m Imports
importRef prov imp (ValueRef ss name) = do
let values' = updateImports (importedValues imp) (exportedValues exps) id name ss prov
return $ imp { importedValues = values' }
importRef prov imp (ValueOpRef ss name) = do
let valueOps' = updateImports (importedValueOps imp) (exportedValueOps exps) id name ss prov
return $ imp { importedValueOps = valueOps' }
importRef prov imp (TypeRef ss name dctors) = do
let types' = updateImports (importedTypes imp) (exportedTypes exps) snd name ss prov
let (dctorNames, src) = allExportedDataConstructors name
dctorLookup :: M.Map (ProperName 'ConstructorName) ExportSource
dctorLookup = M.fromList $ map (, src) dctorNames
traverse_ (traverse_ $ checkDctorExists ss name dctorNames) dctors
let dctors' = foldl (\m d -> updateImports m dctorLookup id d ss prov) (importedDataConstructors imp) (fromMaybe dctorNames dctors)
return $ imp { importedTypes = types', importedDataConstructors = dctors' }
importRef prov imp (TypeOpRef ss name) = do
let ops' = updateImports (importedTypeOps imp) (exportedTypeOps exps) id name ss prov
return $ imp { importedTypeOps = ops' }
importRef prov imp (TypeClassRef ss name) = do
let typeClasses' = updateImports (importedTypeClasses imp) (exportedTypeClasses exps) id name ss prov
return $ imp { importedTypeClasses = typeClasses' }
importRef prov imp (KindRef ss name) = do
let kinds' = updateImports (importedKinds imp) (exportedKinds exps) id name ss prov
return $ imp { importedKinds = kinds' }
importRef _ _ TypeInstanceRef{} = internalError "TypeInstanceRef in importRef"
importRef _ _ ModuleRef{} = internalError "ModuleRef in importRef"
importRef _ _ ReExportRef{} = internalError "ReExportRef in importRef"
allExportedDataConstructors
:: ProperName 'TypeName
-> ([ProperName 'ConstructorName], ExportSource)
allExportedDataConstructors name =
fromMaybe (internalError "Invalid state in allExportedDataConstructors")
$ name `M.lookup` exportedTypes exps
updateImports
:: Ord a
=> M.Map (Qualified a) [ImportRecord a]
-> M.Map a b
-> (b -> ExportSource)
-> a
-> SourceSpan
-> ImportProvenance
-> M.Map (Qualified a) [ImportRecord a]
updateImports imps' exps' expName name ss prov =
let
src = maybe (internalError "Invalid state in updateImports") expName (name `M.lookup` exps')
rec = ImportRecord (Qualified (Just importModule) name) (exportSourceDefinedIn src) ss prov
in
M.alter
(\currNames -> Just $ rec : fromMaybe [] currNames)
(Qualified impQual name)
imps'