haskell-names-0.8.0: src/Language/Haskell/Names/Recursive.hs
{-# LANGUAGE TypeFamilies, ScopedTypeVariables #-}
module Language.Haskell.Names.Recursive
( resolve
, annotate
) where
import Data.Foldable (traverse_)
import Data.Graph(stronglyConnComp, flattenSCC)
import Data.Data (Data)
import Control.Monad (forM, forM_, unless)
import qualified Data.Map as Map (insert)
import Control.Monad.State.Strict (State, execState, get, modify)
import Language.Haskell.Exts
import Language.Haskell.Names.Types
import Language.Haskell.Names.SyntaxUtils
import Language.Haskell.Names.ScopeUtils
import Language.Haskell.Names.ModuleSymbols
import Language.Haskell.Names.Exports
import Language.Haskell.Names.Imports
import Language.Haskell.Names.Open.Base
import Language.Haskell.Names.Annotated
-- | Takes a list of modules and an environment and updates the environment
-- with each of the given modules' exported symbols.
resolve :: (Data l, Eq l) => [Module l] -> Environment -> Environment
resolve modules environment = updatedEnvironment where
moduleSCCs = groupModules modules
updatedEnvironment = execState (traverse_ findFixPoint moduleSCCs) environment
-- | Take a set of modules and return a list of sets, where each sets for
-- a strongly connected component in the import graph.
groupModules :: [Module l] -> [[Module l]]
groupModules modules =
map flattenSCC (stronglyConnComp (map moduleNode modules))
moduleNode :: Module l -> (Module l, ModuleName (), [ModuleName ()])
moduleNode modul =
( modul
, dropAnn (getModuleName modul)
, map (dropAnn . importModule) (getImports modul)
)
-- | Compute interfaces for a set of mutually recursive modules and
-- update the environment accordingly.
findFixPoint :: (Data l, Eq l) => [Module l] -> State Environment ()
findFixPoint modules = loop (replicate (length modules) []) where
loop modulesSymbols = do
forM_ (zip modules modulesSymbols) (\(modul, symbols) -> do
modify (Map.insert (dropAnn (getModuleName modul)) symbols))
environment <- get
modulesSymbols' <- forM modules (\modul -> do
let globalTable = moduleTable (importTable environment modul) modul
return (exportedSymbols globalTable modul))
unless (modulesSymbols == modulesSymbols') (loop modulesSymbols')
-- | Annotate a module with scoping information using the given environment.
-- All imports of the given module should be in the environment.
annotate :: (Data l, Eq l, SrcInfo l) => Environment -> Module l -> Module (Scoped l)
annotate environment modul@(Module _ _ _ _ _) =
Module l' maybeModuleHead' modulePragmas' importDecls' decls' where
Module l maybeModuleHead modulePragmas importDecls decls = modul
l' = none l
maybeModuleHead' = case maybeModuleHead of
Nothing -> Nothing
Just (ModuleHead lh moduleName maybeWarning maybeExports) ->
Just (ModuleHead lh' moduleName' maybeWarning' maybeExports') where
lh'= none lh
moduleName' = noScope moduleName
maybeWarning' = fmap noScope maybeWarning
maybeExports' = fmap (annotateExportSpecList globalTable) maybeExports
modulePragmas' = fmap noScope modulePragmas
importDecls' = annotateImportDecls moduleName environment importDecls
decls' = map (annotateDecl (initialScope (dropAnn moduleName) globalTable)) decls
globalTable = moduleTable (importTable environment modul) modul
moduleName = getModuleName modul
annotate _ _ = error "annotateModule: non-standard modules are not supported"