module Debug.Hoed.CompTree
( CompTree
, Vertex(..)
, mkCompTree
, isRootVertex
, vertexUID
, vertexRes
, replaceVertex
, getJudgement
, setJudgement
, isRight
, isWrong
, isUnassessed
, isAssisted
, isInconclusive
, isPassing
, leafs
, ConstantValue(..)
, unjudgedCharacterCount
#if defined(TRANSCRIPT)
, getTranscript
#endif
, TraceInfo(..)
, traceInfo
, Graph(..)
)where
import Control.Exception
import Control.Monad
import Control.Monad.ST
import Debug.Hoed.EventForest
import Debug.Hoed.Observe
import Debug.Hoed.Span
import Debug.Hoed.Render
import Data.Bits
import Data.Graph.Libgraph
import qualified Data.Set as Set
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IntMap
import Data.IntSet (IntSet)
import Data.List (foldl', unfoldr)
import Data.Maybe
import Data.Semigroup
import Data.Vector.Mutable as VM (STVector)
import qualified Data.Vector.Generic.Mutable as VM
import qualified Data.Vector.Unboxed as U
import Data.Word
import GHC.Exts (IsList (..))
import GHC.Generics
import Prelude hiding (Right)
data Vertex = RootVertex | Vertex {vertexStmt :: CompStmt, vertexJmt :: Judgement}
deriving (Eq,Show,Ord,Generic)
getJudgement :: Vertex -> Judgement
getJudgement RootVertex = Right
getJudgement v = vertexJmt v
setJudgement :: Vertex -> Judgement -> Vertex
setJudgement RootVertex _ = RootVertex
setJudgement v j = v{vertexJmt=j}
isRight :: Vertex -> Bool
isRight v = getJudgement v == Right
isWrong :: Vertex -> Bool
isWrong v = getJudgement v == Wrong
isUnassessed :: Vertex -> Bool
isUnassessed v = getJudgement v == Unassessed
isAssisted :: Vertex -> Bool
isAssisted v = case getJudgement v of (Assisted _) -> True; _ -> False
isInconclusive :: Vertex -> Bool
isInconclusive v = case getJudgement v of
(Assisted ms) -> any isInconclusive' ms
_ -> False
isInconclusive' :: AssistedMessage -> Bool
isInconclusive' (InconclusiveProperty _) = True
isInconclusive' _ = False
isPassing :: Vertex -> Bool
isPassing v = case getJudgement v of
(Assisted ms) -> any isPassing' ms
_ -> False
isPassing' :: AssistedMessage -> Bool
isPassing' (PassingProperty _) = True
isPassing' _ = False
vertexUID :: Vertex -> UID
vertexUID RootVertex = 1
vertexUID (Vertex s _) = stmtIdentifier s
vertexRes :: Vertex -> String
vertexRes RootVertex = "RootVertex"
vertexRes v = stmtRes . vertexStmt $ v
type CompTree = Graph Vertex ()
isRootVertex :: Vertex -> Bool
isRootVertex RootVertex = True
isRootVertex _ = False
leafs :: CompTree -> [Vertex]
leafs g = filter (not . (`Set.member` nonLeafs)) (vertices g)
where
nonLeafs = Set.fromList [s | Arc s t _ <- arcs g]
unjudgedCharacterCount :: CompTree -> Int
unjudgedCharacterCount = sum . map characterCount . filter unjudged . vertices
where characterCount = length . stmtLabel . vertexStmt
unjudged :: Vertex -> Bool
unjudged = not . judged
judged :: Vertex -> Bool
judged v = isRight v || isWrong v
replaceVertex :: CompTree -> Vertex -> CompTree
replaceVertex g v = mapGraph f g
where f RootVertex = RootVertex
f v' | vertexUID v' == vertexUID v = v
| otherwise = v'
mkCompTree :: [CompStmt] -> Dependencies -> CompTree
mkCompTree cs ds = Graph RootVertex vs as
where vs = RootVertex : map (`Vertex` Unassessed) cs
as = [Arc (findVertex i) (findVertex j) () | (i,jj) <- toList ds, j <- toList jj]
vMap :: IntMap Vertex
vMap = foldl' (\m c -> IntMap.insert (stmtIdentifier c) (Vertex c Unassessed) m) IntMap.empty cs
findVertex :: UID -> Vertex
findVertex (1) = RootVertex
findVertex a = case IntMap.lookup a vMap of
Nothing -> error $ "mkCompTree: Error, cannot find a statement with UID " ++ show a ++ "!\n"
++ "We recorded statements with the following UIDs: " ++ (show . IntMap.keys) vMap ++ "\n"
++ unlines (map (\c -> (show . stmtIdentifier) c ++ ": " ++ show c) cs)
(Just v) -> v
data ConstantValue = ConstantValue { valStmt :: !UID, valLoc :: !Location
, valMin :: !UID, valMax :: !UID }
| CVRoot
deriving Eq
instance Show ConstantValue where
show CVRoot = "Root"
show v = "Stmt-" ++ (show . valStmt $ v)
++ "-" ++ (show . valLoc $ v)
++ ": " ++ (show . valMin $ v)
++ "-" ++ (show . valMax $ v)
newtype TopLvlFun = TopLvlFun UID deriving Eq
noTopLvlFun :: TopLvlFun
noTopLvlFun = TopLvlFun (1)
data EventDetails = EventDetails
{ topLvlFun_ :: !TopLvlFun
, locations :: ParentPosition -> Bool
}
topLvlFun :: EventDetails -> UID
topLvlFun EventDetails{topLvlFun_ = TopLvlFun x} = x
type EventDetailsStore s = VM.STVector s EventDetails
getEventDetails :: EventDetailsStore s -> UID -> ST s EventDetails
getEventDetails = VM.unsafeRead
setEventDetails :: EventDetailsStore s -> UID -> EventDetails -> ST s ()
setEventDetails = VM.unsafeWrite
getTopLvlFunOr :: UID -> EventDetails -> UID
getTopLvlFunOr def EventDetails{topLvlFun_}
| topLvlFun_ == noTopLvlFun = def
| TopLvlFun x <- topLvlFun_ = x
type Dependencies = IntMap IntSet
data TraceInfo = TraceInfo
{ computations :: !SpanZipper
, dependencies :: !Dependencies
#if defined(TRANSCRIPT)
, messages :: !(IntMap String)
#endif
}
deriving Show
addMessage :: Event -> String -> TraceInfo -> TraceInfo
#if defined(TRANSCRIPT)
addMessage e msg s = s{ messages = (flip $ IntMap.insert i) (messages s) $ case IntMap.lookup i (messages s) of
Nothing -> msg
(Just msg') -> msg' ++ ", " ++ msg }
where i = eventUID e
getMessage :: Event -> TraceInfo -> String
getMessage e s = case IntMap.lookup i (messages s) of
Nothing -> ""
(Just msg) -> msg
where i = eventUID e
getTranscript :: [Event] -> TraceInfo -> String
getTranscript es t = foldl (\acc e -> (show e ++ m e) ++ "\n" ++ acc) "" es
where m e = case IntMap.lookup (eventUID e) ms of
Nothing -> ""
(Just msg) -> "\n " ++ msg
ms = messages t
#else
addMessage _ _ t = t
#endif
collectEventDetails :: EventDetailsStore s -> Event -> ST s (Bool,UID)
collectEventDetails v e = do
let !p = eventParent e
parentDetails <- getEventDetails v (parentUID p)
let !loc = locations parentDetails (parentPosition p)
!top = getTopLvlFunOr (parentUID p) parentDetails
return (loc, top)
mkFunDetails :: EventDetailsStore s -> Event -> ST s EventDetails
mkFunDetails s e = do
let p = eventParent e
ed <- getEventDetails s (parentUID p)
let !loc = locations ed (parentPosition p)
!top = getTopLvlFunOr (eventUID e) ed
locFun 0 = not loc
locFun 1 = loc
return $ EventDetails (TopLvlFun top) locFun
start, stop,pause,resume :: Event -> EventDetails -> TraceInfo -> TraceInfo
start e ed s = m s{computations = cs}
where i = topLvlFun ed
cs = startSpan i $ computations s
m = addMessage e $ "Start computation " ++ show i ++ ": " ++ show cs
stop e ed s = m s {computations = cs'}
where
i = topLvlFun ed
cs' = stopSpan i (computations s)
m = addMessage e $ "Stop computation " ++ show i ++ ": " ++ show cs'
pause e ed s = m s {computations = cs'}
where
i = topLvlFun ed
cs' = pauseSpan i (computations s)
m = addMessage e $ "Pause up to " ++ show i ++ ": " ++ show cs'
resume e ed s = m s {computations = cs'}
where
i = topLvlFun ed
cs' = resumeSpan i (computations s)
m = addMessage e $ "Resume computation " ++ show i ++ ": " ++ show cs'
activeComputations :: TraceInfo -> [UID]
activeComputations s = map getSpanUID . filter isActive . toList $ computations s
where isActive (Computing _) = True
isActive _ = False
addDependency :: Event -> TraceInfo -> TraceInfo
addDependency _e s =
m s{dependencies = case d of
Just (from,to) -> IntMap.insertWith (<>) from [to] (dependencies s)
Nothing -> dependencies s}
where d = case activeComputations s of
[] -> Nothing
[n] -> Just (1, n)
(n:m:_) -> Just (m, n)
m = case d of
Nothing -> addMessage _e ("does not add dependency")
(Just (a, b)) -> addMessage _e ("adds dependency " ++ show a ++ " -> " ++ show b)
type ConsMap = U.Vector Word
mkConsMap :: Int -> Trace -> ConsMap
mkConsMap l t =
U.create $ do
v <- VM.replicate l 0
forM_ t $ \e ->
case change e of
Cons {} -> do
let p = eventParent e
#if __GLASGOW_HASKELL__ >= 800
VM.unsafeModify v (`setBit` parentPosition p) (parentUID p 1)
#else
let ix = parentUID p 1
x <- VM.unsafeRead v ix
VM.unsafeWrite v ix (x `setBit` parentPosition p)
#endif
_ -> return ()
return v
corToCons :: ConsMap -> Event -> Bool
corToCons cm e = case U.unsafeIndex cm (parentUID p 1) of
0 -> False
other -> testBit other (parentPosition p)
where p = eventParent e
traceInfo :: Int -> Trace -> TraceInfo
traceInfo l trc = runST $ do
v <- VM.replicate (l+1) $ EventDetails noTopLvlFun (const False)
let loop !s e =
case change e of
Observe {} -> do
setEventDetails v (eventUID e) (EventDetails noTopLvlFun (const True))
return s
Fun {} -> do
setEventDetails v (eventUID e) =<< mkFunDetails v e
return s
Enter {}
| corToCons cs e -> do
(loc, top) <- collectEventDetails v e
let !details = EventDetails (TopLvlFun top) (const loc)
setEventDetails v (eventUID e) details
return $ if loc
then addDependency e . start e details $ s
else pause e details s
| otherwise -> return s
Cons {} -> do
(loc, top) <- collectEventDetails v e
let !details = EventDetails (TopLvlFun top) (const loc)
setEventDetails v (eventUID e) details
return $ if loc
then stop e details s
else resume e details s
foldM loop s0 trc
where
s0 :: TraceInfo
s0 = TraceInfo [] []
#if defined(TRANSCRIPT)
IntMap.empty
#endif
cs :: ConsMap
cs = mkConsMap l trc