Agda-2.2.0: src/full/Agda/Syntax/Scope/Monad.hs
{-# 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