packages feed

Eq-1.1: EqManips/EvaluationContext.hs

module EqManips.EvaluationContext( EqTransformInfo( .. )
                                 , EqContext
                                 , performTransformation 
                                 , performTransformationWithContext
                                 , performLastTransformation 
                                 , performLastTransformationWithContext 
                                 , obtainEqResult 
                                 , cleanErrorList 
                                 , addSymbols 
                                 , addSymbol, delSymbol, updateSymbol 
                                 , eqFail, eqPrimFail 
                                 , symbolLookup
                                 , pushContext, popContext, setContext 
                                 , contextStackSize 
#ifdef _DEBUG
                                 , addTrace
                                 , printTrace
                                 , traceContext 
#endif /* _DEBUG */
                                 , emptyContext
                                 ) where

import Data.Map (Map)
import Control.Applicative
import qualified Data.Map as Map

import EqManips.Types
import EqManips.Algorithm.Utils

#ifdef _DEBUG
import System.IO
import qualified EqManips.Renderer.RenderConf as RenderConf

import {-# SOURCE #-} EqManips.Renderer.Ascii( formatFormula )
import {-# SOURCE #-} EqManips.Renderer.Sexpr
#endif /* _DEBUG */

-- | The real context info.
data EqTransformInfo = EqTransformInfo {
        -- | Well, here context mean more "symbol table"
        -- associate some variable with a definition.
          context    :: Map String (Formula ListForm)
        -- | A context "stack" used to handle some scoping
        -- which can be used to evaluate some sums.
        , contextStack :: [Map String (Formula ListForm)]

        -- | Depth of the context stack. Used to limit
        -- recursion in the monad.
        , contextDepth :: !Int

        -- | Some constraints put on variables
        , assertions :: Map String FormulaPrim

        -- | List of errors encountered when
        -- transforming formula
        , errorList  :: [(Formula TreeForm,String)]

        -- | The result of the formula computation
        , result :: Formula ListForm

#ifdef _DEBUG
        -- | Used for debugging, can print everything
        , trace :: [(String, Formula TreeForm)]
#endif /* _DEBUG */
    }

-- | Here we go, our evaluation monad.
-- It's basically a State monad, but providing
-- more services usefull to the software
data EqContext a = EqContext {
        runEqTransform :: EqTransformInfo -> (EqTransformInfo, a)
    }

instance Functor EqContext where
    {-# INLINE fmap #-}
    fmap f m = EqContext $ \c ->
        let (c', a) = runEqTransform m c
        in (c', f a)

instance Applicative EqContext where
    {-# INLINE pure #-}
    pure a = EqContext $ \c -> (c,a)

    {-# INLINE (<*>) #-}
    (EqContext ff) <*> (EqContext a) = EqContext $ \c ->
        let (c' , f) = ff c
            (c'', a') = a c'
        in (c'', f a')

instance Monad EqContext where
    {-# INLINE return #-}
    return a = EqContext $ \c -> (c, a)

    {-# INLINE (>>=) #-}
    prev >>= k = EqContext $ \c -> 
        let (c', a) = runEqTransform prev c
        in runEqTransform (k a) c'

-- | A basic initial context
emptyContext :: EqTransformInfo 
emptyContext = EqTransformInfo {
        context = Map.empty
      , contextStack = []
      , contextDepth = 0
      , assertions = Map.empty
      , errorList = []
      , result = Formula $ Block 0 0 0
#ifdef _DEBUG
      , trace = []
#endif /* _DEBUG */
    }

#ifdef _DEBUG
-- | Function used to add a trace in debug.
-- don't forget to surround it's use by #ifdef _DEBUG/#endif
addTrace :: (String, Formula TreeForm) -> EqContext ()
addTrace newTrace = EqContext $ \c ->
    (c { trace = newTrace : trace c }, ())

-- | Print all the trace found.
printTrace :: Handle -> EqTransformInfo -> IO ()
printTrace f inf = mapM_ showIt . reverse $ trace inf
    where showIt (str, formula) = do
              hPutStrLn f "=========================================="
              hPutStrLn f str
              hPutStrLn f $ sexprRender formula
              hPutStrLn f $ formatFormula RenderConf.defaultRenderConf
                                          formula

traceContext :: EqContext ()
traceContext = EqContext $ \c ->
    let contextes = unlines 
                  . map (\a -> printContext a ++ "\n/////////////////////////////////////////////////\n") 
                  . map Map.toList
                  $ contextStack c
        printContext var = concat $ map (\(a,f) -> a ++ " =\n" 
                                                ++ formatFormula RenderConf.defaultRenderConf
                                                                 (treeIfyFormula f)
                                                ++ "\n")
                                        var
    in
    ( c { trace = ("ContextStack | " ++ contextes, Formula $ Variable "")
                : ("Context | " ++ (show $ context c), Formula $ Variable "") : trace c }
    , ()
    )
#endif /* _DEBUG */

-- | Keep a track of current context, keep previous context clean
pushContext :: EqContext ()
pushContext = EqContext $ \c ->
    (c { contextStack = context c : contextStack c
       , contextDepth = contextDepth c + 1
       }
    , ())

-- | Discard the current deep context and restore the one
-- which was previously "pushed" by pushContext. If no
-- context was there, an empty one is put in place
popContext :: EqContext ()
popContext = EqContext $ \c ->
    let safeHeadTail (x:xs) = (x, xs)
        safeHeadTail     [] = (Map.empty, [])
        (oldContext, stack) = safeHeadTail $ contextStack c
    in
    (c { contextStack = stack
       , context = oldContext
       , contextDepth = contextDepth c - 1
       }
    , ())

setContext :: [(String, Formula ListForm)] -> EqContext ()
setContext newContext = EqContext $ \c ->
    (c { context = Map.fromList newContext }, ())

-- | Cleanup error list, useful in cases of
-- threaded computation
cleanErrorList :: EqContext ()
cleanErrorList = EqContext $ \c -> (c { errorList = [] }, ())

type FormulaForm = ListForm

-- | Public function of the API to retrieve the result of
-- a formula transformation. The type is opaque otherwise.
performTransformation :: EqContext (Formula FormulaForm) -> EqTransformInfo
performTransformation = performTransformationWithContext Map.empty

-- | Evaluate a formula, you can provide variable bindings
performTransformationWithContext :: Map String (Formula ListForm)
                                 -> EqContext (Formula ListForm)
								 -> EqTransformInfo
performTransformationWithContext base m = ctxt { result = formula }
    where (ctxt, formula) = runEqTransform m $ emptyContext { context = base }

-- | Evaluate a programm, with no pre-definitions
performLastTransformation :: EqContext [Formula FormulaForm] -> EqTransformInfo
performLastTransformation =
	performLastTransformationWithContext Map.empty

-- | Run a programm and get the last statement.
-- You can run programm with your pre-defined symbols
performLastTransformationWithContext :: Map String (Formula ListForm)
                                     -> EqContext [Formula FormulaForm]
									 -> EqTransformInfo
performLastTransformationWithContext c m = ctxt { result = last formula }
    where (ctxt, formula) = runEqTransform m $ emptyContext { context = c }

obtainEqResult :: EqContext a -> a
obtainEqResult m = snd $ runEqTransform m emptyContext

-- | Remove a variable from the context
delSymbol :: String -> EqContext ()
delSymbol s = EqContext $ \ctxt ->
    (ctxt { context = Map.delete s $ context ctxt}, ())

updateSymbol :: String -> Formula ListForm -> EqContext ()
updateSymbol varName def = do
    delSymbol varName
    addSymbol varName def

addSymbols :: [(String, Formula ListForm)] -> EqContext ()
addSymbols adds = EqContext $ \eqCtxt ->
    let syms = context eqCtxt
    in -- union is left biased, we use it here, new symbols
       -- at the left of union !!
    ( eqCtxt { context = Map.fromList adds `Map.union` syms}, ())

-- | Add a variable into the context
addSymbol :: String -> Formula ListForm -> EqContext ()
addSymbol varName def = EqContext $ \eqCtxt ->
    let prevSymbol = context eqCtxt
    in ( eqCtxt{ context = Map.insert varName def prevSymbol }, ())

contextStackSize :: EqContext Int
contextStackSize = EqContext $ \eqCtxt ->
    (eqCtxt, contextDepth eqCtxt)

-- | Check if a symbol is present, and if so, return it's
-- definition
symbolLookup :: String -> EqContext (Maybe (Formula ListForm))
symbolLookup varName = EqContext $ \eqCtxt ->
    (eqCtxt, Map.lookup varName $ context eqCtxt)

-- | Used to provide error messages at the end of the computation
-- (when jumping back to IO), and also assure a nice partial evaluation,
-- by replacing the faulty formula by a block.
eqFail :: Formula TreeForm -> String -> EqContext (Formula a)
eqFail formula errorText = EqContext $ \eqCtxt ->
    let prevErr = errorList eqCtxt
    in ( eqCtxt {errorList = (formula, errorText):prevErr}, Formula $ Block 1 1 1)

-- | Little helper to be able to use eqFail easily when
-- manipulating FormulaPrim formula. Assume that FormulaPrim
-- is in List Form. Use eqFail otherwise.
eqPrimFail :: FormulaPrim -> String -> EqContext FormulaPrim
eqPrimFail f s = unTagFormula `fmap` eqFail (treeIfyFormula $ Formula f) s