packages feed

curry-frontend-0.2.3: src/ScopeEnv.hs

-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- 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 qualified Data.Map as Map
import Prelude hiding (lookup)

-------------------------------------------------------------------------------
-------------------------------------------------------------------------------

-- Data type for representing information in nested scopes.
data ScopeEnv a b = ScopeEnv Int (Map.Map a (b,Int)) [Map.Map a (b,Int)]
		    deriving Show


-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Returns an empty scope environment
new :: Ord a => ScopeEnv a b
new = ScopeEnv 0 Map.empty []


-- 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 = Map.insert 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') ->  Map.insert key (val,lev') local)
			     (Map.lookup 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') -> Map.insert key (fun val', lev') local)
	    (Map.lookup 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) (Map.lookup 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 (Map.lookup 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) (Map.lookup 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 (Map.toList top)) []
endScopeUp (ScopeEnv lev top (local:local':locals))
   = ScopeEnv (lev - 1) 
              top 
	      ((foldr (updateSE local) local' (Map.toList 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)) (Map.toList 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) (Map.toList local))


-- Returns the current level
currentLevel :: Ord a => ScopeEnv a b -> Int
currentLevel env = selectSE const env


-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Privates...

--
modifySE :: (Int -> Map.Map a (b,Int) -> Map.Map 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 -> Map.Map 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 => Map.Map a (b,Int) -> (a,(b,Int)) ->  Map.Map a (b,Int) 
          -> Map.Map a (b,Int)
updateSE local (key,(_,lev)) local'
   = maybe local' 
           (\ (val',lev') 
	    -> if lev == lev' then Map.insert key (val',lev) local' 
                              else local')
	   (Map.lookup key local)



-------------------------------------------------------------------------------
-------------------------------------------------------------------------------