KiCS-debugger-0.1.0: biosphere/src/Curry/Debugger/DebugMonad.lhs
% DebugMonad.lhs
% Andreas Baldeau, Christoph Wulf, Bernd Brassel
% April 13, 2009
Overview
========
> {-# LANGUAGE FlexibleContexts #-}
> module Curry.Debugger.DebugMonad (
> module Curry.Debugger.DebugInfo,
> module Curry.Debugger.DebugMonad,
> module Curry.Debugger.Logic) where
Imports
=======
> import Control.Monad.State
> import Curry.Debugger.Oracle
> import Curry.Debugger.Logic
> import Curry.Debugger.DebugInfo
> import Data.Generics
> import Data.Maybe (fromJust)
> import qualified Data.Map as Map
Definitions
===========
`Hook`
------
> data Hook = FuncCallHook String -- function name
> | FuncDeclHook String -- function name
> | ConsHook ConsHookType
> | CaseHook
> | BranchHook
> | FreeHook
> | OrHook
> | LetHook
> deriving Show
`ConsHookType`
--------------
> data ConsHookType = Constructor
> | Literal
> | PartCall
> deriving Show
`ExceptionHook`
---------------
> data ExceptionHook = ErrorHook String -- Prelude.error
> | FailedHook -- Prelude.failed
> | NEPCaseHook DI -- non exhaustive patterns at case
> | NEPRulesHook DI -- non exhaustive patterns at functions
> deriving Show
`Func`
------
Type for representations of functions/constructors.
Contains a Term with function name and static information of the declaration
and the function itself.
> data (DM dm) => Func dm a b = FuncFail
> | FuncOr OrRef [Func dm a b]
> | FuncUnderscore
> | FuncRep Term (a -> dm b)
`GenTerm` instance for function representation: extracts the contained term.
> instance DM dm => GenTerm (Func dm a b) where
TODO: deprecated
> underscore = FuncUnderscore
> genTerm (FuncRep term _) = term
> genTerm x = genericTerm undefined x
> instance Data (Func dm a b)
> instance Typeable (Func dm a b)
Underscore
----------
Returns the representation of a not evaluated term for the result type, 3rd constructor.
> underscore :: GenTerm a => a
> underscore = let res = fromConstr $ indexConstr (dataTypeOf res) 3 in res
Logic
-----
Returns the representation of a failed calculation for the result type, 1st constructor.
> failed :: GenTerm a => a
> failed = let res = fromConstr $ indexConstr (dataTypeOf res) 1 in res
> (?) :: (GenTerm a,DM dm) => a -> a -> dm a
> x ? y = do
> ref <- newOrRef
> return (mkOr ref [x,y])
> mkOr :: GenTerm a => OrRef -> [a] -> a
> mkOr ref xs =
> let orWithUndefined = fromConstr $ indexConstr (dataTypeOf (head xs)) 2
> orWithArgs = gmapT (castArgs ref xs) orWithUndefined
> in orWithArgs
apply given function to all branches
> inline :: (DM m,GenTerm a, GenTerm a2) =>
> ConstraintStore -> OrRef -> (a -> m a2) -> a -> m a2
> inline cs r f x = do
> args <- mapM choose (zip [0..1] $ branches x)
> return (mkOr r args)
> where
> choose (i,x') = do
> setConstraintStore (insertBind cs r i)
> res <- f x'
> setConstraintStore cs
> return res
the or branches of a given term
> branches :: (Data a) => a -> [a]
> branches x = x !@ 1
> castArgs :: (Data a,Data b) => OrRef -> [a] -> b -> b
> castArgs ref xs _ = case cast ref of
> Just r' -> r'
> Nothing -> case cast xs of
> Just xs' -> xs'
> Nothing -> error "castArgs: not applied to argument of Or"
(!@) retrieves the ith argument of a given constructor
> (!@) :: (Data a,Typeable b) => a -> Int -> b
> x !@ i = fromJust (gmapQi i cast x)
the or reference of a given term
> orRef :: Data a => a -> OrRef
> orRef x = x !@ 0
the function called from generated code to treat remaining
cases
> treatCase :: (DM dm,GenTerm a,GenTerm b) => dm b -> (a -> dm b) -> a -> dm b
> treatCase failhook f x
> | constrIndex (toConstr x) <= 2 = treatCase' True f x
> | otherwise = failhook
> treatCase' count f x =
> let meval = if count then eval else id in
> case constrIndex (toConstr x) of
> 1 -> return failed
> 2 -> do
> let r = orRef x
> cs <- getConstraintStore
> case lookupRef cs r of
> Nothing -> meval $ inline cs r (treatCase' count f) x
> Just i -> treatCase' count f (branches x !! i)
> _ -> f x
> term s = Term s (SrcID "Prelude" 0)
Creates a term or a given static info and a constructor added by the transformation.
> genericTerm :: GenTerm a => StaticInfo -> a -> Term
> genericTerm si x = case constrIndex (toConstr x) of
> 1 -> TermFail si
> 2 -> TermOr si (orRef x) (map genTerm (branches x))
> 3 -> TermUnderscore si
> _ -> error "genericTerm: unexpected argument"
> peekType :: TypeRep -> [Int] -> TypeRep
> peekType tyRep indexes = peekType' (typeRepArgs tyRep) indexes
> where
> peekType' :: [TypeRep] -> [Int] -> TypeRep
> peekType' tyReps [i] = tyReps !! i
> peekType' tyReps (i:is) = peekType (tyReps !! i) is
Classes
=======
`OracleState`
-------------
> class OracleState om where
> getOracle :: om Oracle
> setOracle :: Oracle -> om ()
> getExt :: om [String]
> setExt :: [String] -> om ()
> newOrRef :: om OrRef
> getConstraintStore :: om ConstraintStore
> setConstraintStore :: ConstraintStore -> om ()
`DM`
----
> class (Monad dm,OracleState dm) => DM dm where
> hook :: GenTerm a => Hook -> DI -> dm a -> dm a
> hook _ _ ma = ma
> funcCallHook :: GenTerm a => String -> DI -> dm a -> dm a
> funcCallHook name = hook (FuncCallHook name)
> funcDeclHook :: GenTerm a => String -> DI -> dm a -> dm a
> funcDeclHook name = hook (FuncDeclHook name)
> consHook :: GenTerm a => ConsHookType -> DI -> dm a -> dm a
> consHook cht = hook (ConsHook cht)
> constructorHook :: GenTerm a => DI -> dm a -> dm a
> constructorHook = consHook Constructor
> litHook :: GenTerm a => DI -> dm a -> dm a
> litHook = consHook Literal
> partCallHook :: GenTerm a => DI -> dm a -> dm a
> partCallHook = consHook PartCall
> caseHook :: GenTerm a => DI -> dm a -> dm a
> caseHook = hook CaseHook
> branchHook :: GenTerm a => DI -> dm a -> dm a
> branchHook = hook BranchHook
> freeHook :: GenTerm a => DI -> dm a -> dm a
> freeHook = hook FreeHook
> orHook :: GenTerm a => DI -> dm a -> dm a
> orHook = hook OrHook
> letHook :: GenTerm a => DI -> dm a -> dm a
> letHook = hook LetHook
> exceptionHook :: GenTerm a => ExceptionHook -> dm a
> exceptionHook _ = return failed
> errorHook :: GenTerm a => String -> dm a
> errorHook s = exceptionHook (ErrorHook s)
> failedHook :: GenTerm a => dm a
> failedHook = exceptionHook FailedHook
> nepCaseHook :: GenTerm a => DI -> dm a
> nepCaseHook di = exceptionHook (NEPCaseHook di)
> nepRulesHook :: GenTerm a => DI -> dm a
> nepRulesHook di = exceptionHook (NEPRulesHook di)
Functions
=========
Acessing the oracle
-------------------
> popOracle :: DM dm => dm Bool
> popOracle = do
> o <- getOracle
> let (o', b) = pop o
> setOracle o'
> return b
> pushOracle :: DM dm => Bool -> dm ()
> pushOracle b = do
> o <- getOracle
> let o' = push o b
> setOracle o'
`eval`
------
> eval :: (GenTerm a, DM dm) => dm a -> dm a
> eval act = do
> needed <- popOracle
> if needed then act else return underscore
Accessing virtual I/O
---------------------
Extracts and returns the next ext value from the oracle.
> getNextExtVal :: (Read a, OracleState om, Monad om) => om a
> getNextExtVal = do
> vals <- getExt
> setExt $ tail vals
> return $ read $ head vals
Monad transformer
=================
> type DebugT st dm = StateT (DebugState st) dm
> data DebugState st = DebugState {
> oracle :: Oracle,
> ext :: [String],
> toolstate :: st,
> nextOrIdx :: Integer,
> constraints :: ConstraintStore
> }
> deriving Show
> runDebugT :: Monad dm => DebugT st dm a -> dm a
> runDebugT ma =
> (runStateT ma $ DebugState { oracle = undefined,
> ext = undefined,
> toolstate = undefined,
> nextOrIdx = 1,
> constraints = emptyConstraintStore })
> >>= return . fst
> newtype DMT st m a = DMT { runDMT :: DebugT st m a }
> instance Monad m => Monad (DMT st m) where
> a >>= b = DMT (runDMT a >>= runDMT . b)
> return x = DMT (return x)
> instance Monad m => OracleState (DMT st m) where
> getOracle = DMT (get >>= return . oracle)
> setOracle o = DMT (get >>= \ dst -> put dst{oracle=o})
> getExt = DMT (get >>= return . ext)
> setExt ext = DMT (get >>= \ dst -> put dst{ext=ext})
> newOrRef = DMT (do
> dst <- get
> let idx = nextOrIdx dst
> idx' = idx+1
> seq idx' (put dst{nextOrIdx=idx'})
> return idx)
> getConstraintStore = DMT (get >>= return . constraints)
> setConstraintStore s = DMT (get >>= \ dst -> put dst{constraints=s})
> getToolState :: Monad dm => DMT st dm st
> getToolState = DMT (get >>= return . toolstate)
> putToolState :: Monad dm => st -> DMT st dm ()
> putToolState st = DMT (get >>= \ dst -> put dst{toolstate=st})
> modifyToolState :: Monad dm => (st -> st) -> DMT st dm ()
> modifyToolState f = DMT (get >>= \ dst ->
> put dst{toolstate=(f $ toolstate dst)})
> instance MonadTrans (DMT st) where
> lift m = DMT (lift m)
> instance MonadIO m => MonadIO (DMT st m) where
> liftIO = lift . liftIO
> debug :: (DM dm,MonadIO dm) => dm a -> String -> dm a
> debug oa sfile = do
> ora <- liftIO $ readStepsFile sfile
> setOracle ora
> ext <- liftIO $ readExtFile sfile
> setExt ext
> oa
> debugDMT :: (DM (DMT st dm),MonadIO dm) =>
> DMT st dm a -> String -> st -> dm a
> debugDMT exp steps st =
> runDebugT (runDMT (debug (maybePrintOracle (putToolState st >> exp)) steps))
> maybePrintOracle action = do
> res <- action
> o <- getOracle
> unless (o==emptyOracle)
> (liftIO $ putStrLn "oracle not empty!" >> print o)
> return res