{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
module CHR.Data.Lookup.Scoped
( Scoped(..)
, DefaultScpsLkup
, defaultScpsLkup
)
where
import Control.Arrow
import Control.Monad.State
import CHR.Data.Lookup.Types hiding (empty)
import qualified CHR.Data.Lookup.Types as Lkup
import CHR.Pretty hiding (empty)
import CHR.Data.Lens as L
import Prelude hiding (lookup, null, map)
import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Unboxed.Mutable as MV
type ScpId = Int
data Scopes
= Scopes
{ _scopesVec :: UV.Vector ScpId
, _scopesFree :: ScpId
, _scopesCur :: ScpId
}
instance Show Scopes where
show (Scopes v _ _) = show v
instance PP Scopes where
pp = pp . show
data ScopedItem v
= ScopedItem
{ _scopeditemId :: ScpId
, _scopeditemVal :: v
}
deriving Show
instance PP v => PP (ScopedItem v) where
pp (ScopedItem i v) = v >|< ppParens i
data ScpsLkup lkup scps
= ScpsLkup
{ _scpslkupBase :: lkup
, _scpslkupScopes :: scps
}
deriving Show
instance (PP lkup, PP scps) => PP (ScpsLkup lkup scps) where
pp (ScpsLkup lkup scps) = lkup >-< scps
mkLabel ''ScopedItem
mkLabel ''Scopes
mkLabel ''ScpsLkup
scpEnsure :: Int -> Scopes -> Scopes
scpEnsure free s@(Scopes {_scopesVec=v, _scopesFree=f})
| free >= f - UV.length v = s
| otherwise = s {_scopesVec = v UV.++ UV.replicate (free `max` ((3 * UV.length v) `div` 2)) 0}
{-# INLINE scpEnsure #-}
scpAlloc :: Scopes -> (ScpId, Scopes)
scpAlloc s@(Scopes {_scopesVec=v, _scopesFree=f, _scopesCur=c}) = (f, s {_scopesFree = f+1, _scopesVec = UV.modify (\v -> MV.write v f c) v, _scopesCur = f})
{-# INLINE scpAlloc #-}
class Scoped c where
empty :: c
new :: c -> (ScpId,c)
pop :: c -> (ScpId,c)
switch :: ScpId -> c -> (ScpId,c)
scope :: c -> ScpId
curIsVisibleFrom :: ScpId -> c -> Bool
newM :: (MonadState c m) => m ScpId
popM :: (MonadState c m) => m ScpId
switchM :: (MonadState c m) => ScpId -> m ScpId
scopeM :: (MonadState c m) => m ScpId
curIsVisibleFromM :: (MonadState c m) => ScpId -> m Bool
newM = state new
new = runState newM
popM = state pop
pop = runState popM
switchM = state . switch
switch = runState . switchM
scopeM = gets scope
scope = evalState scopeM
curIsVisibleFromM = gets . curIsVisibleFrom
curIsVisibleFrom = evalState . curIsVisibleFromM
instance Scoped Scopes where
empty = Scopes (UV.replicate 3 0) 1 0
new = scpAlloc . scpEnsure 1
switch i s = (_scopesCur s, s {_scopesCur = i})
scope = _scopesCur
pop s@(Scopes {_scopesVec=v, _scopesCur=c})
= (c, s {_scopesCur = v UV.! c})
curIsVisibleFrom i s@(Scopes {_scopesVec=v, _scopesCur=c})
| i == c = True
| i == 0 = False
| otherwise = curIsVisibleFrom (v UV.! i) s
type DefaultScpsLkup k v = ScpsLkup (Map.Map k (ScopedItem v)) Scopes
defaultScpsLkup :: DefaultScpsLkup k v
defaultScpsLkup = ScpsLkup Map.empty empty
whenInScps :: Scoped c => c -> ScpId -> v -> Maybe v
whenInScps scps sc v = if curIsVisibleFrom sc scps then return v else Nothing
instance (Scoped scps, Lookup lkup k v) => Scoped (ScpsLkup lkup scps) where
empty = ScpsLkup Lkup.empty empty
newM = modL scpslkupScopes new
popM = modL scpslkupScopes pop
switchM = modL scpslkupScopes . switch
scope = scope . getL scpslkupScopes
curIsVisibleFrom i = curIsVisibleFrom i . getL scpslkupScopes
instance (Scoped scps, Lookup lkup k (ScopedItem v)) => Lookup (ScpsLkup lkup scps) k v where
lookup k (ScpsLkup lkup scps) = do
ScopedItem sc v <- lookup k lkup
whenInScps scps sc v
alter f k (ScpsLkup lkup scps) =
ScpsLkup (alter (maybe ((ScopedItem $ scope scps) <$> f Nothing)
(\(ScopedItem sc v) -> ScopedItem sc <$> f (Just v))
)
k lkup
)
scps
null l@(ScpsLkup lkup scps) = null lkup || List.null (toList l)
size l@(ScpsLkup lkup _) = size lkup
toList (ScpsLkup lkup scps) = [ (k,v) | (k, ScopedItem sc v) <- toList lkup, curIsVisibleFrom sc scps ]
fromList l = (ScpsLkup (fromList $ List.map (\(k,v) -> (k, ScopedItem (scope s) v)) l) s)
where s = empty