packages feed

dao-0.1.0.1: src/Dao/Stack.hs

-- "src/Dao/Stack.hs"  provides the stack mechanism used for storing
-- local variables during evaluation.
-- 
-- Copyright (C) 2008-2014  Ramin Honary.
-- This file is part of the Dao System.
--
-- The Dao System is free software: you can redistribute it and/or
-- modify it under the terms of the GNU General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
-- 
-- The Dao System is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- 
-- You should have received a copy of the GNU General Public License
-- along with this program (see the file called "LICENSE"). If not, see
-- <http://www.gnu.org/licenses/agpl.html>.

module Dao.Stack where

import qualified Data.Map as M

import           Control.Monad
import           Control.Monad.Identity

newtype Stack key val = Stack { mapList :: [M.Map key val] } deriving Show

emptyStack :: Stack key val
emptyStack = Stack []

stackLookup :: Ord key => key -> Stack key val -> Maybe val
stackLookup key stack = foldl (\f -> mplus f . (M.lookup key)) Nothing (mapList stack)

-- | Always update in the top of the stack, regardless of whether the key being updated has been
-- defined at some lower level in the stack. If the stack is empty, the update function is evaluated
-- with 'Prelude.Nothing' and the result @a@ is returned, but the updated @val@ is disgarded.
stackUpdateTopM
  :: (Monad m, Ord key)
  => (Maybe val -> m (a, Maybe val)) -> key -> Stack key val -> m (a, Stack key val)
stackUpdateTopM updVal key (Stack stack) = case stack of
  []      -> updVal Nothing >>= \ (a, _) -> return (a, Stack [])
  s:stack -> updVal (M.lookup key s) >>= \ (a, o) -> return (a, Stack $ M.alter (const o) key s : stack)

-- | If the key does not exist, the update will occur in the top level of the stack. If the key does
-- exist, regardless of whether the key exists in the top or in some lower level, the value at that
-- key will be updated in the level in which it is defined. If the stack is empty, the update
-- function is evaluated with 'Prelude.Nothing' and the result @a@ is returned, but the updated
-- @val@ is disgarded.
stackUpdateM
  :: (Monad m, Ord key)
  => (Maybe val -> m (a, Maybe val)) -> key -> Stack key val -> m (a, Stack key val)
stackUpdateM updVal key (Stack stack) = loop [] stack where
  loop rx stack = case stack of
    []      -> atTop (reverse rx)
    s:stack -> case M.lookup key s of
      Just  o -> updVal (Just o) >>= \ (a, o) ->
        return (a, Stack $ reverse rx ++ M.alter (const o) key s : stack)
      Nothing -> loop (s:rx) stack
  atTop stack = case stack of
    []      -> updVal Nothing >>= \ (a, _) -> return (a, Stack [])
    s:stack -> updVal Nothing >>= \ (a, o) -> return (a, Stack $ M.alter (const o) key s : stack)

stackUpdate
  :: Ord key
  => (Maybe val -> (a, Maybe val))
  -> key -> Stack key val -> (a, Stack key val)
stackUpdate upd key = runIdentity . stackUpdateM (return . upd) key

stackUpdateTop
  :: Ord key
  => (Maybe val -> (a, Maybe val))
  -> key -> Stack key val -> (a, Stack key val)
stackUpdateTop upd key = runIdentity . stackUpdateTopM (return . upd) key

-- | Define or undefine a value at an address on the top tree in the stack.
stackDefine :: Ord key => key -> Maybe val -> Stack key val -> Stack key val
stackDefine key val = snd . stackUpdate (const ((), val)) key

stackPush :: Ord key => M.Map key val -> Stack key val -> Stack key val
stackPush init stack = stack{ mapList = init : mapList stack }

stackPop :: Ord key => Stack key val -> (Stack key val, M.Map key val)
stackPop stack =
  let mx = mapList stack
  in  if null mx then (stack, M.empty) else (stack{mapList=tail mx}, head mx)