-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- ScopeEnv - provides functions and data types for dealing with nested
-- scope environments to store data from nested scopes
--
-- This module should be imported using "import qualified" to avoid name
-- clashes
--
-- November 2005,
-- Martin Engelke (men@informatik.uni-kiel.de)
--
module ScopeEnv (ScopeEnv,
new, insert, update, modify, lookup, sureLookup,
level, exists, beginScope, endScope, endScopeUp,
toList, toLevelList, currentLevel) where
import Env
import Prelude hiding (lookup)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Returns an empty scope environment
new :: Ord a => ScopeEnv a b
new = ScopeEnv 0 emptyEnv []
-- Inserts a value under a key into the environment of the current scope
insert :: Ord a => a -> b -> ScopeEnv a b -> ScopeEnv a b
insert key val env = modifySE insertLev env
where
insertLev lev local = bindEnv key (val,lev) local
-- Updates the value stored under an existing key in the environment of
-- the current scope
update :: Ord a => a -> b -> ScopeEnv a b -> ScopeEnv a b
update key val env = modifySE updateLev env
where
updateLev lev local = maybe local
(\ (_,lev') -> bindEnv key (val,lev') local)
(lookupEnv key local)
-- Modifies the value of an existing key by applying the function 'fun'
-- in the environment of the current scope
modify :: Ord a => (b -> b) -> a -> ScopeEnv a b -> ScopeEnv a b
modify fun key env = modifySE modifyLev env
where
modifyLev lev local
= maybe local
(\ (val',lev') -> bindEnv key (fun val', lev') local)
(lookupEnv key local)
-- Looks up the value which is stored under a key from the environment of
-- the current scope
lookup :: Ord a => a -> ScopeEnv a b -> Maybe b
lookup key env = selectSE lookupLev env
where
lookupLev lev local = maybe Nothing (Just . fst) (lookupEnv key local)
-- Similar to 'lookup', but returns an alternative value, if the key
-- doesn't exist in the environment of the current scope
sureLookup :: Ord a => a -> b -> ScopeEnv a b -> b
sureLookup key alt env = maybe alt id (lookup key env)
-- Returns the level of the last insertion of a key
level :: Ord a => a -> ScopeEnv a b -> Int
level key env = selectSE levelLev env
where
levelLev lev local = maybe (-1) snd (lookupEnv key local)
-- Checks, whether a key exists in the environment of the current scope
exists :: Ord a => a -> ScopeEnv a b -> Bool
exists key env = selectSE existsLev env
where
existsLev lev local = maybe False (const True) (lookupEnv key local)
-- Switches to the next scope (i.e. pushes the environment of the current
-- scope onto the top of an scope stack and increments the level counter)
beginScope :: Ord a => ScopeEnv a b -> ScopeEnv a b
beginScope (ScopeEnv lev top [])
= ScopeEnv (lev + 1) top [top]
beginScope (ScopeEnv lev top (local:locals))
= ScopeEnv (lev + 1) top (local:local:locals)
-- Switches to the previous scope (i.e. pops the environment from the top
-- of the scope stack and decrements the level counter)
endScope :: Ord a => ScopeEnv a b -> ScopeEnv a b
endScope (ScopeEnv _ top [])
= ScopeEnv 0 top []
endScope (ScopeEnv lev top (_:locals))
= ScopeEnv (lev - 1) top locals
-- Behaves like 'endScope' but additionally updates the environment of
-- the previous scope by updating all keys with the corresponding values
-- from the poped environment
endScopeUp :: Ord a => ScopeEnv a b -> ScopeEnv a b
endScopeUp (ScopeEnv _ top [])
= ScopeEnv 0 top []
endScopeUp (ScopeEnv lev top (local:[]))
= ScopeEnv 0 (foldr (updateSE local) top (envToList top)) []
endScopeUp (ScopeEnv lev top (local:local':locals))
= ScopeEnv (lev - 1)
top
((foldr (updateSE local) local' (envToList local')):locals)
-- Returns the environment of current scope as a (key,value) list
toList :: Ord a => ScopeEnv a b -> [(a,b)]
toList env = selectSE toListLev env
where
toListLev lev local = map (\ (key,(val,_)) -> (key,val)) (envToList local)
-- Returns all (key,value) pairs from the environment of the current scope
-- which has been inserted in the current level
toLevelList :: Ord a => ScopeEnv a b -> [(a,b)]
toLevelList env = selectSE toLevelListLev env
where
toLevelListLev lev local
= map (\ (key,(val,_)) -> (key,val))
(filter (\ (_,(_,lev')) -> lev' == lev) (envToList local))
-- Returns the current level
currentLevel :: Ord a => ScopeEnv a b -> Int
currentLevel env = selectSE const env
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Privates...
--
modifySE :: (Int -> Env a (b,Int) -> Env a (b,Int)) -> ScopeEnv a b
-> ScopeEnv a b
modifySE f (ScopeEnv _ top [])
= ScopeEnv 0 (f 0 top) []
modifySE f (ScopeEnv lev top (local:locals))
= ScopeEnv lev top ((f lev local):locals)
--
selectSE :: (Int -> Env a (b,Int) -> c) -> ScopeEnv a b -> c
selectSE f (ScopeEnv _ top []) = f 0 top
selectSE f (ScopeEnv lev _ (local:_)) = f lev local
--
updateSE :: Ord a => Env a (b,Int) -> (a,(b,Int)) -> Env a (b,Int)
-> Env a (b,Int)
updateSE local (key,(_,lev)) local'
= maybe local'
(\ (val',lev')
-> if lev == lev' then bindEnv key (val',lev) local'
else local')
(lookupEnv key local)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Data type for representing information in nested scopes.
data ScopeEnv a b = ScopeEnv Int (Env a (b,Int)) [Env a (b,Int)]
deriving Show
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------