--  Compiler Toolkit: compiler state management basics
--
--  Author : Manuel M. T. Chakravarty
--  Created: 7 November 97
--
--  Copyright (C) [1997..1999] Manuel M. T. Chakravarty
--
--  This file 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 2 of the License, or
--  (at your option) any later version.
--
--  This file 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.
--
--- DESCRIPTION ---------------------------------------------------------------
--
--  This module provides basic types and services used to realize the state
--  management of the compiler.
--
--- DOCU ----------------------------------------------------------------------
--
--  language: Haskell 98
--
--  * The monad `PreCST' is an instance of `STB' where the base state is fixed.
--    However, the base state itself is parametrized by an extra state
--    component that can be instantiated by the compiler that uses the toolkit
--    (to store information like compiler switches) -- this is the reason for
--    adding the prefix `Pre'.
--
--  * The module exports the details of the `BaseState' etc as they have to be
--    know by `State'.  The latter ensures the necessary abstraction for
--    modules that do not belong to the state management.
--
--  * Due to this module, the state management modules can share internal
--    information about the data types hidden to the rest of the system.
--
--  * The following state components are maintained:
--
--    + errorsBS (type `ErrorState')    -- keeps track of raised errors
--    + namesBS (type `NameSupply')     -- provides unique names
--    + extraBS (generic type)          -- extra compiler-dependent state
--                                         information, e.g., for compiler
--                                         switches
--
--- TODO ----------------------------------------------------------------------
--

module Control.StateBase (PreCST(..), ErrorState(..), BaseState(..),
                  unpackCST, readCST, writeCST, transCST, liftIO)
where
import Control.StateTrans (STB, readGeneric, writeGeneric, transGeneric)
import qualified Control.StateTrans as StateTrans (liftIO)
import Data.Errors     (ErrorLevel(..), Error)
import Language.C.Data.Name


-- state used in the whole compiler
-- --------------------------------

-- | form of the error state
--
-- * when no error was raised yet, the error level is the lowest possible one
--
data ErrorState = ErrorState ErrorLevel    -- worst error level that was raised
                             Int         -- number of errors (excl warnings)
                             [Error]     -- already raised errors

-- | base state
--
data BaseState e = BaseState {
                     errorsBS   :: ErrorState,
                     supplyBS   :: [Name],                    -- unique names
                     extraBS    :: e                          -- extra state
                 }

-- | the compiler state transformer
--

newtype PreCST e s a = CST (STB (BaseState e) s a)

instance Monad (PreCST e s) where
  return = yield
  (>>=)  = (+>=)


-- | unwrapper coercion function
--
unpackCST   :: PreCST e s a -> STB (BaseState e) s a
unpackCST m  = let CST m' = m in m'


-- monad operations
-- ----------------

-- | the monad's unit
--
yield   :: a -> PreCST e s a
yield a  = CST $ return a

-- | the monad's bind
--
(+>=)   :: PreCST e s a -> (a -> PreCST e s b) -> PreCST e s b
m +>= k  = CST $ unpackCST m >>= (\a -> unpackCST (k a))


-- generic state manipulation
-- --------------------------

-- | given a reader function for the state, wrap it into an CST monad
--
readCST   :: (s -> a) -> PreCST e s a
readCST f  = CST $ readGeneric f

-- | given a new state, inject it into an CST monad
--
writeCST    :: s -> PreCST e s ()
writeCST s'  = CST $ writeGeneric s'

-- | given a transformer function for the state, wrap it into an CST monad
--
transCST   :: (s -> (s, a)) -> PreCST e s a
transCST f  = CST $ transGeneric f

-- interaction with the encapsulated 'IO' monad
-- --------------------------------------------

-- | lifts an 'IO' state transformer into 'CST'
--
liftIO   :: IO a -> PreCST e s a
liftIO m  = CST $ (StateTrans.liftIO m)
