{-# LANGUAGE PatternGuards #-}

-- | A Ninja style environment, equivalent to a non-empty list of mutable hash tables.
module Development.Ninja.Env(
    Env, newEnv, scopeEnv, addEnv, askEnv, fromEnv
    ) where

import qualified Data.HashMap.Strict as Map
import Data.Hashable
import Data.IORef


data Env k v = Env (IORef (Map.HashMap k v)) (Maybe (Env k v))

instance Show (Env k v) where show :: Env k v -> String
show Env k v
_ = String
"Env"


newEnv :: IO (Env k v)
newEnv :: IO (Env k v)
newEnv = do IORef (HashMap k v)
ref <- HashMap k v -> IO (IORef (HashMap k v))
forall a. a -> IO (IORef a)
newIORef HashMap k v
forall k v. HashMap k v
Map.empty; Env k v -> IO (Env k v)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Env k v -> IO (Env k v)) -> Env k v -> IO (Env k v)
forall a b. (a -> b) -> a -> b
$ IORef (HashMap k v) -> Maybe (Env k v) -> Env k v
forall k v. IORef (HashMap k v) -> Maybe (Env k v) -> Env k v
Env IORef (HashMap k v)
ref Maybe (Env k v)
forall a. Maybe a
Nothing


scopeEnv :: Env k v -> IO (Env k v)
scopeEnv :: Env k v -> IO (Env k v)
scopeEnv Env k v
e = do IORef (HashMap k v)
ref <- HashMap k v -> IO (IORef (HashMap k v))
forall a. a -> IO (IORef a)
newIORef HashMap k v
forall k v. HashMap k v
Map.empty; Env k v -> IO (Env k v)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Env k v -> IO (Env k v)) -> Env k v -> IO (Env k v)
forall a b. (a -> b) -> a -> b
$ IORef (HashMap k v) -> Maybe (Env k v) -> Env k v
forall k v. IORef (HashMap k v) -> Maybe (Env k v) -> Env k v
Env IORef (HashMap k v)
ref (Maybe (Env k v) -> Env k v) -> Maybe (Env k v) -> Env k v
forall a b. (a -> b) -> a -> b
$ Env k v -> Maybe (Env k v)
forall a. a -> Maybe a
Just Env k v
e


addEnv :: (Eq k, Hashable k) => Env k v -> k -> v -> IO ()
addEnv :: Env k v -> k -> v -> IO ()
addEnv (Env IORef (HashMap k v)
ref Maybe (Env k v)
_) k
k v
v = IORef (HashMap k v) -> (HashMap k v -> HashMap k v) -> IO ()
forall a. IORef a -> (a -> a) -> IO ()
modifyIORef IORef (HashMap k v)
ref ((HashMap k v -> HashMap k v) -> IO ())
-> (HashMap k v -> HashMap k v) -> IO ()
forall a b. (a -> b) -> a -> b
$ k -> v -> HashMap k v -> HashMap k v
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
Map.insert k
k v
v


askEnv :: (Eq k, Hashable k) => Env k v -> k -> IO (Maybe v)
askEnv :: Env k v -> k -> IO (Maybe v)
askEnv (Env IORef (HashMap k v)
ref Maybe (Env k v)
e) k
k = do
    HashMap k v
mp <- IORef (HashMap k v) -> IO (HashMap k v)
forall a. IORef a -> IO a
readIORef IORef (HashMap k v)
ref
    case k -> HashMap k v -> Maybe v
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
Map.lookup k
k HashMap k v
mp of
        Just v
v -> Maybe v -> IO (Maybe v)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe v -> IO (Maybe v)) -> Maybe v -> IO (Maybe v)
forall a b. (a -> b) -> a -> b
$ v -> Maybe v
forall a. a -> Maybe a
Just v
v
        Maybe v
Nothing | Just Env k v
e <- Maybe (Env k v)
e -> Env k v -> k -> IO (Maybe v)
forall k v. (Eq k, Hashable k) => Env k v -> k -> IO (Maybe v)
askEnv Env k v
e k
k
        Maybe v
_ -> Maybe v -> IO (Maybe v)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe v
forall a. Maybe a
Nothing

fromEnv :: Env k v -> IO (Map.HashMap k v)
fromEnv :: Env k v -> IO (HashMap k v)
fromEnv (Env IORef (HashMap k v)
ref Maybe (Env k v)
_) = IORef (HashMap k v) -> IO (HashMap k v)
forall a. IORef a -> IO a
readIORef IORef (HashMap k v)
ref