{-# LANGUAGE CPP #-} {-| The scope monad with operations. -} module Agda.Syntax.Scope.Monad where import Prelude hiding (mapM) import Control.Applicative import Control.Monad hiding (mapM) import Data.Map (Map) import Data.Traversable import Data.List import qualified Data.Map as Map import Agda.Syntax.Common import Agda.Syntax.Position import Agda.Syntax.Fixity import Agda.Syntax.Abstract.Name as A import Agda.Syntax.Concrete as C import Agda.Syntax.Scope.Base import Agda.TypeChecking.Monad.Base import Agda.TypeChecking.Monad.State import Agda.TypeChecking.Monad.Options import Agda.Utils.Tuple import Agda.Utils.Fresh import Agda.Utils.Size import Agda.Utils.List #include "../../undefined.h" import Agda.Utils.Impossible -- * The scope checking monad -- | To simplify interaction between scope checking and type checking (in -- particular when chasing imports), we use the same monad. type ScopeM = TCM -- * Errors notInScope :: C.QName -> ScopeM a notInScope x = typeError $ NotInScope [x] -- * General operations -- | Apply a function to the scope info. modifyScopeInfo :: (ScopeInfo -> ScopeInfo) -> ScopeM () modifyScopeInfo f = do scope <- getScope setScope $ f scope -- | Apply a function to the scope stack. modifyScopeStack :: (ScopeStack -> ScopeStack) -> ScopeM () modifyScopeStack f = modifyScopeInfo $ \s -> s { scopeStack = f $ scopeStack s } -- | Apply a function to the top scope. modifyTopScope :: (Scope -> Scope) -> ScopeM () modifyTopScope f = modifyScopeStack $ \(s:ss) -> f s : ss -- | Apply a monadic function to the top scope. modifyTopScopeM :: (Scope -> ScopeM Scope) -> ScopeM () modifyTopScopeM f = do s : _ <- scopeStack <$> getScope s' <- f s modifyTopScope (const s') -- | Apply a function to the public or private name space. modifyTopNameSpace :: Access -> (NameSpace -> NameSpace) -> ScopeM () modifyTopNameSpace acc f = modifyTopScope action where action s = s { scopePublic = pub $ scopePublic s , scopePrivate = pri $ scopePrivate s } (pub, pri) = case acc of PublicAccess -> (f, id) PrivateAccess -> (id, f) -- | Set context precedence setContextPrecedence :: Precedence -> ScopeM () setContextPrecedence p = modifyScopeInfo $ \s -> s { scopePrecedence = p } getContextPrecedence :: ScopeM Precedence getContextPrecedence = scopePrecedence <$> getScope withContextPrecedence :: Precedence -> ScopeM a -> ScopeM a withContextPrecedence p m = do p' <- getContextPrecedence setContextPrecedence p x <- m setContextPrecedence p' return x getLocalVars :: ScopeM LocalVars getLocalVars = scopeLocals <$> getScope setLocalVars :: LocalVars -> ScopeM () setLocalVars vars = modifyScope $ \s -> s { scopeLocals = vars } -- | Run a computation without changing the local variables. withLocalVars :: ScopeM a -> ScopeM a withLocalVars m = do vars <- getLocalVars x <- m setLocalVars vars return x -- * Names -- | Create a fresh abstract name from a concrete name. freshAbstractName :: Fixity -> C.Name -> ScopeM A.Name freshAbstractName fx x = do i <- fresh return $ A.Name i x (getRange x) fx -- | @freshAbstractName_ = freshAbstractName defaultFixity@ freshAbstractName_ :: C.Name -> ScopeM A.Name freshAbstractName_ = freshAbstractName defaultFixity -- | Create a fresh abstract qualified name. freshAbstractQName :: Fixity -> C.Name -> ScopeM A.QName freshAbstractQName fx x = do y <- freshAbstractName fx x m <- getCurrentModule return $ A.qualify m y -- * Simple queries -- | Returns the name of the current module, with the range set to -- 'noRange'. getCurrentModule :: ScopeM ModuleName getCurrentModule = setRange noRange . A.mnameFromList . concatMap A.mnameToList . reverse . map scopeName . scopeStack <$> getScope -- * Resolving names data ResolvedName = VarName A.Name | DefinedName AbstractName | ConstructorName [AbstractName] | UnknownName deriving (Show) -- | Look up the abstract name referred to by a given concrete name. resolveName :: C.QName -> ScopeM ResolvedName resolveName x = do scope <- getScope let vars = map (C.QName -*- id) $ scopeLocals scope defs = allNamesInScope . mergeScopes . scopeStack $ scope case lookup x vars of Just y -> return $ VarName $ y { nameConcrete = unqualify x } Nothing -> case Map.lookup x defs of Just ds | all ((==ConName) . anameKind) ds -> return $ ConstructorName $ map (\d -> updateConcreteName d $ unqualify x) ds Just [d] -> return $ DefinedName $ updateConcreteName d (unqualify x) Just ds -> typeError $ AmbiguousName x (map anameName ds) Nothing -> return UnknownName where updateConcreteName :: AbstractName -> C.Name -> AbstractName updateConcreteName d@(AbsName { anameName = an@(A.QName { qnameName = qn }) }) x = d { anameName = an { qnameName = qn { nameConcrete = x } } } -- | Look up a module in the scope. resolveModule :: C.QName -> ScopeM AbstractModule resolveModule x = do ms <- resolveModule' x case ms of [AbsModule m] -> return $ AbsModule (m `withRangesOfQ` x) [] -> typeError $ NoSuchModule x ms -> typeError $ AmbiguousModule x (map amodName ms) resolveModule' :: C.QName -> ScopeM [AbstractModule] resolveModule' x = do ms <- allModulesInScope . mergeScopes . scopeStack <$> getScope case Map.lookup x ms of Just ms -> return ms Nothing -> return [] -- | Get the fixity of a name. The name is assumed to be in scope. getFixity :: C.QName -> ScopeM Fixity getFixity x = do r <- resolveName x case r of VarName y -> return $ nameFixity y DefinedName d -> return $ nameFixity $ qnameName $ anameName d ConstructorName ds | null fs -> __IMPOSSIBLE__ | allEqual fs -> return $ head fs | otherwise -> return defaultFixity where fs = map (nameFixity . qnameName . anameName) ds UnknownName -> __IMPOSSIBLE__ -- * Binding names -- | Bind a variable. The abstract name is supplied as the second argument. bindVariable :: C.Name -> A.Name -> ScopeM () bindVariable x y = do scope <- getScope let scope' = scope { scopeLocals = (x, y) : scopeLocals scope } setScope scope' -- | Bind a defined name. Must not shadow anything. bindName :: Access -> KindOfName -> C.Name -> A.QName -> ScopeM () bindName acc kind x y = do r <- resolveName (C.QName x) ys <- case r of DefinedName d -> typeError $ ClashingDefinition (C.QName x) $ anameName d VarName z -> typeError $ ClashingDefinition (C.QName x) $ A.qualify (mnameFromList []) z ConstructorName [] -> __IMPOSSIBLE__ ConstructorName ds | kind == ConName && all ((==ConName) . anameKind) ds -> return [ AbsName y kind ] | otherwise -> typeError $ ClashingDefinition (C.QName x) $ anameName (head ds) -- TODO: head UnknownName -> return [AbsName y kind] modifyTopScope $ addNamesToScope acc (C.QName x) ys -- | Bind a module name. bindModule :: Access -> C.Name -> A.ModuleName -> ScopeM () bindModule acc x m = bindQModule acc (C.QName x) m -- | Bind a qualified module name. bindQModule :: Access -> C.QName -> A.ModuleName -> ScopeM () bindQModule acc x m = modifyTopScope $ addModuleToScope acc x $ AbsModule m -- * Module manipulation operations -- | Clear the scope of any no names. stripNoNames :: ScopeM () stripNoNames = modifyScopeStack $ map strip where strip = mapScope (\_ -> stripN) (\_ -> stripN) stripN m = Map.filterWithKey (const . notNoName) m notNoName = not . any isNoName . qnameParts -- | Push a new scope onto the scope stack pushScope :: A.ModuleName -> ScopeM () pushScope name = modifyScopeStack (s:) where s = emptyScope { scopeName = name } {-| Pop the top scope from the scope stack and incorporate its (public) contents in the new top scope. Depending on the first argument the contents is added to the public or private part of the top scope. Basically if the stack looks like this: @ scope A: x -> Q.B.A.x scope B: y -> Q.B.y scope Q: .. @ then after popping it will look like @ scope B: A.x -> Q.B.A.x y -> Q.B.y scope Q: .. @ -} popScope :: Access -> ScopeM () popScope acc = do modifyScopeStack $ \(s0:s1:ss) -> mergeScope s1 (setScopeAccess acc $ mapScope_ (qual s0) (qual s0) $ noPrivate s0) : ss where qual s m = Map.mapKeys (qual' (mnameToList $ scopeName s)) m where qual' xs x = foldr C.Qual x $ map nameConcrete xs noPrivate s = s { scopePrivate = emptyNameSpace } -- | Pop the top scope from the stack and discard its contents. popScope_ :: ScopeM () popScope_ = modifyScopeStack tail -- | Returns a scope containing everything starting with a particular module -- name. Used to open a module. matchPrefix :: C.QName -> ScopeM Scope matchPrefix m = filterScope (isPrefix m) (isPrefix m) . mergeScopes . scopeStack <$> getScope where isPrefix _ (C.QName _ ) = False isPrefix (C.QName m) (C.Qual m' x) = m == m' isPrefix (C.Qual m m2) (C.Qual m' x) = m == m' && isPrefix m2 x -- | @renamedCanonicalNames old new s@ returns a renaming replacing all -- (abstract) names @old.m.x@ with @new.m.x@. Any other names are left -- untouched. renamedCanonicalNames :: ModuleName -> ModuleName -> Scope -> ScopeM (Map A.QName A.QName, Map A.ModuleName A.ModuleName) renamedCanonicalNames old new s = (,) <$> renamedNames names <*> renamedMods mods where ns = scopePublic $ setScopeAccess PublicAccess s names = nsNames ns mods = nsModules ns renamedNames ds = Map.fromList <$> zip xs <$> mapM renName xs where xs = filter (`isInModule` old) $ map anameName $ concat $ Map.elems ds renamedMods ms = Map.fromList <$> zip xs <$> mapM renMod xs where xs = filter (`isSubModuleOf` old) $ map amodName $ concat $ Map.elems ms -- Change a binding M.x -> old.M'.y to M.x -> new.M'.y renName :: A.QName -> ScopeM A.QName renName y = do i <- fresh return . qualifyQ new . dequalify $ y { qnameName = (qnameName y) { nameId = i } } where dequalify = A.qnameFromList . drop (size old) . A.qnameToList -- Change a binding M.x -> old.M'.y to M.x -> new.M'.y renMod :: A.ModuleName -> ScopeM A.ModuleName renMod = return . qualifyM new . dequalify where dequalify = A.mnameFromList . drop (size old) . A.mnameToList -- | Apply an importdirective and check that all the names mentioned actually -- exist. applyImportDirectiveM :: C.QName -> ImportDirective -> Scope -> ScopeM Scope applyImportDirectiveM m dir scope = do xs <- filterM doesn'tExist names reportSLn "scope.import.apply" 20 $ "non existing names: " ++ show xs case xs of [] -> return $ applyImportDirective dir scope _ -> typeError $ ModuleDoesntExport m xs where names :: [ImportedName] names = map fst (renaming dir) ++ case usingOrHiding dir of Using xs -> xs Hiding xs -> xs doesn'tExist (ImportedName x) = case Map.lookup (C.QName x) $ allNamesInScope scope of Just _ -> return False Nothing -> return True doesn'tExist (ImportedModule x) = case Map.lookup (C.QName x) $ allModulesInScope scope of Just _ -> return False Nothing -> return True -- | Open a module. Assumes that all preconditions have been checked, i.e. that -- the module is not opened into a different context than it was defined. openModule_ :: C.QName -> ImportDirective -> ScopeM () openModule_ m dir = addScope . setScopeAccess acc =<< applyImportDirectiveM m dir . unqualifyScope m =<< matchPrefix m where addScope s | not (publicOpen dir) = modifyTopScope (`mergeScope` s) | otherwise = do -- In case of a public open we check that there are no -- clashes with previously defined names. pub0 <- scopePublic . head . scopeStack <$> getScope let pub1 = scopePublic s [def0, def1] = map (Map.keys . nsNames) [pub0, pub1] [mod0, mod1] = map (Map.keys . nsModules) [pub0, pub1] -- Clashing definitions? case intersect def0 def1 of [] -> return () x : _ -> case Map.lookup x (nsNames pub0) of Just [q] -> typeError $ ClashingDefinition x (anameName q) _ -> __IMPOSSIBLE__ -- Clashing modules? case intersect mod0 mod1 of [] -> return () x : _ -> case (Map.lookup x (nsModules pub0), Map.lookup x (nsModules pub1)) of (Just [q0], Just [q1]) -> typeError $ ClashingModule (amodName q0) (amodName q1) _ -> __IMPOSSIBLE__ -- All is well. Merge. modifyTopScope (`mergeScope` s) acc | publicOpen dir = PublicAccess | otherwise = PrivateAccess