156 lines
6.5 KiB
Haskell
156 lines
6.5 KiB
Haskell
module Module.Resolver
|
|
( ResolvedExport(..)
|
|
, ResolvedModule(..)
|
|
, resolveModuleImport
|
|
, resolveModuleImportSelecting
|
|
, resolveModuleImports
|
|
, resolvedModulesEnv
|
|
) where
|
|
|
|
import ContentStore.Alias
|
|
import ContentStore.Arboricx (decodeTreeTerm, treeTermDomain)
|
|
import ContentStore.ViewTree (decodeViewTree, viewTreeKind, viewTreeRootTerm)
|
|
import ContentStore.Object
|
|
import ContentStore.Resolver
|
|
import Module.Manifest
|
|
import Research
|
|
|
|
import qualified Data.Map as Map
|
|
import qualified Data.Set as Set
|
|
import qualified Data.Text as T
|
|
|
|
-- | A manifest export resolved into the importing source's local lexical scope.
|
|
-- The executable term is loaded, while object/view refs remain available for
|
|
-- later checker and diagnostics phases.
|
|
data ResolvedExport = ResolvedExport
|
|
{ resolvedExportSourceName :: T.Text
|
|
, resolvedExportLocalName :: String
|
|
, resolvedExportObject :: ObjectRef
|
|
, resolvedExportAbi :: T.Text
|
|
, resolvedExportView :: Maybe ObjectRef
|
|
, resolvedExportProvenance :: Maybe ViewProvenance
|
|
, resolvedExportTerm :: T
|
|
} deriving (Show, Eq)
|
|
|
|
data ResolvedModule = ResolvedModule
|
|
{ resolvedModuleTarget :: String
|
|
, resolvedModuleNamespace :: String
|
|
, resolvedModuleManifest :: ObjectHash
|
|
, resolvedModuleExports :: [ResolvedExport]
|
|
} deriving (Show, Eq)
|
|
|
|
resolveModuleImports :: ObjectResolver -> [TricuAST] -> IO ([ResolvedModule], [TricuAST])
|
|
resolveModuleImports resolver asts = do
|
|
let (imports, nonImports) = foldr splitImport ([], []) asts
|
|
modules <- mapM (uncurry (resolveModuleImport resolver)) imports
|
|
return (modules, nonImports)
|
|
where
|
|
splitImport (SImport target namespace) (is, rest) = ((target, namespace) : is, rest)
|
|
splitImport ast (is, rest) = (is, ast : rest)
|
|
|
|
resolveModuleImport :: ObjectResolver -> String -> String -> IO ResolvedModule
|
|
resolveModuleImport resolver moduleTarget namespace =
|
|
resolveModuleImportSelecting resolver Nothing moduleTarget namespace
|
|
|
|
resolveModuleImportSelecting :: ObjectResolver -> Maybe (Set.Set T.Text) -> String -> String -> IO ResolvedModule
|
|
resolveModuleImportSelecting resolver selected moduleTarget namespace = do
|
|
manifestHash <- resolveModuleManifestHash resolver moduleTarget
|
|
mManifest <- resolveManifest resolver manifestHash
|
|
manifest <- case mManifest of
|
|
Nothing -> errorWithoutStackTrace $
|
|
"Module import failed for " ++ show moduleTarget
|
|
++ " as " ++ show namespace
|
|
++ ": manifest object not found (kind " ++ T.unpack (unDomain manifestDomain)
|
|
++ ", hash " ++ T.unpack manifestHash ++ ")"
|
|
Just value -> return value
|
|
let wantedExports = case selected of
|
|
Nothing -> moduleManifestExports manifest
|
|
Just names -> filter (\ex -> moduleExportName ex `Set.member` names) (moduleManifestExports manifest)
|
|
exports <- mapM (resolveModuleExport resolver localNamespace) wantedExports
|
|
return ResolvedModule
|
|
{ resolvedModuleTarget = moduleTarget
|
|
, resolvedModuleNamespace = namespace
|
|
, resolvedModuleManifest = manifestHash
|
|
, resolvedModuleExports = exports
|
|
}
|
|
where
|
|
localNamespace = if namespace == "!Local" then "" else namespace
|
|
|
|
resolveModuleExport :: ObjectResolver -> String -> ModuleExport -> IO ResolvedExport
|
|
resolveModuleExport resolver namespace ex = do
|
|
let ref = moduleExportObject ex
|
|
sourceName = moduleExportName ex
|
|
term <- resolveExportTerm resolver sourceName ref
|
|
return ResolvedExport
|
|
{ resolvedExportSourceName = sourceName
|
|
, resolvedExportLocalName = nsVariable namespace (T.unpack sourceName)
|
|
, resolvedExportObject = ref
|
|
, resolvedExportAbi = moduleExportAbi ex
|
|
, resolvedExportView = moduleExportView ex
|
|
, resolvedExportProvenance = moduleExportViewProvenance ex
|
|
, resolvedExportTerm = term
|
|
}
|
|
|
|
resolveExportTerm :: ObjectResolver -> T.Text -> ObjectRef -> IO T
|
|
resolveExportTerm resolver sourceName ref
|
|
| objectRefKind ref == viewTreeKind = do
|
|
bytes <- requireObject "view tree"
|
|
case decodeViewTree bytes >>= viewTreeRootTerm of
|
|
Left err -> errorWithoutStackTrace $
|
|
"Module export " ++ show (T.unpack sourceName)
|
|
++ " references invalid view tree " ++ T.unpack (objectRefHash ref)
|
|
++ ": " ++ err
|
|
Right term -> return term
|
|
| objectRefKind ref == unDomain treeTermDomain = do
|
|
bytes <- requireObject "tree term"
|
|
case decodeTreeTerm bytes of
|
|
Left err -> errorWithoutStackTrace $
|
|
"Module export " ++ show (T.unpack sourceName)
|
|
++ " references invalid tree term " ++ T.unpack (objectRefHash ref)
|
|
++ ": " ++ err
|
|
Right term -> return term
|
|
| otherwise = errorWithoutStackTrace $
|
|
"Module export " ++ show (T.unpack sourceName)
|
|
++ " has unsupported object kind " ++ show (T.unpack (objectRefKind ref))
|
|
++ "; expected " ++ show (T.unpack viewTreeKind)
|
|
++ " or " ++ show (T.unpack (unDomain treeTermDomain))
|
|
where
|
|
requireObject label = do
|
|
mBytes <- resolverObject resolver ref
|
|
case mBytes of
|
|
Just bytes -> return bytes
|
|
Nothing -> errorWithoutStackTrace $
|
|
"Module export " ++ show (T.unpack sourceName)
|
|
++ " references missing " ++ label ++ " " ++ T.unpack (objectRefHash ref)
|
|
++ " (kind " ++ T.unpack (objectRefKind ref) ++ ")"
|
|
|
|
resolvedModulesEnv :: [ResolvedModule] -> Env
|
|
resolvedModulesEnv modules = Map.fromList
|
|
[ (resolvedExportLocalName ex, resolvedExportTerm ex)
|
|
| m <- modules
|
|
, ex <- resolvedModuleExports m
|
|
]
|
|
|
|
resolveModuleManifestHash :: ObjectResolver -> String -> IO ObjectHash
|
|
resolveModuleManifestHash resolver moduleTarget = do
|
|
mAlias <- resolverAlias resolver ModuleAlias (T.pack moduleTarget)
|
|
case mAlias of
|
|
Just ref ->
|
|
if objectRefKind ref == unDomain manifestDomain
|
|
then return (objectRefHash ref)
|
|
else errorWithoutStackTrace $
|
|
"Module alias " ++ show moduleTarget
|
|
++ " points at unsupported object kind " ++ show (T.unpack (objectRefKind ref))
|
|
++ "; expected " ++ show (T.unpack (unDomain manifestDomain))
|
|
++ " (hash " ++ T.unpack (objectRefHash ref) ++ ")"
|
|
Nothing ->
|
|
case textToHashBytes (T.pack moduleTarget) of
|
|
Right _ -> return (T.pack moduleTarget)
|
|
Left _ -> errorWithoutStackTrace $
|
|
"Module alias not found: " ++ show moduleTarget
|
|
++ "; add it to tricu.workspace or write a ModuleAlias, or import by manifest hash"
|
|
|
|
nsVariable :: String -> String -> String
|
|
nsVariable "" name = name
|
|
nsVariable moduleName name = moduleName ++ "." ++ name
|