packages feed

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