{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TupleSections #-} {-| This module defines the notion of a scope and operations on scopes. -} module Agda.Syntax.Scope.Base where import Control.Arrow ((***), first, second) import Control.Applicative import Control.DeepSeq import Data.Function import Data.List as List import Data.Map (Map) import qualified Data.Map as Map import Data.Maybe import Data.Typeable (Typeable) -- import Debug.Trace (trace) import Agda.Syntax.Position import Agda.Syntax.Common import Agda.Syntax.Fixity import Agda.Syntax.Abstract.Name as A import Agda.Syntax.Concrete.Name as C import Agda.Syntax.Concrete (ImportDirective(..), UsingOrHiding(..), ImportedName(..), Renaming(..)) import Agda.Utils.AssocList (AssocList) import qualified Agda.Utils.AssocList as AssocList import Agda.Utils.Functor import Agda.Utils.Lens import Agda.Utils.List import qualified Agda.Utils.Map as Map #include "undefined.h" import Agda.Utils.Impossible -- * Scope representation -- | A scope is a named collection of names partitioned into public and private -- names. data Scope = Scope { scopeName :: A.ModuleName , scopeParents :: [A.ModuleName] , scopeNameSpaces :: ScopeNameSpaces , scopeImports :: Map C.QName A.ModuleName , scopeDatatypeModule :: Bool } deriving (Typeable) -- | See 'Agda.Syntax.Common.Access'. data NameSpaceId = PrivateNS -- ^ Things not exported by this module. | PublicNS -- ^ Things defined and exported by this module. | ImportedNS -- ^ Things from open public, exported by this module. | OnlyQualifiedNS -- ^ Visible (as qualified) from outside, -- but not exported when opening the module. -- Used for qualified constructors. deriving (Typeable, Eq, Bounded, Enum) type ScopeNameSpaces = [(NameSpaceId, NameSpace)] localNameSpace :: Access -> NameSpaceId localNameSpace PublicAccess = PublicNS localNameSpace PrivateAccess = PrivateNS localNameSpace OnlyQualified = OnlyQualifiedNS nameSpaceAccess :: NameSpaceId -> Access nameSpaceAccess PrivateNS = PrivateAccess nameSpaceAccess _ = PublicAccess -- | Get a 'NameSpace' from 'Scope'. scopeNameSpace :: NameSpaceId -> Scope -> NameSpace scopeNameSpace ns = fromMaybe __IMPOSSIBLE__ . lookup ns . scopeNameSpaces -- | A lens for 'scopeNameSpaces' updateScopeNameSpaces :: (ScopeNameSpaces -> ScopeNameSpaces) -> Scope -> Scope updateScopeNameSpaces f s = s { scopeNameSpaces = f (scopeNameSpaces s) } -- | ``Monadic'' lens (Functor sufficient). updateScopeNameSpacesM :: (Functor m) => (ScopeNameSpaces -> m ScopeNameSpaces) -> Scope -> m Scope updateScopeNameSpacesM f s = for (f $ scopeNameSpaces s) $ \ x -> s { scopeNameSpaces = x } -- | The complete information about the scope at a particular program point -- includes the scope stack, the local variables, and the context precedence. data ScopeInfo = ScopeInfo { scopeCurrent :: A.ModuleName , scopeModules :: Map A.ModuleName Scope , scopeLocals :: LocalVars , scopePrecedence :: Precedence } deriving (Typeable) -- | Local variables. type LocalVars = AssocList C.Name LocalVar -- | A local variable can be shadowed by an import. -- In case of reference to a shadowed variable, we want to report -- a scope error. data LocalVar = LocalVar { localVar :: A.Name } -- ^ Unique ID of local variable. | ShadowedVar { localVar :: A.Name, localShadowedBy :: [AbstractName] } -- ^ This local variable is shadowed by one or more imports. -- (List not empty). deriving (Typeable) instance NFData LocalVar where rnf x = seq x () instance Eq LocalVar where (==) = (==) `on` localVar instance Ord LocalVar where compare = compare `on` localVar -- | We show shadowed variables as prefixed by a ".", as not in scope. instance Show LocalVar where show (LocalVar x) = show x show (ShadowedVar x xs) = "." ++ show x -- | Shadow a local name by a non-empty list of imports. shadowLocal :: [AbstractName] -> LocalVar -> LocalVar shadowLocal [] _ = __IMPOSSIBLE__ shadowLocal ys (LocalVar x ) = ShadowedVar x ys shadowLocal ys (ShadowedVar x zs) = ShadowedVar x (ys ++ zs) -- | Project name of unshadowed local variable. notShadowedLocal :: LocalVar -> Maybe A.Name notShadowedLocal (LocalVar x) = Just x notShadowedLocal ShadowedVar{} = Nothing -- | Get all locals that are not shadowed. notShadowedLocals :: LocalVars -> AssocList C.Name A.Name notShadowedLocals = mapMaybe $ \ (c,x) -> (c,) <$> notShadowedLocal x -- | Lens for 'scopeLocals'. updateScopeLocals :: (LocalVars -> LocalVars) -> ScopeInfo -> ScopeInfo updateScopeLocals f sc = sc { scopeLocals = f (scopeLocals sc) } setScopeLocals :: LocalVars -> ScopeInfo -> ScopeInfo setScopeLocals vars = updateScopeLocals (const vars) ------------------------------------------------------------------------ -- * Name spaces -- -- Map concrete names to lists of abstract names. ------------------------------------------------------------------------ -- | A @NameSpace@ contains the mappings from concrete names that the user can -- write to the abstract fully qualified names that the type checker wants to -- read. data NameSpace = NameSpace { nsNames :: NamesInScope -- ^ Maps concrete names to a list of abstract names. , nsModules :: ModulesInScope -- ^ Maps concrete module names to a list of abstract module names. } deriving (Typeable) type ThingsInScope a = Map C.Name [a] type NamesInScope = ThingsInScope AbstractName type ModulesInScope = ThingsInScope AbstractModule -- | Set of types consisting of exactly 'AbstractName' and 'AbstractModule'. -- -- A GADT just for some dependent-types trickery. data InScopeTag a where NameTag :: InScopeTag AbstractName ModuleTag :: InScopeTag AbstractModule -- | Type class for some dependent-types trickery. class Eq a => InScope a where inScopeTag :: InScopeTag a instance InScope AbstractName where inScopeTag = NameTag instance InScope AbstractModule where inScopeTag = ModuleTag -- | @inNameSpace@ selects either the name map or the module name map from -- a 'NameSpace'. What is selected is determined by result type -- (using the dependent-type trickery). inNameSpace :: forall a. InScope a => NameSpace -> ThingsInScope a inNameSpace = case inScopeTag :: InScopeTag a of NameTag -> nsNames ModuleTag -> nsModules ------------------------------------------------------------------------ -- * Decorated names -- -- - What kind of name? (defined, constructor...) -- - Where does the name come from? (to explain to user) ------------------------------------------------------------------------ -- | For the sake of parsing left-hand sides, we distinguish -- constructor and record field names from defined names. data KindOfName = ConName -- ^ Constructor name. | FldName -- ^ Record field name. | DefName -- ^ Ordinary defined name. | PatternSynName -- ^ Name of a pattern synonym. | QuotableName -- ^ A name that can only quoted. deriving (Eq, Show, Typeable, Enum, Bounded) -- | A list containing all name kinds. allKindsOfNames :: [KindOfName] allKindsOfNames = [minBound..maxBound] -- | Where does a name come from? -- -- This information is solely for reporting to the user, -- see 'Agda.Interaction.InteractionTop.whyInScope'. data WhyInScope = Defined -- ^ Defined in this module. | Opened C.QName WhyInScope -- ^ Imported from another module. | Applied C.QName WhyInScope -- ^ Imported by a module application. deriving (Typeable) -- | A decoration of 'Agda.Syntax.Abstract.Name.QName'. data AbstractName = AbsName { anameName :: A.QName -- ^ The resolved qualified name. , anameKind :: KindOfName -- ^ The kind (definition, constructor, record field etc.). , anameLineage :: WhyInScope -- ^ Explanation where this name came from. } deriving (Typeable) -- | A decoration of abstract syntax module names. data AbstractModule = AbsModule { amodName :: A.ModuleName -- ^ The resolved module name. , amodLineage :: WhyInScope -- ^ Explanation where this name came from. } deriving (Typeable) instance Eq AbstractName where (==) = (==) `on` anameName instance Ord AbstractName where compare = compare `on` anameName -- | Van Laarhoven lens on 'anameName'. lensAnameName :: Functor m => (A.QName -> m A.QName) -> AbstractName -> m AbstractName lensAnameName f am = f (anameName am) <&> \ m -> am { anameName = m } instance Eq AbstractModule where (==) = (==) `on` amodName instance Ord AbstractModule where compare = compare `on` amodName -- | Van Laarhoven lens on 'amodName'. lensAmodName :: Functor m => (A.ModuleName -> m A.ModuleName) -> AbstractModule -> m AbstractModule lensAmodName f am = f (amodName am) <&> \ m -> am { amodName = m } -- * Operations on name and module maps. mergeNames :: Eq a => ThingsInScope a -> ThingsInScope a -> ThingsInScope a mergeNames = Map.unionWith union ------------------------------------------------------------------------ -- * Operations on name spaces ------------------------------------------------------------------------ -- | The empty name space. emptyNameSpace :: NameSpace emptyNameSpace = NameSpace Map.empty Map.empty -- | Map functions over the names and modules in a name space. mapNameSpace :: (NamesInScope -> NamesInScope ) -> (ModulesInScope -> ModulesInScope) -> NameSpace -> NameSpace mapNameSpace fd fm ns = ns { nsNames = fd $ nsNames ns , nsModules = fm $ nsModules ns } -- | Zip together two name spaces. zipNameSpace :: (NamesInScope -> NamesInScope -> NamesInScope ) -> (ModulesInScope -> ModulesInScope -> ModulesInScope) -> NameSpace -> NameSpace -> NameSpace zipNameSpace fd fm ns1 ns2 = ns1 { nsNames = nsNames ns1 `fd` nsNames ns2 , nsModules = nsModules ns1 `fm` nsModules ns2 } -- | Map monadic function over a namespace. mapNameSpaceM :: Applicative m => (NamesInScope -> m NamesInScope ) -> (ModulesInScope -> m ModulesInScope) -> NameSpace -> m NameSpace mapNameSpaceM fd fm ns = update ns <$> fd (nsNames ns) <*> fm (nsModules ns) where update ns ds ms = ns { nsNames = ds, nsModules = ms } ------------------------------------------------------------------------ -- * General operations on scopes ------------------------------------------------------------------------ -- | The empty scope. emptyScope :: Scope emptyScope = Scope { scopeName = noModuleName , scopeParents = [] , scopeNameSpaces = [ (nsid, emptyNameSpace) | nsid <- [minBound..maxBound] ] , scopeImports = Map.empty , scopeDatatypeModule = False } -- | The empty scope info. emptyScopeInfo :: ScopeInfo emptyScopeInfo = ScopeInfo { scopeCurrent = noModuleName , scopeModules = Map.singleton noModuleName emptyScope , scopeLocals = [] , scopePrecedence = TopCtx } -- | Map functions over the names and modules in a scope. mapScope :: (NameSpaceId -> NamesInScope -> NamesInScope ) -> (NameSpaceId -> ModulesInScope -> ModulesInScope) -> Scope -> Scope mapScope fd fm = updateScopeNameSpaces $ AssocList.mapWithKey mapNS where mapNS acc = mapNameSpace (fd acc) (fm acc) -- | Same as 'mapScope' but applies the same function to all name spaces. mapScope_ :: (NamesInScope -> NamesInScope ) -> (ModulesInScope -> ModulesInScope) -> Scope -> Scope mapScope_ fd fm = mapScope (const fd) (const fm) -- | Map monadic functions over the names and modules in a scope. mapScopeM :: (Functor m, Applicative m) => (NameSpaceId -> NamesInScope -> m NamesInScope ) -> (NameSpaceId -> ModulesInScope -> m ModulesInScope) -> Scope -> m Scope mapScopeM fd fm = updateScopeNameSpacesM $ AssocList.mapWithKeyM mapNS where mapNS acc = mapNameSpaceM (fd acc) (fm acc) -- | Same as 'mapScopeM' but applies the same function to both the public and -- private name spaces. mapScopeM_ :: (Functor m, Applicative m) => (NamesInScope -> m NamesInScope ) -> (ModulesInScope -> m ModulesInScope) -> Scope -> m Scope mapScopeM_ fd fm = mapScopeM (const fd) (const fm) -- | Zip together two scopes. The resulting scope has the same name as the -- first scope. zipScope :: (NameSpaceId -> NamesInScope -> NamesInScope -> NamesInScope ) -> (NameSpaceId -> ModulesInScope -> ModulesInScope -> ModulesInScope) -> Scope -> Scope -> Scope zipScope fd fm s1 s2 = s1 { scopeNameSpaces = [ (nsid, zipNS nsid ns1 ns2) | ((nsid, ns1), (nsid', ns2)) <- zipWith' (,) (scopeNameSpaces s1) (scopeNameSpaces s2) , assert (nsid == nsid') ] , scopeImports = Map.union (scopeImports s1) (scopeImports s2) } where assert True = True assert False = __IMPOSSIBLE__ zipNS acc = zipNameSpace (fd acc) (fm acc) -- | Same as 'zipScope' but applies the same function to both the public and -- private name spaces. zipScope_ :: (NamesInScope -> NamesInScope -> NamesInScope ) -> (ModulesInScope -> ModulesInScope -> ModulesInScope) -> Scope -> Scope -> Scope zipScope_ fd fm = zipScope (const fd) (const fm) -- | Filter a scope keeping only concrete names matching the predicates. -- The first predicate is applied to the names and the second to the modules. filterScope :: (C.Name -> Bool) -> (C.Name -> Bool) -> Scope -> Scope filterScope pd pm = mapScope_ (Map.filterKeys pd) (Map.filterKeys pm) -- | Return all names in a scope. allNamesInScope :: InScope a => Scope -> ThingsInScope a allNamesInScope = namesInScope [minBound..maxBound] allNamesInScope' :: InScope a => Scope -> ThingsInScope (a, Access) allNamesInScope' s = foldr1 mergeNames [ map (, nameSpaceAccess ns) <$> namesInScope [ns] s | ns <- [minBound..maxBound] ] -- | Returns the scope's non-private names. exportedNamesInScope :: InScope a => Scope -> ThingsInScope a exportedNamesInScope = namesInScope [PublicNS, ImportedNS, OnlyQualifiedNS] namesInScope :: InScope a => [NameSpaceId] -> Scope -> ThingsInScope a namesInScope ids s = foldr1 mergeNames [ inNameSpace (scopeNameSpace nsid s) | nsid <- ids ] allThingsInScope :: Scope -> NameSpace allThingsInScope = thingsInScope [minBound..maxBound] thingsInScope :: [NameSpaceId] -> Scope -> NameSpace thingsInScope fs s = NameSpace { nsNames = namesInScope fs s , nsModules = namesInScope fs s } -- | Merge two scopes. The result has the name of the first scope. mergeScope :: Scope -> Scope -> Scope mergeScope = zipScope_ mergeNames mergeNames -- | Merge a non-empty list of scopes. The result has the name of the first -- scope in the list. mergeScopes :: [Scope] -> Scope mergeScopes [] = __IMPOSSIBLE__ mergeScopes ss = foldr1 mergeScope ss -- * Specific operations on scopes -- | Move all names in a scope to the given name space (except never move from -- Imported to Public). setScopeAccess :: NameSpaceId -> Scope -> Scope setScopeAccess a s = (`updateScopeNameSpaces` s) $ AssocList.mapWithKey $ const . ns where zero = emptyNameSpace one = allThingsInScope s imp = thingsInScope [ImportedNS] s noimp = thingsInScope [PublicNS, PrivateNS, OnlyQualifiedNS] s ns b = case (a, b) of (PublicNS, PublicNS) -> noimp (PublicNS, ImportedNS) -> imp _ | a == b -> one | otherwise -> zero -- | Update a particular name space. setNameSpace :: NameSpaceId -> NameSpace -> Scope -> Scope setNameSpace nsid ns = updateScopeNameSpaces $ AssocList.update nsid ns -- | Add names to a scope. addNamesToScope :: NameSpaceId -> C.Name -> [AbstractName] -> Scope -> Scope addNamesToScope acc x ys s = mergeScope s s1 where s1 = setScopeAccess acc $ setNameSpace PublicNS ns emptyScope ns = emptyNameSpace { nsNames = Map.singleton x ys } -- | Add a name to a scope. addNameToScope :: NameSpaceId -> C.Name -> AbstractName -> Scope -> Scope addNameToScope acc x y s = addNamesToScope acc x [y] s -- | Remove a name from a scope. removeNameFromScope :: NameSpaceId -> C.Name -> Scope -> Scope removeNameFromScope ns x s = mapScope remove (const id) s where remove ns' | ns' /= ns = id | otherwise = Map.delete x -- | Add a module to a scope. addModuleToScope :: NameSpaceId -> C.Name -> AbstractModule -> Scope -> Scope addModuleToScope acc x m s = mergeScope s s1 where s1 = setScopeAccess acc $ setNameSpace PublicNS ns emptyScope ns = emptyNameSpace { nsModules = Map.singleton x [m] } -- | Apply an 'ImportDirective' to a scope. applyImportDirective :: ImportDirective -> Scope -> Scope applyImportDirective dir s = mergeScope usedOrHidden renamed where usedOrHidden = useOrHide (hideLHS (renaming dir) $ usingOrHiding dir) s renamed = rename (renaming dir) $ useOrHide useRenamedThings s useRenamedThings = Using $ map renFrom $ renaming dir hideLHS :: [Renaming] -> UsingOrHiding -> UsingOrHiding hideLHS _ i@(Using _) = i hideLHS ren (Hiding xs) = Hiding $ xs ++ map renFrom ren useOrHide :: UsingOrHiding -> Scope -> Scope useOrHide (Hiding xs) s = filterNames notElem notElem xs s useOrHide (Using xs) s = filterNames elem elem xs s filterNames :: (C.Name -> [C.Name] -> Bool) -> (C.Name -> [C.Name] -> Bool) -> [ImportedName] -> Scope -> Scope filterNames pd pm xs = filterScope' (flip pd ds) (flip pm ms) where ds = [ x | ImportedName x <- xs ] ms = [ m | ImportedModule m <- xs ] filterScope' pd pm = filterScope pd pm -- Renaming rename :: [Renaming] -> Scope -> Scope rename rho = mapScope_ (Map.mapKeys $ ren drho) (Map.mapKeys $ ren mrho) where mrho = [ (x, y) | Renaming { renFrom = ImportedModule x, renTo = y } <- rho ] drho = [ (x, y) | Renaming { renFrom = ImportedName x, renTo = y } <- rho ] ren r x = fromMaybe x $ lookup x r -- | Rename the abstract names in a scope. renameCanonicalNames :: Map A.QName A.QName -> Map A.ModuleName A.ModuleName -> Scope -> Scope renameCanonicalNames renD renM = mapScope_ renameD renameM where renameD = Map.map $ map $ over lensAnameName $ \ x -> Map.findWithDefault x x renD renameM = Map.map $ map $ over lensAmodName $ \ x -> Map.findWithDefault x x renM -- | Remove private name space of a scope. -- -- Should be a right identity for 'exportedNamesInScope'. -- @exportedNamesInScope . restrictPrivate == exportedNamesInscope@. restrictPrivate :: Scope -> Scope restrictPrivate s = setNameSpace PrivateNS emptyNameSpace $ s { scopeImports = Map.empty } -- | Remove names that can only be used qualified (when opening a scope) removeOnlyQualified :: Scope -> Scope removeOnlyQualified s = setNameSpace OnlyQualifiedNS emptyNameSpace s -- | Add an explanation to why things are in scope. inScopeBecause :: (WhyInScope -> WhyInScope) -> Scope -> Scope inScopeBecause f = mapScope_ mapName mapMod where mapName = fmap . map $ \a -> a { anameLineage = f $ anameLineage a } mapMod = fmap . map $ \a -> a { amodLineage = f $ amodLineage a } -- | Get the public parts of the public modules of a scope publicModules :: ScopeInfo -> Map A.ModuleName Scope publicModules scope = Map.filterWithKey (\ m _ -> reachable m) allMods where -- Get all modules in the ScopeInfo. allMods = Map.map restrictPrivate $ scopeModules scope root = scopeCurrent scope modules s = map amodName $ concat $ Map.elems $ allNamesInScope s chase m = m : concatMap chase ms where ms = maybe __IMPOSSIBLE__ modules $ Map.lookup m allMods reachable = (`elem` chase root) everythingInScope :: ScopeInfo -> NameSpace everythingInScope scope = allThingsInScope $ mergeScopes $ [ s | (m, s) <- Map.toList (scopeModules scope), m `elem` current ] where this = scopeCurrent scope parents = maybe __IMPOSSIBLE__ scopeParents $ Map.lookup this $ scopeModules scope current = this : parents -- | Compute a flattened scope. Only include unqualified names or names -- qualified by modules in the first argument. flattenScope :: [[C.Name]] -> ScopeInfo -> Map C.QName [AbstractName] flattenScope ms scope = Map.unionWith (++) (build ms allNamesInScope root) imported where current = moduleScope $ scopeCurrent scope root = mergeScopes $ current : map moduleScope (scopeParents current) imported = Map.unionsWith (++) [ qual c (build ms' exportedNamesInScope $ moduleScope a) | (c, a) <- Map.toList $ scopeImports root , let m = C.qnameParts c ms' = map (drop (length m)) $ filter (m `isPrefixOf`) ms , not $ null ms' ] qual c = Map.mapKeys (q c) where q (C.QName x) = C.Qual x q (C.Qual m x) = C.Qual m . q x build :: [[C.Name]] -> (forall a. InScope a => Scope -> ThingsInScope a) -> Scope -> Map C.QName [AbstractName] build ms getNames s = Map.unionWith (++) (Map.mapKeys (\x -> C.QName x) (getNames s)) $ Map.unionsWith (++) $ [ Map.mapKeys (\y -> C.Qual x y) $ build ms' exportedNamesInScope $ moduleScope m | (x, mods) <- Map.toList (getNames s) , let ms' = [ tl | hd:tl <- ms, hd == x ] , not $ null ms' , AbsModule m _ <- mods ] moduleScope :: A.ModuleName -> Scope moduleScope m = fromMaybe __IMPOSSIBLE__ $ Map.lookup m $ scopeModules scope -- | Look up a name in the scope scopeLookup :: InScope a => C.QName -> ScopeInfo -> [a] scopeLookup q scope = map fst $ scopeLookup' q scope scopeLookup' :: forall a. InScope a => C.QName -> ScopeInfo -> [(a, Access)] scopeLookup' q scope = nubBy ((==) `on` fst) $ findName q root ++ topImports ++ imports where -- 1. Finding a name in the current scope and its parents. moduleScope :: A.ModuleName -> Scope moduleScope m = fromMaybe __IMPOSSIBLE__ $ Map.lookup m $ scopeModules scope current :: Scope current = moduleScope $ scopeCurrent scope root :: Scope root = mergeScopes $ current : map moduleScope (scopeParents current) -- | Find a concrete, possibly qualified name in scope @s@. findName :: forall a. InScope a => C.QName -> Scope -> [(a, Access)] findName q0 s = case q0 of C.QName x -> lookupName x s C.Qual x q -> do let -- | Get the modules named @x@ in scope @s@. mods :: [A.ModuleName] mods = amodName . fst <$> lookupName x s -- | Get the definitions named @x@ in scope @s@ and interpret them as modules. -- Andreas, 2013-05-01: Issue 836 debates this feature: -- Qualified constructors are qualified by their datatype rather than a module defs :: [A.ModuleName] defs = mnameFromList . qnameToList . anameName . fst <$> lookupName x s -- Andreas, 2013-05-01: Issue 836 complains about the feature -- that constructors can also be qualified by their datatype -- and projections by their record type. This feature is off -- if we just consider the modules: -- m <- mods -- The feature is on if we consider also the data and record types: -- trace ("mods ++ defs = " ++ show (mods ++ defs)) $ do m <- nub $ mods ++ defs -- record types will appear both as a mod and a def -- Get the scope of module m, if any, and remove its private definitions. let ss = maybeToList $ Map.lookup m $ scopeModules scope ss' = restrictPrivate <$> ss -- trace ("ss = " ++ show ss ) $ do -- trace ("ss' = " ++ show ss') $ do s' <- ss' findName q s' where lookupName :: forall a. InScope a => C.Name -> Scope -> [(a, Access)] lookupName x s = fromMaybe [] $ Map.lookup x $ allNamesInScope' s -- 2. Finding a name in the top imports. topImports :: [(a, Access)] topImports = case (inScopeTag :: InScopeTag a) of NameTag -> [] ModuleTag -> map (first (`AbsModule` Defined)) (imported q) imported :: C.QName -> [(A.ModuleName, Access)] imported q = map (,PublicAccess) $ maybeToList $ Map.lookup q $ scopeImports root -- 3. Finding a name in the imports belonging to an initial part of the qualifier. imports :: [(a, Access)] imports = do (m, x) <- splitName q m <- fst <$> imported m findName x (restrictPrivate $ moduleScope m) -- return all possible splittings, e.g. -- splitName X.Y.Z = [(X, Y.Z), (X.Y, Z)] splitName :: C.QName -> [(C.QName, C.QName)] splitName (C.QName x) = [] splitName (C.Qual x q) = (C.QName x, q) : [ (C.Qual x m, r) | (m, r) <- splitName q ] -- * Inverse look-up data AllowAmbiguousConstructors = AllowAmbiguousConstructors | NoAmbiguousConstructors deriving (Eq) -- | Find the shortest concrete name that maps (uniquely) to a given abstract -- name. inverseScopeLookup :: Either A.ModuleName A.QName -> ScopeInfo -> Maybe C.QName inverseScopeLookup = inverseScopeLookup' AllowAmbiguousConstructors inverseScopeLookup' :: AllowAmbiguousConstructors -> Either A.ModuleName A.QName -> ScopeInfo -> Maybe C.QName inverseScopeLookup' ambCon name scope = case name of Left m -> best $ filter unambiguousModule $ findModule m Right q -> best $ filter unambiguousName $ findName nameMap q where this = scopeCurrent scope current = this : scopeParents (moduleScope this) scopes = [ (m, restrict m s) | (m, s) <- Map.toList (scopeModules scope) ] moduleScope :: A.ModuleName -> Scope moduleScope m = fromMaybe __IMPOSSIBLE__ $ Map.lookup m $ scopeModules scope restrict m s | m `elem` current = s | otherwise = restrictPrivate s len :: C.QName -> Int len (C.QName _) = 1 len (C.Qual _ x) = 1 + len x best :: [C.QName] -> Maybe C.QName best xs = headMaybe $ sortBy (compare `on` len) xs unique :: forall a . [a] -> Bool unique [] = __IMPOSSIBLE__ unique [_] = True unique (_:_:_) = False unambiguousModule q = unique (scopeLookup q scope :: [AbstractModule]) unambiguousName q = unique xs || AllowAmbiguousConstructors == ambCon && all ((ConName ==) . anameKind) xs where xs = scopeLookup q scope findName :: Ord a => Map a [(A.ModuleName, C.Name)] -> a -> [C.QName] findName table q = do (m, x) <- fromMaybe [] $ Map.lookup q table if m `elem` current then return (C.QName x) else do y <- findModule m return $ C.qualify y x findModule :: A.ModuleName -> [C.QName] findModule q = findName moduleMap q ++ fromMaybe [] (Map.lookup q importMap) importMap = Map.unionsWith (++) $ do (m, s) <- scopes (x, y) <- Map.toList $ scopeImports s return $ Map.singleton y [x] moduleMap = Map.unionsWith (++) $ do (m, s) <- scopes (x, ms) <- Map.toList (allNamesInScope s) q <- amodName <$> ms return $ Map.singleton q [(m, x)] nameMap = Map.unionsWith (++) $ do (m, s) <- scopes (x, ms) <- Map.toList (allNamesInScope s) q <- anameName <$> ms return $ Map.singleton q [(m, x)] -- | Takes the first component of 'inverseScopeLookup'. inverseScopeLookupName :: A.QName -> ScopeInfo -> Maybe C.QName inverseScopeLookupName x = inverseScopeLookup (Right x) inverseScopeLookupName' :: AllowAmbiguousConstructors -> A.QName -> ScopeInfo -> Maybe C.QName inverseScopeLookupName' ambCon x = inverseScopeLookup' ambCon (Right x) -- | Takes the second component of 'inverseScopeLookup'. inverseScopeLookupModule :: A.ModuleName -> ScopeInfo -> Maybe C.QName inverseScopeLookupModule x = inverseScopeLookup (Left x) ------------------------------------------------------------------------ -- * (Debug) printing ------------------------------------------------------------------------ instance Show AbstractName where show = show . anameName instance Show AbstractModule where show = show . amodName instance Show NameSpaceId where show nsid = case nsid of PublicNS -> "public" PrivateNS -> "private" ImportedNS -> "imported" OnlyQualifiedNS -> "only-qualified" instance Show NameSpace where show (NameSpace names mods) = unlines $ blockOfLines "names" (map pr $ Map.toList names) ++ blockOfLines "modules" (map pr $ Map.toList mods) where pr :: (Show a, Show b) => (a,b) -> String pr (x, y) = show x ++ " --> " ++ show y instance Show Scope where show (scope@Scope{ scopeName = name, scopeParents = parents, scopeImports = imps }) = unlines $ [ "* scope " ++ show name ] ++ ind ( concat [ blockOfLines (show nsid) (lines $ show $ scopeNameSpace nsid scope) | nsid <- [minBound..maxBound] ] ++ blockOfLines "imports" (case Map.keys imps of [] -> [] ks -> [ show ks ] ) ) where ind = map (" " ++) -- | Add first string only if list is non-empty. blockOfLines :: String -> [String] -> [String] blockOfLines _ [] = [] blockOfLines hd ss = hd : map (" " ++) ss instance Show ScopeInfo where show (ScopeInfo this mods locals ctx) = unlines $ [ "ScopeInfo" , " current = " ++ show this ] ++ (if null locals then [] else [ " locals = " ++ show locals ]) ++ [ " context = " ++ show ctx , " modules" ] ++ map (" "++) (relines . map show $ Map.elems mods) where relines = filter (not . null) . lines . unlines ------------------------------------------------------------------------ -- * Boring instances ------------------------------------------------------------------------ instance KillRange ScopeInfo where killRange m = m instance HasRange AbstractName where getRange = getRange . anameName instance SetRange AbstractName where setRange r x = x { anameName = setRange r $ anameName x }