packages feed

Hoed 0.4.1 → 0.5.0

raw patch · 30 files changed

+1272/−416 lines, 30 filesdep +cereal-textdep +cereal-vectordep +hashablePVP ok

version bump matches the API change (PVP)

Dependencies added: cereal-text, cereal-vector, hashable, hashtables, open-browser, primitive, regex-tdfa-text, text, transformers, vector-th-unbox

API changes (from Hackage documentation)

- Debug.Hoed: O :: (forall a. (Observable a) => String -> a -> a) -> Observer
- Debug.Hoed: newtype Observer
- Debug.Hoed.Observe: O :: (forall a. (Observable a) => String -> a -> a) -> Observer
- Debug.Hoed.Observe: badEvents :: Trace
- Debug.Hoed.Observe: newtype Observer
- Debug.Hoed.Observe: startEventStream :: IO ()
- Debug.Hoed.Observe: type Observing a = a -> a
+ Debug.Hoed.CompTree: instance (Control.DeepSeq.NFData a, Control.DeepSeq.NFData b) => Control.DeepSeq.NFData (Data.Graph.Libgraph.Core.Arc a b)
+ Debug.Hoed.CompTree: instance Control.DeepSeq.NFData Data.Graph.Libgraph.AlgoDebug.AssistedMessage
+ Debug.Hoed.CompTree: instance Control.DeepSeq.NFData Data.Graph.Libgraph.AlgoDebug.Judgement
+ Debug.Hoed.CompTree: instance Control.DeepSeq.NFData Debug.Hoed.CompTree.Vertex
+ Debug.Hoed.CompTree: instance Data.Hashable.Class.Hashable Debug.Hoed.CompTree.Vertex
+ Debug.Hoed.Observe: ConsChar :: !Char -> Change
+ Debug.Hoed.Observe: EventWithId :: !UID -> !Event -> EventWithId
+ Debug.Hoed.Observe: [event] :: EventWithId -> !Event
+ Debug.Hoed.Observe: data EventWithId
+ Debug.Hoed.Observe: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Debug.Hoed.Observe.Change
+ Debug.Hoed.Observe: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Debug.Hoed.Observe.Event
+ Debug.Hoed.Observe: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Debug.Hoed.Observe.Parent
+ Debug.Hoed.Observe: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Debug.Hoed.Observe.Change
+ Debug.Hoed.Observe: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Debug.Hoed.Observe.Event
+ Debug.Hoed.Observe: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Debug.Hoed.Observe.Parent
+ Debug.Hoed.Observe: instance Data.Vector.Unboxed.Base.Unbox Debug.Hoed.Observe.Change
+ Debug.Hoed.Observe: instance Data.Vector.Unboxed.Base.Unbox Debug.Hoed.Observe.Event
+ Debug.Hoed.Observe: instance Data.Vector.Unboxed.Base.Unbox Debug.Hoed.Observe.Parent
+ Debug.Hoed.Observe: instance GHC.Show.Show Debug.Hoed.Observe.EventWithId
+ Debug.Hoed.Observe: lookupOrAddString :: Text -> IO Int
+ Debug.Hoed.Observe: lookupString :: Int -> Text
+ Debug.Hoed.Observe: strings :: MVar (Pair Int (CuckooHashTable Text Int))
+ Debug.Hoed.Observe: stringsLookupTable :: IORef (Vector Text)
+ Debug.Hoed.Render: instance Data.Hashable.Class.Hashable Debug.Hoed.Render.CDSsansUID
+ Debug.Hoed.Render: instance Data.Hashable.Class.Hashable Debug.Hoed.Render.CompStmt
+ Debug.Hoed.Render: instance GHC.Base.Applicative Debug.Hoed.Render.MemoM
+ Debug.Hoed.Render: instance GHC.Base.Functor Debug.Hoed.Render.MemoM
+ Debug.Hoed.Render: instance GHC.Base.Monad Debug.Hoed.Render.MemoM
+ Debug.Hoed.Render: instance GHC.Classes.Eq Debug.Hoed.Render.CDSsansUID
+ Debug.Hoed.Render: instance GHC.Classes.Ord Debug.Hoed.Render.CDSsansUID
+ Debug.Hoed.Render: instance GHC.Exts.IsList Debug.Hoed.Render.ParentList
- Debug.Hoed: CompStmt :: !String -> !UID -> !StmtDetails -> CompStmt
+ Debug.Hoed: CompStmt :: !Text -> !UID -> !StmtDetails -> CompStmt
- Debug.Hoed: HoedAnalysis :: [Event] -> CompTree -> HoedAnalysis
+ Debug.Hoed: HoedAnalysis :: Trace -> CompTree -> HoedAnalysis
- Debug.Hoed: [hoedTrace] :: HoedAnalysis -> [Event]
+ Debug.Hoed: [hoedTrace] :: HoedAnalysis -> Trace
- Debug.Hoed: [stmtLabel] :: CompStmt -> !String
+ Debug.Hoed: [stmtLabel] :: CompStmt -> !Text
- Debug.Hoed: observe :: (Observable a) => String -> a -> a
+ Debug.Hoed: observe :: (Observable a) => Text -> a -> a
- Debug.Hoed: observeOpaque :: String -> a -> Parent -> a
+ Debug.Hoed: observeOpaque :: Text -> a -> Parent -> a
- Debug.Hoed: send :: String -> ObserverM a -> Parent -> a
+ Debug.Hoed: send :: Text -> ObserverM a -> Parent -> a
- Debug.Hoed.CompTree: traceInfo :: Int -> Trace -> TraceInfo
+ Debug.Hoed.CompTree: traceInfo :: Verbosity -> Trace -> IO TraceInfo
- Debug.Hoed.Observe: Cons :: !Int -> !String -> Change
+ Debug.Hoed.Observe: Cons :: !Word8 -> !Text -> Change
- Debug.Hoed.Observe: Event :: !UID -> !Parent -> !Change -> Event
+ Debug.Hoed.Observe: Event :: {-# UNPACK #-} !Parent -> !Change -> Event
- Debug.Hoed.Observe: Observe :: !String -> Change
+ Debug.Hoed.Observe: Observe :: !Text -> Change
- Debug.Hoed.Observe: ObserverM :: (Int -> Int -> (a, Int)) -> ObserverM a
+ Debug.Hoed.Observe: ObserverM :: (Int -> Word8 -> (a, Word8)) -> ObserverM a
- Debug.Hoed.Observe: [eventParent] :: Event -> !Parent
+ Debug.Hoed.Observe: [eventParent] :: Event -> {-# UNPACK #-} !Parent
- Debug.Hoed.Observe: [eventUID] :: Event -> !UID
+ Debug.Hoed.Observe: [eventUID] :: EventWithId -> !UID
- Debug.Hoed.Observe: [runMO] :: ObserverM a -> Int -> Int -> (a, Int)
+ Debug.Hoed.Observe: [runMO] :: ObserverM a -> Int -> Word8 -> (a, Word8)
- Debug.Hoed.Observe: events :: IORef Trace
+ Debug.Hoed.Observe: events :: Rope IO MVector Event
- Debug.Hoed.Observe: gdmShallowShow :: GObservable f => f a -> String
+ Debug.Hoed.Observe: gdmShallowShow :: GObservable f => f a -> Text
- Debug.Hoed.Observe: generateContext :: (a -> Parent -> a) -> String -> a -> (a, Int)
+ Debug.Hoed.Observe: generateContext :: (a -> Parent -> a) -> Text -> a -> (a, Int)
- Debug.Hoed.Observe: gobserve :: (a -> Parent -> a) -> String -> a -> (a, Int)
+ Debug.Hoed.Observe: gobserve :: (a -> Parent -> a) -> Text -> a -> (a, Int)
- Debug.Hoed.Observe: observe :: (Observable a) => String -> a -> a
+ Debug.Hoed.Observe: observe :: (Observable a) => Text -> a -> a
- Debug.Hoed.Observe: observeOpaque :: String -> a -> Parent -> a
+ Debug.Hoed.Observe: observeOpaque :: Text -> a -> Parent -> a
- Debug.Hoed.Observe: send :: String -> ObserverM a -> Parent -> a
+ Debug.Hoed.Observe: send :: Text -> ObserverM a -> Parent -> a
- Debug.Hoed.Observe: type ParentPosition = Int
+ Debug.Hoed.Observe: type ParentPosition = Word8
- Debug.Hoed.Observe: type Trace = [Event]
+ Debug.Hoed.Observe: type Trace = Vector Event
- Debug.Hoed.Render: CompStmt :: !String -> !UID -> !StmtDetails -> CompStmt
+ Debug.Hoed.Render: CompStmt :: !Text -> !UID -> !StmtDetails -> CompStmt
- Debug.Hoed.Render: StmtCon :: !String -> !String -> StmtDetails
+ Debug.Hoed.Render: StmtCon :: Hashed Text -> Hashed Text -> StmtDetails
- Debug.Hoed.Render: StmtLam :: ![String] -> !String -> !String -> StmtDetails
+ Debug.Hoed.Render: StmtLam :: [Hashed Text] -> Hashed Text -> Hashed Text -> StmtDetails
- Debug.Hoed.Render: [stmtCon] :: StmtDetails -> !String
+ Debug.Hoed.Render: [stmtCon] :: StmtDetails -> Hashed Text
- Debug.Hoed.Render: [stmtLabel] :: CompStmt -> !String
+ Debug.Hoed.Render: [stmtLabel] :: CompStmt -> !Text
- Debug.Hoed.Render: [stmtLamArgs] :: StmtDetails -> ![String]
+ Debug.Hoed.Render: [stmtLamArgs] :: StmtDetails -> [Hashed Text]
- Debug.Hoed.Render: [stmtLamRes] :: StmtDetails -> !String
+ Debug.Hoed.Render: [stmtLamRes] :: StmtDetails -> Hashed Text
- Debug.Hoed.Render: [stmtPretty] :: StmtDetails -> !String
+ Debug.Hoed.Render: [stmtPretty] :: StmtDetails -> Hashed Text
- Debug.Hoed.Render: eventsToCDS :: [Event] -> CDSSet
+ Debug.Hoed.Render: eventsToCDS :: Trace -> CDSSet
- Debug.Hoed.Render: stmtRes :: CompStmt -> String
+ Debug.Hoed.Render: stmtRes :: CompStmt -> Text

Files

+ Data/Rope/Mutable.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE RecordWildCards #-}+module Data.Rope.Mutable+  ( Rope+  , Data.Rope.Mutable.new+  , new'+  , fromList+  , write+  , reset+  ) where++import Control.Monad+import Control.Monad.Primitive+import Data.Foldable as F+import Data.Primitive.MutVar+import Data.Proxy+import qualified Data.Vector as V+import qualified Data.Vector.Generic as VG+import qualified Data.Vector.Generic.Mutable as M+import Data.Vector.Generic.Mutable as M (MVector, read, new)++data RopeState m v a = RopeState+  { ropeDepth :: !Int+  , ropeLastIndex :: !Int+  , ropeElements :: [v (PrimState m) a]+  , spillOver :: v (PrimState m) a+  }++-- | A mutable bag-like collection with atomic O(1) inserts+data Rope m v a = Rope+  { ropeState   :: MutVar (PrimState m) (RopeState m v a)+  , ropeDim     :: !Int -- ^ Size of internal vectors+  }++defaultRopeDim = 10000++{-# INLINE write #-}+write :: forall v a m . (PrimMonad m, MVector v a) => Rope m v a -> Int -> a -> m ()+write Rope {..} (fromEnum -> ix) a = join $ atomicModifyMutVar' ropeState updateState+  where+    (d, r) = divMod ix ropeDim+    updateState :: RopeState m v a -> (RopeState m v a, m ())+    updateState st@RopeState {..}+      | d < ropeDepth =+        ( st {ropeLastIndex = max ropeLastIndex ix}+        , M.unsafeWrite (ropeElements !! (ropeDepth - 1 - d)) r a)+      | d == ropeDepth =+        ( st+          { ropeElements = spillOver : ropeElements+          , ropeDepth = d + 1+          , ropeLastIndex = max ropeLastIndex ix+          }+        , do M.unsafeWrite spillOver r a+             v <- M.new ropeDim+             atomicModifyMutVar' ropeState $ \st' -> (st' {spillOver = v}, ()))+      | otherwise = error $ "index " ++ show ix ++ " too far away from the last index " ++ show ropeLastIndex++new :: forall v a m . (PrimMonad m, MVector v a) => m (Rope m v a)+new = new' defaultRopeDim++new' :: forall v a m . (PrimMonad m, MVector v a) => Int -> m (Rope m v a)+new' ropeDim = do+  spillOver  <- M.new ropeDim+  ropeState <- newMutVar (RopeState 0 (-1) [] spillOver)+  return Rope{..}++-- | Returns an immutable snapshot of the rope contents after resetting the rope to the empty state+reset :: forall v a m . (VG.Vector v a, PrimMonad m) => Proxy v -> Rope m (VG.Mutable v) a -> m (v a)+reset proxy it@Rope{..} = do+  (lastIndex, ropeElements) <-+    atomicModifyMutVar' ropeState $ \RopeState {..} ->+      (RopeState 0 (-1) [] spillOver, (ropeLastIndex, ropeElements))+  lv <- mapM VG.unsafeFreeze ropeElements+  let joined :: v a+        | h:t <- lv+        = VG.concat (reverse t ++ [VG.slice 0 (lastIndex `mod` ropeDim + 1) h])+        | otherwise = VG.empty+  return joined++fromList :: forall v m a. (PrimMonad m, MVector v a) => Int -> [a] -> m(Rope m v a)+fromList dim xx = do+  rope <- new' dim+  forM_ (zip [0..] xx) $ \(i,x) -> write rope i x+  return rope++propFromList dim xx =+  check . toList <$> (reset (Proxy :: Proxy V.Vector) =<< fromList dim xx)+  where+    check xx'+      | xx == xx' = True+      | otherwise = error (show xx')
Debug/Hoed.hs view
@@ -1,6 +1,8 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ImplicitParams  #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-}@@ -184,7 +186,6 @@   , UnevalHandler(..)     -- * The Observable class-  , Observer(..)   , Observable(..)   , (<<)   , thunk@@ -198,13 +199,17 @@   ) where  import Control.DeepSeq+import Control.Monad+import qualified Data.Vector.Generic as VG import           Debug.Hoed.CompTree import           Debug.Hoed.Console import           Debug.Hoed.Observe import           Debug.Hoed.Prop import           Debug.Hoed.Render import           Debug.Hoed.Serialize+import           Debug.Hoed.Util +import           Data.Foldable (toList) import           Data.IORef import           Prelude                      hiding (Right) import           System.Clock@@ -245,11 +250,12 @@ debugO program =      do { runOnce         ; initUniq-        ; startEventStream         ; let errorMsg e = "[Escaping Exception in Code : " ++ show e ++ "]"         ; ourCatchAllIO (do { _ <- program ; return () })                         (hPutStrLn stderr . errorMsg)-        ; endEventStream+        ; res <- endEventStream+        ; initUniq+        ; return res         }  -- | The main entry point; run some IO code, and debug inside it.@@ -317,14 +323,8 @@   return ()  -data Verbosity = Verbose | Silent--condPutStrLn :: Verbosity -> String -> IO ()-condPutStrLn Silent _    = return ()-condPutStrLn Verbose msg = hPutStrLn stderr msg- data HoedAnalysis = HoedAnalysis-  { hoedTrace       :: [Event]+  { hoedTrace       :: Trace   , hoedCompTree    :: CompTree   } @@ -336,36 +336,62 @@ defaultHoedOptions :: HoedOptions defaultHoedOptions = HoedOptions Silent 110 +--------------------------------------------+ -- |Entry point giving you access to the internals of Hoed. Also see: runO. runO' :: HoedOptions -> IO a -> IO HoedAnalysis runO' HoedOptions{..} program = let ?statementWidth = prettyWidth in do+  hSetBuffering stderr NoBuffering   createDirectoryIfMissing True ".Hoed/"-  t1 <- getTime Monotonic+  tProgram <- stopWatch   condPutStrLn verbose "=== program output ===\n"   events <- debugO program-  t2 <- getTime Monotonic-  let programTime = toSecs(diffTimeSpec t1 t2)-  condPutStrLn verbose $ "\n=== program terminated (" ++ show programTime ++ " seconds) ==="-  condPutStrLn verbose"Please wait while the computation tree is constructed..."+  programTime <- tProgram+  condPutStrLn verbose $ "\n=== program terminated (" ++ show programTime ++ ") ===" -  let e = length events+#if defined(DEBUG)+  writeFile ".Hoed/Events"     (unlines . map show $ toList events)+#endif    condPutStrLn verbose "\n=== Statistics ===\n"-  condPutStrLn verbose $ show e ++ " events"+  condPutStrLn verbose $ show (VG.length events) ++ " events"+  condPutStrLn verbose"Please wait while the computation tree is constructed..." -  let !cdss = eventsToCDS events-      !eqs  = force $ renderCompStmts cdss-      ti   = traceInfo e (reverse events)-      !ds  = force $ dependencies ti-      ct  = mkCompTree eqs ds+  tTrace <- stopWatch+  ti  <- traceInfo verbose events+  traceTime <- tTrace+  condPutStrLn verbose $ " " ++ show traceTime +  let cdss = eventsToCDS events+      eqs  = renderCompStmts cdss+  let !ds  = force $ dependencies ti+      ct   = mkCompTree eqs ds++  condPutStr verbose "Calculating the nodes of the computation graph"+  tCds <- stopWatch+  forM_ (zip [0..] cdss) $ \(i,x) -> do+    evaluate (force x)+    when (isPowerOf 2 i) $ condPutStr verbose "."+  cdsTime <- tCds+  condPutStrLn verbose $ " " ++ show cdsTime++  condPutStr verbose "Rendering the nodes of the computation graph"+  tEqs <- stopWatch+  forM_ (zip [0..] eqs) $ \(i,x) -> do+    evaluate (case stmtDetails x of+                       StmtCon c _ -> seq c ()+                       StmtLam args res _ -> args `seq` res `seq` ())+    when (isPowerOf 2 i) $ condPutStr verbose "."+  eqsTime <- tEqs+  condPutStrLn verbose $ " " ++ show eqsTime+ #if defined(DEBUG)-  writeFile ".Hoed/Events"     (unlines . map show . reverse $ events)-  writeFile ".Hoed/Cdss"       (unlines . map show $ cdss2)-  writeFile ".Hoed/Eqs"        (unlines . map show $ eqs)+  -- writeFile ".Hoed/Cdss"       (unlines . map show $ cdss2)+  writeFile ".Hoed/Eqs"        (unlines . map show $ toList eqs)+  writeFile ".Hoed/Deps"       (unlines . map show $ toList ds) #endif #if defined(TRANSCRIPT)-  writeFile ".Hoed/Transcript" (getTranscript events ti)+  writeFile ".Hoed/Transcript" (getTranscript (toList events) ti) #endif    let n  = length eqs@@ -375,14 +401,15 @@   condPutStrLn verbose $ show (length . arcs $ ct) ++ " edges in computation tree"   condPutStrLn verbose $ "computation tree has a branch factor of " ++ show b ++ " (i.e the average number of children of non-leaf nodes)" -  t3 <- getTime Monotonic-  let compTime = toSecs(diffTimeSpec t2 t3)-  condPutStrLn verbose $ "\n=== Debug Session (" ++ show compTime ++ " seconds) ===\n"+  let compTime = traceTime + cdsTime + eqsTime+  condPutStrLn verbose $ "\n=== Debug Session (" ++ show compTime ++ ") ===\n"   return $ HoedAnalysis events ct-    where-       toSecs :: TimeSpec -> Double-       toSecs spec = fromIntegral(sec spec) + fromIntegral(nsec spec) * 1e-9 +isPowerOf n 0 = False+isPowerOf n k | n == k         = True+              | k `mod` n == 0 = isPowerOf n (k `div` n)+              | otherwise      = False+ -- | Trace and write computation tree to file. Useful for regression testing. logO :: FilePath -> IO a -> IO () logO filePath program = {- SCC "logO" -} do@@ -390,12 +417,6 @@   writeFile filePath (showGraph hoedCompTree)   return () -  where showGraph g        = showWith g showVertex showArc-        showVertex RootVertex = ("\".\"","shape=none")-        showVertex v       = ("\"" ++ (escape . showCompStmt) v ++ "\"", "")-        showArc _          = ""-        showCompStmt       = show . vertexStmt- -- | As logO, but with property-based judging. logOwp :: UnevalHandler -> FilePath -> [Propositions] -> IO a -> IO () logOwp handler filePath properties program = do@@ -413,7 +434,9 @@   #if __GLASGOW_HASKELL__ >= 710--- A catch-all instance for non observable types+-- NOTE: This instance is orphaned here deliberately.+--       Moving it will break polymorphic observations in GHC 8.2x+-- | A catch-all instance for non observable types that produces the opaque observation @<?>@. instance {-# OVERLAPPABLE #-} Observable a where   observer = observeOpaque "<?>"   constrain _ _ = error "constrained by untraced value"
Debug/Hoed/CompTree.hs view
@@ -3,10 +3,6 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE MultiWayIf            #-}--- This file is part of the Haskell debugger Hoed.------ Copyright (c) Maarten Faddegon, 2015- {-# LANGUAGE BangPatterns          #-} {-# LANGUAGE CPP                   #-} {-# LANGUAGE DeriveGeneric         #-}@@ -15,6 +11,11 @@ {-# LANGUAGE OverloadedLists       #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} +-- This file is part of the Haskell debugger Hoed.+--+-- Copyright (c) Maarten Faddegon, 2015++ module Debug.Hoed.CompTree ( CompTree , Vertex(..)@@ -41,25 +42,31 @@ , traceInfo , Graph(..) -- re-export from LibGraph )where-import           Control.Exception+import           Control.DeepSeq+import           Control.Exception as E import           Control.Monad-import           Control.Monad.ST import           Debug.Hoed.EventForest import           Debug.Hoed.Observe import           Debug.Hoed.Span import           Debug.Hoed.Render+import           Debug.Hoed.Util  import           Data.Bits+import qualified Data.Foldable          as F import           Data.Graph.Libgraph import qualified Data.Set               as Set+import           Data.Hashable 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           Data.Text (Text, pack, unpack)+import qualified Data.Text as T+import qualified Data.Vector.Generic as VG import qualified Data.Vector.Generic.Mutable as VM+import           Data.Vector.Mutable as VM (IOVector) import qualified Data.Vector.Unboxed    as U import           Data.Word import           GHC.Exts               (IsList (..))@@ -67,8 +74,24 @@ import           Prelude                hiding (Right)  data Vertex = RootVertex | Vertex {vertexStmt :: CompStmt, vertexJmt :: Judgement}-  deriving (Eq,Show,Ord,Generic)+  deriving (Show,Ord,Generic) +instance Eq Vertex where+  RootVertex == RootVertex   = True+  v1@Vertex{} == v2@Vertex{} = vertexStmt v1 == vertexStmt v2+  _ == _ = False++instance Hashable Vertex where+  hashWithSalt s RootVertex    = s `hashWithSalt` (-1 :: Int)+  hashWithSalt s (Vertex cs _) = s `hashWithSalt` cs++instance NFData Vertex++-- FIXME These orphan instances should be moved to libgraph+instance NFData AssistedMessage+instance NFData Judgement+instance (NFData a, NFData b) => NFData (Arc a b)+ getJudgement :: Vertex -> Judgement getJudgement RootVertex = Right getJudgement v          = vertexJmt v@@ -108,7 +131,7 @@  vertexRes :: Vertex -> String vertexRes RootVertex = "RootVertex"-vertexRes v          = stmtRes . vertexStmt $ v+vertexRes v          = unpack . stmtRes . vertexStmt $ v  -- | The forest of computation trees. Also see the Libgraph library. type CompTree = Graph Vertex ()@@ -126,7 +149,7 @@ -- of the unjudged computation statements (i.e not Right or Wrong) in the tree. unjudgedCharacterCount :: CompTree -> Int unjudgedCharacterCount = sum . map characterCount . filter unjudged . vertices-  where characterCount = length . stmtLabel . vertexStmt+  where characterCount = fromIntegral . T.length . stmtLabel . vertexStmt  unjudged :: Vertex -> Bool unjudged = not . judged@@ -197,12 +220,12 @@ topLvlFun :: EventDetails -> UID topLvlFun EventDetails{topLvlFun_ = TopLvlFun x} = x -type EventDetailsStore s = VM.STVector s EventDetails+type EventDetailsStore s = VM.IOVector EventDetails -getEventDetails :: EventDetailsStore s -> UID -> ST s EventDetails+getEventDetails :: EventDetailsStore s -> UID -> IO EventDetails getEventDetails = VM.unsafeRead -setEventDetails :: EventDetailsStore s -> UID -> EventDetails -> ST s ()+setEventDetails :: EventDetailsStore s -> UID -> EventDetails -> IO () setEventDetails = VM.unsafeWrite  @@ -256,7 +279,7 @@ #endif ------------------------------------------------------------------------------------------------------------------------ -collectEventDetails :: EventDetailsStore s -> Event -> ST s (Bool,UID)+collectEventDetails :: EventDetailsStore s -> Event -> IO (Bool,UID) collectEventDetails v e = do             let !p = eventParent e             parentDetails <- getEventDetails v (parentUID p)@@ -267,12 +290,12 @@ -- When we see a Fun event whose parent is not a Fun event it is a top level Fun event, -- otherwise just copy the reference to the top level Fun event from the parent. -- A top level Fun event references itself.-mkFunDetails :: EventDetailsStore s -> Event -> ST s EventDetails-mkFunDetails s e = do+mkFunDetails :: EventDetailsStore s -> UID -> Event -> IO EventDetails+mkFunDetails s uid e = do     let p = eventParent e     ed  <- getEventDetails s (parentUID p)     let !loc = locations ed (parentPosition p)-        !top = getTopLvlFunOr (eventUID e) ed+        !top = getTopLvlFunOr uid ed         locFun 0 = not loc         locFun 1 = loc     return $ EventDetails (TopLvlFun top) locFun@@ -331,69 +354,74 @@  --- Iff an event is a constant then the UID of its parent and its ParentPosition --- are elements of the ConsMap.-mkConsMap :: Int -> Trace -> ConsMap-mkConsMap l t =+mkConsMap :: Trace -> ConsMap+mkConsMap t =   U.create $ do-    v <- VM.replicate l 0-    forM_ t $ \e ->-      case change e of-        Cons {} -> do+    v <- VM.replicate (VG.length t) 0+    VG.forM_ t $ \e ->+      when (isCons (change e)) $ do           let p = eventParent e #if __GLASGOW_HASKELL__ >= 800-          VM.unsafeModify v (`setBit` parentPosition p) (parentUID p - 1)+          VM.unsafeModify v (`setBit` fromIntegral(parentPosition p)) (parentUID p) #else-          let ix = parentUID p - 1+          let ix = parentUID p           x <- VM.unsafeRead v ix-          VM.unsafeWrite v ix (x `setBit` parentPosition p)+          VM.unsafeWrite v ix (x `setBit` fromIntegral(parentPosition p)) #endif-        _ -> return ()     return v+  where+    isCons Cons{} = True+    isCons ConsChar{} = True+    isCons _ = False  corToCons :: ConsMap -> Event -> Bool-corToCons cm e = case U.unsafeIndex cm (parentUID p - 1) of+corToCons cm e = case U.unsafeIndex cm (parentUID p) of                    0 -> False-                   other -> testBit other (parentPosition p)+                   other -> testBit other (fromIntegral $ parentPosition p)   where p = eventParent e  ------------------------------------------------------------------------------------------------------------------------ -traceInfo :: Int -> Trace -> TraceInfo-traceInfo l trc = runST $ do-  -- Practically speaking, event UIDs start in 1-  v <- VM.replicate (l+1) $ EventDetails noTopLvlFun (const False)-  let loop !s e =-        case change e of+traceInfo :: Verbosity -> Trace -> IO TraceInfo+traceInfo verbose trc = do+  condPutStr verbose "Calculating the edges of the computation graph"+  v <- VM.replicate l $ EventDetails noTopLvlFun (const False)+  let loop !s uid e = do+        when (uid `mod` l100 == 0) $ condPutStr verbose "."+        case (change e) of           Observe {} -> do-            setEventDetails v (eventUID e) (EventDetails noTopLvlFun (const True))+            setEventDetails v uid (EventDetails noTopLvlFun (const True))             return s           Fun {} -> do-            setEventDetails v (eventUID e) =<< mkFunDetails v e+            setEventDetails v uid =<< mkFunDetails v uid e             return s             -- Span start           Enter {}             | corToCons cs e -> do               (loc, top) <- collectEventDetails v e               let !details = EventDetails (TopLvlFun top) (const loc)-              setEventDetails v (eventUID e) details+              setEventDetails v uid details               return $ if loc                   then addDependency e . start e details $ s                   else pause e details s             | otherwise -> return s-            -- Span end-          Cons {} -> do+            -- Span end (Cons or Char)+          other -> do             (loc, top) <- collectEventDetails v e             let !details = EventDetails (TopLvlFun top) (const loc)-            setEventDetails v (eventUID e) details+            setEventDetails v uid details             return $ if loc               then stop e details s               else resume e details s-  foldM loop s0 trc+  VG.ifoldM' loop s0 trc   where+    l = VG.length trc+    l100 = max 1 (l `div` 100)     s0 :: TraceInfo     s0 = TraceInfo [] [] #if defined(TRANSCRIPT)            IntMap.empty #endif     cs :: ConsMap-    cs = mkConsMap l trc+    cs = mkConsMap trc 
Debug/Hoed/Console.hs view
@@ -1,32 +1,319 @@-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE LambdaCase                #-}-{-# LANGUAGE NamedFieldPuns            #-}-{-# LANGUAGE OverloadedStrings         #-}-{-# LANGUAGE RecordWildCards           #-}-{-# LANGUAGE ScopedTypeVariables       #-}+{-# LANGUAGE CPP                   #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE PatternSynonyms       #-}+{-# LANGUAGE LambdaCase            #-}+{-# LANGUAGE NamedFieldPuns        #-}+{-# LANGUAGE OverloadedLists       #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RecordWildCards       #-}+{-# LANGUAGE ViewPatterns          #-} -- This file is part of the Haskell debugger Hoed. -- -- Copyright (c) Maarten Faddegon, 2014-2017-module Debug.Hoed.Console(debugSession) where+module Debug.Hoed.Console(debugSession, showGraph) where  import           Control.Monad+import           Control.Arrow (first, second)+import           Data.Char+import           Data.Foldable            as F import           Data.Graph.Libgraph      as G-import           Data.List                as List (group, nub, sort)+import           Data.List                as List (foldl', group, mapAccumL, nub, sort) import qualified Data.Map.Strict          as Map+import qualified Data.IntMap.Strict       as IntMap import           Data.Maybe+import           Data.Sequence            (Seq, ViewL (..), viewl, (<|))+import qualified Data.Sequence            as Seq import qualified Data.Set                 as Set+import           Data.Text (Text, unpack)+import qualified Data.Text                as T+import qualified Data.Text.IO             as T+import qualified Data.Vector              as V+import qualified Data.Vector.Generic      as VG+import           Data.Word import           Debug.Hoed.Compat import           Debug.Hoed.CompTree import           Debug.Hoed.Observe import           Debug.Hoed.Prop import           Debug.Hoed.ReadLine import           Debug.Hoed.Render-import           Text.PrettyPrint.FPretty-import           Text.Regex.TDFA+import           Debug.Hoed.Serialize import           Prelude                  hiding (Right)+import           System.Directory+import           System.Exit+import           System.IO+import           System.Process+import           Text.PrettyPrint.FPretty hiding ((<$>))+import           Text.Regex.TDFA+import           Text.Regex.TDFA.Text+import           Web.Browser  {-# ANN module ("HLint: ignore Use camelCase" :: String) #-} +--------------------------------------------------------------------------------+-- events+type Id = Int+-- | A representation of an event as a span+data Span = Span { spanStart, spanEnd :: Id, polarity :: Bool } deriving (Eq, Ord, Show)+-- | A representation of a trace as a span diagram: a list of columns of spans.+type Spans = [ [Span] ]+-- | The nesting depth of a span+type Depth = Int++-- | Recover the spans from the trace and arrange the columns such that they reflect+--   span containment. A span @b@ is contained in another span @a@ if+--+--   > start b > start a && end b < end a+--+--   The span in column N is contained below N other spans.+--   The renderer should draw the columns left to right.+traceToSpans :: (UID -> EventWithId) -> Trace -> Spans+traceToSpans lookupEvent =+  map sort .+  Map.elems .+  Map.fromListWith (++) .+  map (second (: [])) . snd . VG.ifoldl' (traceToSpans2' lookupEvent) ([], [])++-- This version tries to distinguish between Fun events that terminate a span,+-- and Fun events that signal a second application of a function+traceToSpans2'+  :: (UID -> EventWithId)+  -> (Seq Id, [(Depth, Span)])+  -> UID+  -> Event+  -> (Seq Id, [(Depth, Span)])+traceToSpans2' lookupEv (stack, result) uid e+  | isStart (change e) = (uid <| stack, result)+  | start :< stack' <- viewl stack+  , isEnd lookupEv uid e =+    ( stack'+    , (Seq.length stack', Span start uid (getPolarity e)) : result)+  | otherwise = (stack, result)+  where+    isStart Enter {} = True+    isStart _ = False+    getPolarity Event {change = Observe {}} = True+    getPolarity Event {eventParent = Parent p 0}+      | Event {change = Fun} <- event $ lookupEv p =+        not $ getPolarity (event $ lookupEv p)+    getPolarity Event {eventParent = Parent p _} = getPolarity (event $ lookupEv p)++isEnd lookupEv _ Event {change = Cons {}} = True+isEnd lookupEv uid me@(Event {change = Fun {}}) =+  case prevEv of+    Event{change = Enter{}} -> eventParent prevEv == eventParent me+    _ -> False+  where+    prevEv= event $ lookupEv (uid - 1)+isEnd _ _ _ = False++printTrace :: Trace -> IO ()+printTrace trace =+  putStrLn $+  renderTrace' lookupEvent lookupDescs (traceToSpans lookupEvent trace, trace)+    -- fast lookup via an array+  where+    lookupEvent i = EventWithId i (trace VG.! i)+    lookupDescs =+      (fromMaybe [] .+       (`IntMap.lookup` (IntMap.fromListWith+                           (++)+                           [ (p, [EventWithId uid e])+                           | (uid, e@Event {eventParent = Parent p _}) <-+                               VG.toList (VG.indexed trace)+                           ])))++-- | TODO to be improved+renderTrace :: (Spans, Trace) -> IO ()+renderTrace (spans, trace) = do+  putStrLn "Events"+  putStrLn "------"+  VG.mapM_ print trace+  putStrLn ""+  putStrLn "Spans"+  putStrLn "-----"+  mapM_ print spans++renderTrace' :: (UID -> EventWithId)+             -> (UID -> [EventWithId])+             -> (Spans, Trace)+             -> String+renderTrace' lookupEvent lookupDescs (columns, events) = unlines renderedLines+    -- roll :: State -> (Event, Maybe ColumnEvent) -> (State, String)+  where+    depth = length columns+    ((_, evWidth), renderedLines) =+      mapAccumL roll (replicate (depth + 1) ' ', 0)+      $ align (uncurry EventWithId <$> VG.toList (VG.indexed events)) columnEvents+    -- Merge trace events and column events+    align (ev:evs) (colEv@(rowIx, colIx, pol, isStart):colEvs)+      | eventUID ev == rowIx = (ev, Just (colIx, pol, isStart)) : align evs colEvs+      | otherwise = (ev, Nothing) : align evs (colEv : colEvs)+    align [] [] = []+    align ev [] = map  (\x -> (x, Nothing)) ev+    -- Produce the output in three columns: spans, events, and explains+    -- For spans: keep a state of the open spans+    --   (the state is the rendering in characters)+    -- For events: keep a state of the widest event+    -- For explains: circularly reuse the widest event result+    roll (state, width) (ev, Nothing)+      | (w, s) <- showWithExplains ev = ((state, max width w), state ++ s)+    roll (state, width) (ev, Just (col, pol, True))+      | state' <- update state col '|'+      , state'' <- update state col (if pol then '↑' else '┬')+      , (w, s) <- showWithExplains ev = ((state', max width w), state'' ++ s)+    roll (state, width) (ev, Just (col, pol, False))+      | state' <- update state col ' '+      , state'' <- update state col (if pol then '↓' else '┴')+      , (w, s) <- showWithExplains ev = ((state', max width w), state'' ++ s)+    -- columnEvents :: [(Line,ColIndex,StartOrEnd)]+    -- view the column spans as an event stream+    -- there's an event when a span starts or ends+    columnEvents =+      sortOn+        (\(a, b, c, d) -> a)+        [ (rowIx, colIx, pol, isStart)+        | (colIx, spans) <- zip [0 ..] columns+        , Span {..} <- spans+        , (rowIx, pol, isStart) <- [(spanStart, polarity, True), (spanEnd, polarity, False)]+        ]+    -- update element n of a list with a new value v+    update [] _ _ = []+    update (_:xs) 0 v = v : xs+    update (x:xs) n v = x : update xs (n - 1) v+    -- show the event, add the necessary padding to fill the event col width,+    -- and append the explain.+    showWithExplains ev+      | showEv <- show ev+      , l <- length showEv =+        (l, showEv ++ replicate (evWidth - l) ' ' ++ explain (eventUID ev) (event ev))+    -- Value requests+    explain uid Event {eventParent = Parent p 0, change = Enter}+      | Event {change = Fun, eventParent = Parent p' _} <- event $ lookupEvent p+      , (name,dist) <- findRoot (event $ lookupEvent p') =+        "-- request arg of " ++ unpack name ++ "/" ++ show (dist + 1)+    explain uid Event {eventParent = Parent p 1, change = Enter}+      | Event {change = Fun, eventParent = Parent p' _} <- event $ lookupEvent p+      , (name,dist) <- findRoot (event $ lookupEvent p') =+        "-- request result of " ++ unpack name ++ "/" ++ show (dist+1)+    explain uid Event {eventParent = Parent p 0, change = Enter}+      | Event {change = Observe name} <- event $ lookupEvent p =+        "-- request value of " ++ unpack name+    explain uid Event {eventParent = Parent p i, change = Enter}+      | Event {change = Cons ar name} <- event $ lookupEvent p =+        "-- request value of arg " ++ show i ++ " of constructor " ++ unpack name+    -- Arguments of functions+    explain uid me@Event {eventParent = Parent p 0, change = it@FunOrCons}+      | Event {change = Fun, eventParent = Parent p' _} <- event $ lookupEvent p+      , (name,dist) <- findRoot (event $ lookupEvent p') =+        "-- arg " ++ show (dist+1) ++ " of " ++ unpack name ++ " is " ++ showChange it+    -- Results of functions+    explain uid Event {eventParent = Parent p 1, change = it@Fun}+      | Event {change = Fun, eventParent = Parent p' _} <- event $ lookupEvent p+      , (name,dist) <- findRoot (event $ lookupEvent p') =+        "-- result of " ++ unpack name ++ "/" ++ show (dist+1) ++ " is a function"+    explain uid me@Event {eventParent = Parent p 1, change = Cons{}}+      | Event {change = Fun, eventParent = Parent p' _} <- event $ lookupEvent p+      , (name,dist) <- findRoot (event $ lookupEvent p')+      , arg <- findArg p =+        "-- " ++ unpack name ++ "/" ++ show (dist+1) ++ " " ++ arg ++" = " ++ findValue lookupDescs (EventWithId uid me)+    -- Descendants of Cons events+    explain uid Event {eventParent = Parent p i, change = Cons _ name}+      | Event {change = Cons ar name'} <- event $ lookupEvent p =+        "-- arg " ++ show i ++ " of constructor " ++ unpack name' ++ " is " ++ unpack name+    -- Descendants of root events+    explain uid Event {eventParent = Parent p i, change = Fun}+      | Event {change = Observe name} <- event $ lookupEvent p =+        "-- " ++ unpack name ++ " is a function"+    explain uid me@Event {eventParent = Parent p i, change = Cons{}}+      | Event {change = Observe name} <- event $ lookupEvent p =+        "-- " ++ unpack name ++ " = " ++ findValue lookupDescs (EventWithId uid me)+    explain _ _ = ""++    -- Returns the root observation for this event, together with the distance to it+    findRoot Event{change = Observe name} = (name, 0)+    findRoot Event{eventParent} = succ <$> findRoot (event $ lookupEvent $ parentUID eventParent)++    variableNames = map (:[]) ['a'..'z']++    showChange Fun = "a function"+    showChange (Cons ar name) = "constructor " ++ unpack name++    findArg eventUID =+      case [ e | e@(event -> Event{eventParent = Parent p 0, change = Cons{}}) <- lookupDescs eventUID] of+        [cons] -> findValue lookupDescs cons+        other  -> error $ "Unexpected set of descendants of " ++ show eventUID ++ ": Fun - " ++ show other++findValue :: (UID -> [EventWithId]) -> EventWithId -> String+findValue lookupDescs = go+  where+    go :: EventWithId -> String+    go EventWithId {eventUID = me, event = Event {change = ConsChar c}} = show c+    go EventWithId {eventUID = me, event = Event {change = Cons ar name}}+      | ar == 0 = unpack name+      | isAlpha (T.head name) =+        unpack name +++        " " +++        unwords+          (map go $+           sortOn+             (parentPosition . eventParent . event)+             [e | e@EventWithId {event = Event {change = Cons {}}} <- lookupDescs me])+      | ar == 1+      , [a] <- [e | e@EventWithId {event = Event {change = Cons {}}} <- lookupDescs me] = unpack name ++ go a+      | ar == 2+      , [a, b] <- sortOn (parentPosition . eventParent . event) [e | e@(event -> Event {change = Cons {}}) <- lookupDescs me] =+        unwords [go a, unpack name, go b]+    go EventWithId {eventUID, event = Event {change = Enter {}}}+      | [e] <- lookupDescs eventUID = go e+    go other = error $ show other++data RequestDetails = RD Int Explanation++data ReturnDetails+  = ReturnFun+  | ReturnCons { constructor :: Text, arity :: Word8, value :: String}++data Explanation+  = Observation String+  | Request RequestDetails+  | Return RequestDetails ReturnDetails++instance Show Explanation where+  show (Observation obs) = ""+  show (Request r) = "request " ++ showRequest r+  show (Return r val) = showReturn r val++showRequest (RD 0 (Observation name)) = unwords ["value of", name]+showRequest (RD 0 (Return (RD _ (Observation name)) ReturnFun)) = unwords ["arg of", name]+showRequest (RD 1 (Return (RD _ (Observation name)) ReturnFun)) = unwords ["result of", name]+showRequest (RD n (Return _ (ReturnCons name ar _))) = unwords ["arg", show n, "of constructor", unpack name ]++showReturn (RD p (Observation obs)) ReturnFun = unwords ["result of ", obs, "is a function"]+showReturn (RD p req) (ReturnCons name ar val) = unwords [show req, "=", val]++buildExplanation :: (UID -> EventWithId) -> (UID -> [EventWithId]) -> EventWithId -> Explanation+buildExplanation lookupEvent lookupDescs = go . event where+  go Event{eventParent = Parent p pos, change = Enter}+    | par <- go (event $ lookupEvent p)+    = Request (RD 0 par)+  go Event{eventParent = Parent p pos, change = Fun}+    | Request rd <- go (event $ lookupEvent p)+    = Return rd ReturnFun+  go Event{eventParent = Parent p pos, change = Cons ar name}+    | Request rd <- go (event $ lookupEvent p)+    , value <- findValue lookupDescs (lookupEvent p)+    = Return rd (ReturnCons name ar value)+++eitherFunOrCons Fun{} = True+eitherFunOrCons Cons {} = True+eitherFunOrCons _ = False++pattern FunOrCons <- (eitherFunOrCons -> True)++--------------------------------------------------------------------------------+-- Debug session+ debugSession :: Trace -> CompTree -> [Propositions] -> IO () debugSession trace tree ps =   case filter (not . isRootVertex) vs of@@ -118,7 +405,7 @@   , ps       :: [Propositions]   } -adbCommand, observeCommand, listCommand, exitCommand :: Command State+adbCommand, graphCommand, observeCommand, listCommand, exitCommand :: Command State adbCommand =   Command "adb" [] "Start algorithmic debugging." $ \case     [] -> Just $ \_ -> return $ Down adbFrame@@ -140,6 +427,17 @@       let regexp = makeRegex $ case args of [] -> ".*" ; _ -> unwords args       in Same <$ listStmts compTree regexp +graphCommand =+  Command "graph" ["regexp"]+    ("Show the computation graph of an expression." </>+     "Requires graphviz dotp.") $ \case+        regexp -> Just $ \State{..} -> Same <$ graphStmts (unwords regexp) compTree++eventsCommand =+  Command "events" [] "Print the Event trace (useful only for debugging Hoed)" $ \case+    [] -> Just $ \State{..} -> Same <$ printTrace trace+    _  -> Nothing+ exitCommand =   Command "exit" [] "Leave the debugging session." $ \case     [] -> Just $ \_ -> return (Up Nothing)@@ -149,6 +447,10 @@ mainLoopCommands =   sortOn name     [ adbCommand+#ifdef DEBUG+    , eventsCommand+#endif+    , graphCommand     , listCommand     , observeCommand     , exitCommand@@ -165,14 +467,14 @@  listStmts :: CompTree -> Regex -> IO () listStmts g regex =-  putStrLn $-  unlines $+  T.putStrLn $+  T.unlines $   snub $   map (stmtLabel . vertexStmt . G.root) $   selectVertices (\v -> matchLabel v && isRelevantToUser g v) g   where     matchLabel RootVertex = False-    matchLabel v          = match regex (stmtLabel $ vertexStmt v)+    matchLabel v          = match regex (unpack $ stmtLabel $ vertexStmt v)     snub = map head . List.group . sort  -- Restricted to statements for lambda functions or top level constants.@@ -207,6 +509,8 @@  -------------------------------------------------------------------------------- -- observe++ printStmts :: CompTree -> String -> IO () printStmts g regexp     | null vs_filtered  =@@ -228,7 +532,7 @@     show(vertexStmt $ G.root g) :     concat       [ "  where" :-        map ("    " ++) locals+        map (("    " ++) . unpack) locals       | not (null locals)]   where     locals =@@ -243,6 +547,43 @@               succs g (G.root g)           ] +--------------------------------------------------------------------------+-- graph+graphStmts :: String -> CompTree -> IO ()+graphStmts "" g = renderAndOpen g+graphStmts (makeRegex -> r) g = do+          let matches =+                map subGraphFromRoot $+                selectVertices (\v -> matchRegex r v && isRelevantToUser g v) g+          case matches of+            [one] -> renderAndOpen one+            _ ->+              putStrLn "More than one match, please select only one expression."++renderAndOpen g = do+  tempDir <- getTemporaryDirectory+  (tempFile, hTempFile) <- openTempFile tempDir "hoed.svg"+  hClose hTempFile+  cmd "dot" ["-Tsvg", "-o", tempFile] (showGraph g)+  _success <- openBrowser ("file:///" ++ tempFile)+  return ()++showGraph g = showWith g showVertex showArc+  where+    showVertex RootVertex = ("\".\"", "shape=none")+    showVertex v          = ("\"" ++ (escape . showCompStmt) v ++ "\"", "")+    showArc _ = ""+    showCompStmt = show . vertexStmt++cmd line args inp = do+  putStrLn $ unwords (line:args)+  (exit, stdout, stderr) <- readProcessWithExitCode line args inp+  unless (exit == ExitSuccess) $ do+    putStrLn $ "Failed with code: " ++ show exit+    putStrLn stdout+    putStrLn stderr+  return exit+ -------------------------------------------------------------------------------- -- algorithmic debugging @@ -330,8 +671,4 @@   unjudged' Right = False   unjudged' Wrong = False   unjudged' _     = True---- 
Debug/Hoed/EventForest.hs view
@@ -22,39 +22,40 @@ ) where import Debug.Hoed.Observe import Data.IntMap (IntMap)+import Data.Vector.Generic (ifoldl) import qualified Data.IntMap as IntMap  -- Searchable mapping from UID of the parent and position in list of siblings -- to a child event. -- type EventForest = [(UID, [(ParentPosition, Event)])]-type EventForest = IntMap [(ParentPosition, Event)]+type EventForest = IntMap [(ParentPosition, EventWithId)]  isRoot :: Event -> Bool isRoot e = case change e of Observe{} -> True; _ -> False -elems :: EventForest -> [[(ParentPosition, Event)]]+elems :: EventForest -> [[(ParentPosition, EventWithId)]] elems = IntMap.elems -addEvent :: EventForest -> Event -> EventForest-addEvent frt e-  | isRoot e  = frt+addEvent :: EventForest -> UID -> Event -> EventForest+addEvent frt uid e+  | isRoot e = frt   | otherwise = IntMap.insert i s frt   where i  = parentUID . eventParent $ e         p  = parentPosition . eventParent $ e         ms = IntMap.lookup i frt-        s  = case ms of Nothing   -> [(p,e)]-                        (Just s') -> (p,e) : s'+        s  = case ms of Nothing   -> [(p,EventWithId uid e)]+                        (Just s') ->  (p,EventWithId uid e) : s'   mkEventForest :: Trace -> EventForest-mkEventForest trc = foldl addEvent IntMap.empty trc+mkEventForest trc = ifoldl addEvent IntMap.empty trc -parentUIDLookup :: UID -> EventForest  -> [(ParentPosition,Event)]+parentUIDLookup :: UID -> EventForest  -> [(ParentPosition,EventWithId)] parentUIDLookup i frt = case IntMap.lookup i frt of   Nothing   -> []   (Just es) -> es -parentPosLookup :: ParentPosition -> [(ParentPosition,Event)] -> [Event]+parentPosLookup :: ParentPosition -> [(ParentPosition,EventWithId)] -> [EventWithId] parentPosLookup p = map snd . filter ((==p) . fst)  data InfixOrPrefix = Infix | Prefix@@ -62,13 +63,13 @@ data Location = Trunk | ArgumentOf Location | ResultOf Location | FieldOf Int Location   deriving Eq -instance Show Location where +instance Show Location where    show Trunk            = ""    show (ArgumentOf loc) = 'a' : show loc    show (ResultOf   loc) = 'r' : show loc    show (FieldOf n  loc) = 'f' : show n ++ show loc -type Visit a = Maybe Event -> Location -> a -> a+type Visit a = Maybe EventWithId -> Location -> a -> a  idVisit :: Visit a idVisit _ _ z = z@@ -82,30 +83,31 @@ -- particular ordering and we just return the applications (AppEvents) in the -- order we find them in the trace (i.e. evaluation order). -dfsChildren :: EventForest -> Event -> [Maybe Event]-dfsChildren frt e = case change e of+dfsChildren :: EventForest -> EventWithId -> [Maybe EventWithId]+dfsChildren frt e = case change (event e) of     Enter{}              -> manyByPosition 0 -- Should be Nothing?     (Cons l _)           -> foldl (\acc x -> acc ++ manyByPosition x) [] [0..(l-1)]+    ConsChar _           -> []     Observe{}            -> manyByPosition 0     Fun                  -> manyByPosition 0 ++ manyByPosition 1 -  where manyByPosition :: ParentPosition -> [Maybe Event]+  where manyByPosition :: ParentPosition -> [Maybe EventWithId]         manyByPosition pos = case filter (\(pos',_) -> pos == pos') cs of           [] -> [Nothing]           ts -> map (Just . snd) ts          -- Events in the frt that list our event as parent (in no particular order).-        cs :: [(ParentPosition,Event)]+        cs :: [(ParentPosition,EventWithId)]         cs = parentUIDLookup (eventUID e) frt           dfsFold :: InfixOrPrefix -> Visit a -> Visit a -> a -        -> Location -> (Maybe Event) -> EventForest -> a+        -> Location -> (Maybe EventWithId) -> EventForest -> a  dfsFold ip pre post z loc me frt    = post me loc $ case me of       Nothing -> z'-      (Just e) -> case change e of+      (Just e) -> case change (event e) of          Fun -> let [arg,res] = cs           in case ip of@@ -121,27 +123,27 @@    where z'  = pre me loc z -        cs :: [Maybe Event]+        cs :: [Maybe EventWithId]         cs = case me of (Just e) -> dfsChildren frt e; Nothing -> error "dfsFold filter failed"          csFold = foldl (\z'' (c,loc') -> dfsFold ip pre post z'' loc' c frt) z' -treeUIDs :: EventForest -> Event -> [UID]+treeUIDs :: EventForest -> EventWithId -> [UID] treeUIDs frt = (map eventUID) . eventsInTree frt  -- Given an event r, return depth first ordered list of events in the (sub)tree starting from r.-eventsInTree :: EventForest -> Event -> [Event]+eventsInTree :: EventForest -> EventWithId -> [EventWithId] eventsInTree frt r = reverse $ dfsFold Prefix add idVisit [] Trunk (Just r) frt   where add (Just e) _ es = e : es         add Nothing  _ es = es  -- Find all toplevel AppEvents for RootEvent r-topLevelApps :: EventForest -> Event -> [Event]+topLevelApps :: EventForest -> EventWithId -> [EventWithId] topLevelApps frt r = foldl appendApp []  $ dfsChildren frt r -appendApp :: [Event] -> Maybe Event -> [Event]+appendApp :: [EventWithId] -> Maybe EventWithId -> [EventWithId] appendApp z me = case me of   Nothing  -> z-  (Just e) -> case change e of Fun -> e : z-                               _   -> z+  (Just e) -> case change (event e) of Fun -> e : z+                                       _   -> z   
Debug/Hoed/Observe.lhs view
@@ -5,10 +5,16 @@ {-# LANGUAGE Rank2Types #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies  #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE CPP #-} @@ -90,9 +96,26 @@ \begin{code} import Prelude hiding (Right) import qualified Prelude+import Control.Concurrent.MVar import Control.Monad import Data.Array as Array+import qualified Data.HashTable.IO as H+import Data.IORef+import Data.List (sortOn)+import Data.Maybe+import Data.Monoid ((<>))+import Data.Proxy+import Data.Rope.Mutable (Rope, new', write, reset)+import Data.Strict.Tuple (Pair(..))+import Data.Text (Text, pack)+import qualified Data.Vector as V+import qualified Data.Vector.Generic as VG+import Data.Vector.Unboxed (Vector)+import Data.Vector.Unboxed.Deriving+import Data.Vector.Unboxed.Mutable (MVector)+import Data.Word import Debug.Hoed.Fields+import Debug.Trace  import GHC.Generics @@ -114,13 +137,158 @@  \end{code} +%************************************************************************+%*                                                                      *+\subsection{Event stream}+%*                                                                      *+%************************************************************************++Trival output functions+ \begin{code}-infixl 9 <<++type UID = Int++data Event = Event { eventParent :: {-# UNPACK #-} !Parent+                   , change      ::                !Change  }+        deriving (Eq,Generic)++data EventWithId = EventWithId {eventUID :: !UID, event :: !Event}++data Change+        = Observe          !Text+        | Cons     !Word8  !Text+        | ConsChar         !Char+        | Enter+        | Fun+        deriving (Eq, Show,Generic)++type ParentPosition = Word8++data Parent = Parent+        { parentUID      :: !UID            -- my parents UID+        , parentPosition :: !ParentPosition -- my branch number (e.g. the field of a data constructor)+        } deriving (Eq,Generic)++instance Show Event where+  show e = (show . change $ e) ++ " (" ++ (show . eventParent $ e) ++ ")"++instance Show EventWithId where+  show (EventWithId uid e) = (show uid) ++ ": " ++ (show . change $ e) ++ " (" ++ (show . eventParent $ e) ++ ")"++instance Show Parent where+  show p = "P " ++ (show . parentUID $ p) ++ " " ++ (show . parentPosition $ p)++root = Parent (-1) 0++isRootEvent :: Event -> Bool+isRootEvent e = case change e of Observe{} -> True; _ -> False \end{code} +\begin{code}+type Trace = Vector Event++endEventStream :: IO Trace+endEventStream = do+  (stringsCount :!: stringsHashTable) <- takeMVar strings+  unsortedStrings <- H.toList stringsHashTable+  putMVar strings . (0 :!:) =<< H.new+  let stringsTable = V.unsafeAccum (\_ -> id) (V.replicate stringsCount (error "uninitialized")) [(i,s) | (s,i) <- unsortedStrings]+  writeIORef stringsLookupTable stringsTable+  reset (Proxy :: Proxy Vector) events++sendEvent :: Int -> Parent -> Change -> IO ()+sendEvent nodeId !parent !change = do+  write events nodeId (Event parent change)++lookupOrAddString s = do+  (stringsCount :!: stringsTable) <- takeMVar strings+  value <- H.lookup stringsTable s+  (count',res) <- case value of+            Just x -> return (stringsCount, x)+            Nothing -> H.insert stringsTable s stringsCount >> return (stringsCount+1, stringsCount)+  putMVar strings (count' :!: stringsTable)+  return res++-- Global store of unboxed events.+-- Since we cannot unbox Strings, these are represented as references to the+--  strings table+{-# NOINLINE events #-}+events :: Rope IO MVector Event+events = unsafePerformIO $ do+  rope <- new' 10000  -- size of the lazy vectors internal to the rope structure+  return rope++{-# NOINLINE strings #-}+strings :: MVar(Pair Int (H.CuckooHashTable Text Int))+strings = unsafePerformIO $ do+  h <- H.newSized 100000  -- suggested capacity for the hash table+  newMVar (0 :!: h)++{-# NOINLINE stringsLookupTable #-}+stringsLookupTable :: IORef (V.Vector Text)+stringsLookupTable = unsafePerformIO $ newIORef  mempty++lookupString id = unsafePerformIO $ (V.! id) <$> readIORef  stringsLookupTable++derivingUnbox "Change"+    [t| Change -> (Word8, Word8, Int) |]+    [| \case+            Observe  s -> (0,0,unsafePerformIO(lookupOrAddString s))+            Cons c   s -> (1,c,unsafePerformIO(lookupOrAddString s))+            ConsChar c -> (2,0,fromEnum c)+            Enter      -> (3,0,0)+            Fun        -> (4,0,0)+     |]+    [| \case (0,_,s) -> Observe (lookupString s)+             (1,c,s) -> Cons c  (lookupString s)+             (2,_,c) -> ConsChar (toEnum c)+             (3,_,_) -> Enter+             (4,_,_) -> Fun+     |]++derivingUnbox "Parent"+    [t| Parent -> (UID, ParentPosition) |]+    [| \ (Parent a b) -> (a,b) |]+    [| \ (a,b) -> Parent a b |]++derivingUnbox "Event"+    [t| Event -> (Parent, Change) |]+    [| \(Event a b) -> (a,b) |]+    [| \ (a,b) -> Event a b |]++\end{code}+  %************************************************************************ %*                                                                      *+\subsection{unique name supply code}+%*                                                                      *+%************************************************************************++Use the single threaded version++\begin{code}++initUniq :: IO ()+initUniq = writeIORef uniq 1++getUniq :: IO UID+getUniq = atomicModifyIORef' uniq (\n -> (n+1,n))++peepUniq :: IO UID+peepUniq = readIORef uniq++-- locals+{-# NOINLINE uniq #-}+uniq :: IORef UID+uniq = unsafePerformIO $ newIORef 0++\end{code}+++%************************************************************************+%*                                                                      * \subsection{GDM Generics} %*                                                                      * %************************************************************************@@ -128,8 +296,17 @@ The generic implementation of the observer function.  \begin{code}+-- | A type class for observable values.+--+--    * For 'Generic' datatypes it can be derived automatically.+--    * For opaque datatypes, use 'observeOpaque' or rely on the catch-all @<?>@ instance.+--    * Custom implementations can exclude one or more fields from the observation:+--+--    @ instance (Observable a, Observable b) => Observable (excluded, a,b) where+--        observe (excluded,a,b) = send "(,,)" (return (,,) excluded << a << b)+--    @ class Observable a where-        observer  :: a -> Parent -> a +        observer  :: a -> Parent -> a         default observer :: (Generic a, GObservable (Rep a)) => a -> Parent -> a         observer x c = to (gdmobserver (from x) c) @@ -140,7 +317,7 @@ class GObservable f where         gdmobserver :: f a -> Parent -> f a         gdmObserveArgs :: f a -> ObserverM (f a)-        gdmShallowShow :: f a -> String+        gdmShallowShow :: f a -> Text  constrainBase :: (Show a, Eq a) => a -> a -> a constrainBase x c | x == c = x@@ -190,7 +367,7 @@ instance (GObservable a, Constructor c) => GObservable (M1 C c a) where  gdmobserver m1 = send (gdmShallowShow m1) (gdmObserveArgs m1)  gdmObserveArgs (M1 x) = do {x' <- gdmObserveArgs x; return (M1 x')}- gdmShallowShow = conName+ gdmShallowShow = pack . conName  -- Unit: used for constructors without arguments instance GObservable U1 where@@ -254,7 +431,7 @@  The Haskell Base types  \begin{code}-instance Observable Int     where observer  = observeBase +instance Observable Int     where observer  = observeBase                                   constrain = constrainBase instance Observable Bool    where observer  = observeBase                                   constrain = constrainBase@@ -264,8 +441,11 @@                                   constrain = constrainBase instance Observable Double  where observer  = observeBase                                   constrain = constrainBase-instance Observable Char    where observer  = observeBase-                                  constrain = constrainBase+instance Observable Char    where+  observer lit cxt = seq lit $ unsafeWithUniq $ \node -> do+    sendEvent node cxt (ConsChar lit)+    return lit+  constrain = constrainBase instance Observable ()      where observer  = observeOpaque "()"                                   constrain = constrainBase @@ -275,9 +455,9 @@ -- we evalute to WHNF, and not further.  observeBase :: (Show a) => a -> Parent -> a-observeBase lit cxt = seq lit $ send (show lit) (return lit) cxt+observeBase lit cxt = seq lit $ send (pack $ show lit) (return lit) cxt -observeOpaque :: String -> a -> Parent -> a+observeOpaque :: Text -> a -> Parent -> a observeOpaque str val cxt = seq val $ send str (return val) cxt \end{code} @@ -337,7 +517,7 @@  \begin{code} instance Observable SomeException where-  observer e = send ("<Exception> " ++ show e) (return e)+  observer e = send ("<Exception> " <> pack(show e)) (return e)   constrain = undefined  -- instance Observable ErrorCall where@@ -356,13 +536,6 @@ %*                                                                      * %************************************************************************ -MF: why/when do we need these types?-\begin{code}-type Observing a = a -> a--newtype Observer = O (forall a . (Observable a) => String -> a -> a)-\end{code}-  %************************************************************************ %*                                                                      *@@ -374,7 +547,7 @@ for placing numbers on sub-observations.  \begin{code}-newtype ObserverM a = ObserverM { runMO :: Int -> Int -> (a,Int) }+newtype ObserverM a = ObserverM { runMO :: Int -> Word8 -> (a,Word8) }  instance Functor ObserverM where     fmap  = liftM@@ -411,6 +584,7 @@ (<<) :: (Observable a) => ObserverM (a -> b) -> a -> ObserverM b -- fn << a = do { fn' <- fn ; a' <- thunk a ; return (fn' a') } fn << a = gdMapM (thunk observer) fn a+infixl 9 <<  gdMapM :: (Monad m)         => (a -> m a)  -- f@@ -444,7 +618,7 @@ -- 'observe' can also observe functions as well a structural values. --  {-# NOINLINE gobserve #-}-gobserve :: (a->Parent->a) -> String -> a -> (a,Int)+gobserve :: (a->Parent->a) -> Text -> a -> (a,Int) gobserve f name a = generateContext f name a  {- | @@ -477,7 +651,7 @@ @ -} {-# NOINLINE observe #-}-observe ::  (Observable a) => String -> a -> a+observe ::  (Observable a) => Text -> a -> a observe lbl = fst . (gobserve observer lbl)  {- This gets called before observer, allowing us to mark@@ -505,16 +679,16 @@ \end{code}  \begin{code}-generateContext :: (a->Parent->a) -> String -> a -> (a,Int)+generateContext :: (a->Parent->a) -> Text -> a -> (a,Int) generateContext f {- tti -} label orig = unsafeWithUniq $ \node ->-     do sendEvent node (Parent 0 0) (Observe label)+     do sendEvent node root (Observe label)         return (observer_ f orig (Parent                       { parentUID      = node                       , parentPosition = 0                       })                , node) -send :: String -> ObserverM a -> Parent -> a+send :: Text -> ObserverM a -> Parent -> a send consLabel fn context = unsafeWithUniq $ \ node ->      do { let (r,portCount) = runMO fn node 0         ; sendEvent node context (Cons portCount consLabel)@@ -548,106 +722,10 @@ \end{code}  -%************************************************************************-%*                                                                      *-\subsection{Event stream}-%*                                                                      *-%************************************************************************ -Trival output functions--\begin{code}-type Trace = [Event]--data Event = Event-                { eventUID     :: !UID      -- my UID-                , eventParent  :: !Parent-                , change       :: !Change-                }-        deriving (Eq,Generic)--data Change-        = Observe       !String-        | Cons    !Int  !String-        | Enter-        | Fun-        deriving (Eq, Show,Generic)--type ParentPosition = Int--data Parent = Parent-        { parentUID      :: !UID            -- my parents UID-        , parentPosition :: !ParentPosition -- my branch number (e.g. the field of a data constructor)-        } deriving (Eq,Generic)--instance Show Event where-  show e = (show . eventUID $ e) ++ ": " ++ (show . change $ e) ++ " (" ++ (show . eventParent $ e) ++ ")"--instance Show Parent where-  show p = "P " ++ (show . parentUID $ p) ++ " " ++ (show . parentPosition $ p)--root = Parent 0 0--isRootEvent :: Event -> Bool-isRootEvent e = case change e of Observe{} -> True; _ -> False--startEventStream :: IO ()-startEventStream = writeIORef events []--endEventStream :: IO Trace-endEventStream =-        do { es <- readIORef events-           ; writeIORef events badEvents -           ; return es-           }--sendEvent :: Int -> Parent -> Change -> IO ()-sendEvent nodeId parent change =-        do { let !event = Event nodeId parent change-           ; atomicModifyIORef' events (\es -> (event : es, ()))-           }---- local-events :: IORef Trace-events = unsafePerformIO $ newIORef badEvents--badEvents :: Trace-badEvents = error "Bad Event Stream"--\end{code}-  %************************************************************************ %*                                                                      *-\subsection{unique name supply code}-%*                                                                      *-%************************************************************************--Use the single threaded version--\begin{code}-type UID = Int--initUniq :: IO ()-initUniq = writeIORef uniq 1--getUniq :: IO UID-getUniq = atomicModifyIORef' uniq (\n -> (n+1,n))--peepUniq :: IO UID-peepUniq = readIORef uniq---- locals-{-# NOINLINE uniq #-}-uniq :: IORef UID-uniq = unsafePerformIO $ newIORef 1--\end{code}----%************************************************************************-%*                                                                      * \subsection{Global, initualizers, etc} %*                                                                      * %************************************************************************@@ -682,7 +760,7 @@  handleExc :: Parent -> SomeException -> IO a -- handleExc context exc = return (send "throw" (return throw << exc) context)-handleExc context exc = return (send (show exc) (return (throw exc)) context)+handleExc context exc = return (send (pack $ show exc) (return (throw exc)) context) \end{code}  %************************************************************************
Debug/Hoed/Prop.hs view
@@ -1,19 +1,22 @@+{-# LANGUAGE ViewPatterns #-} -- This file is part of the Haskell debugger Hoed. -- -- Copyright (c) Maarten Faddegon 2015-2016 -{-# LANGUAGE DefaultSignatures, TypeOperators, FlexibleContexts, FlexibleInstances, StandaloneDeriving, CPP, DeriveGeneric #-}+{-# LANGUAGE OverloadedLists, DefaultSignatures, TypeOperators, FlexibleContexts, FlexibleInstances, StandaloneDeriving, CPP, DeriveGeneric #-}  module Debug.Hoed.Prop where -- ( judge -- , Propositions(..) -- ) where-import Debug.Hoed.Observe(Observable(..),Trace(..),UID,Event(..),Change(..),ourCatchAllIO,evaluate,eventParent,parentPosition)+import qualified Data.Vector.Generic as VG+import Debug.Hoed.Observe(EventWithId(..), Observable(..),Trace(..),UID,Event(..),Change(..),ourCatchAllIO,evaluate,eventParent,parentPosition) import Debug.Hoed.Render(CompStmt(..),noNewlines) import Debug.Hoed.CompTree(CompTree,Vertex(..),Graph(..),vertexUID,vertexRes,replaceVertex,getJudgement,setJudgement) import Debug.Hoed.EventForest(EventForest,mkEventForest,dfsChildren) import Debug.Hoed.Serialize import qualified Data.IntMap as M+import qualified Data.Text as T import Prelude hiding (Right) import Data.Graph.Libgraph(Judgement(..),AssistedMessage(..),mapGraph) import System.Directory(createDirectoryIfMissing)@@ -108,7 +111,7 @@ lookupPropositions :: [Propositions] -> Vertex -> Maybe Propositions lookupPropositions _ RootVertex = Nothing lookupPropositions ps v = lookupWith funName lbl ps-  where lbl = (stmtLabel . vertexStmt) v+  where lbl = (T.unpack . stmtLabel . vertexStmt) v  lookupWith :: Eq a => (b->a) -> a -> [b] -> Maybe b lookupWith f x ys = case filter (\y -> f y == x) ys of@@ -328,14 +331,9 @@   writeFile sourceFile prgm   return prgm   where -  prgm = generate handler prop ms trc (getEventFromMap $ eventMap trc) i f+  prgm = generate handler prop ms trc (\i -> EventWithId i (trc VG.! i)) i f   i    = (stmtIdentifier . vertexStmt) v-  f    = (stmtLabel . vertexStmt) v---getEventFromMap m j = fromJust $ M.lookup j m--eventMap trc = M.fromList $ map (\e -> (eventUID e, e)) trc+  f    = (T.unpack . stmtLabel . vertexStmt) v  mkPropRes :: Proposition -> ExitCode -> String -> PropRes mkPropRes prop (ExitFailure _) out        = Error prop out@@ -414,7 +412,7 @@  ------------------------------------------------------------------------------------------------------------------------ -generate :: UnevalHandler -> Proposition -> [Module ] -> Trace -> (UID->Event) -> UID -> String -> String+generate :: UnevalHandler -> Proposition -> [Module ] -> Trace -> (UID->EventWithId) -> UID -> String -> String generate handler prop ms trc getEvent i f = generateHeading prop ms ++ generateMain handler prop trc getEvent i f  generateHeading :: Proposition -> [Module] -> String@@ -441,7 +439,7 @@ generateImport :: Module -> String generateImport m =  "import " ++ (moduleName m) ++ "\n" -generateMain :: UnevalHandler -> Proposition -> Trace -> (UID->Event) -> UID -> String -> String+generateMain :: UnevalHandler -> Proposition -> Trace -> (UID->EventWithId) -> UID -> String -> String generateMain handler prop trc getEvent i f   = "main = Hoed.runOstore \"" ++ (propName prop) ++"\" $ "             ++ propVarBind handler (foldl accSig ((propName prop) ++ " ",unevalState handler) (signature prop)) prop@@ -465,13 +463,13 @@     cf :: PropVarGen String     cf | handler == RestrictedBottom             = foldl1 (liftPV $ \acc c -> acc ++ " " ++ c)-             [ propVarReturn $ "(" ++ genConAp (length args) f+             ([ propVarReturn $ "(" ++ genConAp (length args) f              , generateRes (unevalHandler handler) trc getEvent i              , propVarReturn $ ")"-             ]+             ] :: [PropVarGen String])        | otherwise = propVarReturn f -generateRes :: (PropVarGen String) -> Trace -> (UID -> Event) -> UID -> PropVarGen String+generateRes :: (PropVarGen String) -> Trace -> (UID -> EventWithId) -> UID -> PropVarGen String generateRes unevalGen trc getEvent i  | areFun mres = (propVarReturn " {- generateRes -} ") `pvCat`                  (generateRes unevalGen trc getEvent (eventUID . head . justFuns $ mres)) -- (*)@@ -486,10 +484,10 @@  frt = (mkEventForest trc)  e   = getEvent i -generateRes' :: PropVarGen String -> Trace -> (UID->Event) -> Maybe Event -> PropVarGen String+generateRes' :: PropVarGen String -> Trace -> (UID->EventWithId) -> Maybe EventWithId -> PropVarGen String generateRes' unevalGen trc getEvent Nothing = unevalGen generateRes' unevalGen trc getEvent (Just e)- | change e == Fun = + | change (event e) == Fun =    case dfsChildren frt e of     [_,_    ,_,mr] ->      generateRes' unevalGen trc getEvent mr@@ -502,7 +500,7 @@  where  frt = (mkEventForest trc) -generateArgs :: (PropVarGen String) -> Trace -> (UID -> Event) -> UID -> [PropVarGen String]+generateArgs :: (PropVarGen String) -> Trace -> (UID -> EventWithId) -> UID -> [PropVarGen String] generateArgs unevalGen trc getEvent i =  (propVarReturn " {- generateArgs -} ") `pvCat`  pvArg `pvCat` (propVarReturn $ " {- more: " ++ show mres ++ " -} ") @@ -522,21 +520,22 @@ nothingOrArg Nothing = True nothingOrArg (Just e) = isArg e +noArg :: [Maybe a] -> Bool noArg [Nothing] = True noArg _         = False -areFun :: [Maybe Event] -> Bool+areFun :: [Maybe EventWithId] -> Bool areFun (_:e:_) = isJustFun e areFun _       = False -justFuns :: [Maybe Event] -> [Event]-justFuns = map fromJust . filter isJustFun +justFuns :: [Maybe EventWithId] -> [EventWithId]+justFuns = map fromJust . filter isJustFun -isJustFun :: Maybe Event -> Bool-isJustFun (Just e) = change e == Fun+isJustFun :: Maybe EventWithId -> Bool+isJustFun (Just e) = change (event e) == Fun isJustFun Nothing  = False -generateFunMap :: (PropVarGen String) -> Trace -> (UID -> Event) -> [Event] -> PropVarGen String+generateFunMap :: (PropVarGen String) -> Trace -> (UID -> EventWithId) -> [EventWithId] -> PropVarGen String generateFunMap unevalGen trc getEvent funs   | length funs > 0 = caseOf `pvCat` (pvConcat cases') `pvCat` esac  | otherwise       = propVarReturn "{- a fun without applications? -}"@@ -548,7 +547,7 @@  cases  = map (\fun -> generateCase unevalGen trc getEvent fun) funs  cases' = intersperse (propVarReturn "; ") cases -generateCase :: (PropVarGen String) -> Trace -> (UID -> Event) -> Event -> PropVarGen String+generateCase :: (PropVarGen String) -> Trace -> (UID -> EventWithId) -> EventWithId -> PropVarGen String generateCase unevalGen trc getEvent fun =  (propVarReturn $ " {- CASE " ++ show fun ++ " -} ") `pvCat`  case args of @@ -571,7 +570,7 @@ isJustRes (Just e) = isRes e isJustRes Nothing  = False -hasParentPos i = (==i) . parentPosition . eventParent+hasParentPos i = (==i) . parentPosition . eventParent . event  pvCat :: PropVarGen String -> PropVarGen String -> PropVarGen String pvCat = liftPV (++)@@ -579,18 +578,18 @@ pvConcat :: [PropVarGen String] -> PropVarGen String pvConcat = foldl pvCat (propVarReturn "") -moreArgs :: PropVarGen String -> Trace -> (UID->Event) -> Maybe Event -> [PropVarGen String]+moreArgs :: PropVarGen String -> Trace -> (UID->EventWithId) -> Maybe EventWithId -> [PropVarGen String] moreArgs _ trc getEvent Nothing = [] moreArgs unevalGen trc getEvent (Just e)-  | change e == Fun = generateArgs unevalGen trc getEvent (eventUID e)+  | change (event e) == Fun = generateArgs unevalGen trc getEvent (eventUID e)   | otherwise       = [] -generateExpr :: PropVarGen String -> Trace -> (UID -> Event) -> EventForest -> Maybe Event -> PropVarGen String+generateExpr :: PropVarGen String -> Trace -> (UID -> EventWithId) -> EventForest -> Maybe EventWithId -> PropVarGen String generateExpr unevalGen _ _ _ Nothing    = unevalGen generateExpr unevalGen trc getEvent frt (Just e) =    -- (propVarReturn $ "{- generateExpr " ++ show e ++ "-}") `pvCat` -  case change e of-  (Cons _ s) -> let s' = if isAlpha (head s) then s else "(" ++ s ++ ")"+  case change (event e) of+  (Cons _ (T.unpack -> s)) -> let s' = if isAlpha (head s) then s else "(" ++ s ++ ")"                 in liftPV (++) ( foldl (liftPV $ \acc c -> acc ++ " " ++ c)                                  (propVarReturn ("(" ++ s')) cs                                ) @@ -653,7 +652,7 @@             | any (== (Just False)) mbs = Just False             | all (== (Just True))  mbs = Just True             | otherwise                 = Nothing-            where mbs = [(catchGEq x y) `seq` (catchGEq x y), (catchGEq x' y') `seq` (catchGEq x' y')]+            where mbs = [(catchGEq x y) `seq` (catchGEq x y), (catchGEq x' y') `seq` (catchGEq x' y')] :: [Maybe Bool]  -- Unit: used for constructors without arguments instance GParEq U1 where
Debug/Hoed/Render.hs view
@@ -1,6 +1,11 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE ImplicitParams    #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLists   #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} -- This file is part of the Haskell debugger Hoed.@@ -19,16 +24,34 @@ ,sortOn ) where import           Control.DeepSeq+import           Control.Exception        (assert)+import           Control.Monad.Primitive+import           Control.Monad.ST+import           Control.Monad.Trans.Reader+import           Control.Monad.Trans.State.Strict import           Data.Array               as Array import           Data.Char                (isAlpha)-import           Data.List                (nub, sort)-import           Data.Strict.Tuple+import           Data.Coerce+import           Data.Hashable+import           Data.List                (nub, sort, unfoldr)+import qualified Data.HashTable.ST.Cuckoo as H+import           Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IntMap+import           Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import           Data.Primitive.MutVar+import           Data.Text (Text, pack, unpack)+import qualified Data.Text as T+import qualified Data.Vector as V+import qualified Data.Vector.Generic as VG+import           Data.Word import           Debug.Hoed.Compat import           Debug.Hoed.Observe+import           GHC.Exts(IsList(..)) import           GHC.Generics-import           Prelude                  hiding (lookup)-import           Text.PrettyPrint.FPretty hiding (sep, (<$>))-import           Text.Read+import           Text.PrettyPrint.FPretty hiding (sep, (<$>), text)+import qualified Text.PrettyPrint.FPretty as FPretty+import           Text.Read ()   ------------------------------------------------------------------------@@ -41,7 +64,7 @@ -- event that starts the observation. And stmtUIDs is the list of -- UIDs of all events that form the statement. -data CompStmt = CompStmt { stmtLabel      :: !String+data CompStmt = CompStmt { stmtLabel      :: !Text                          , stmtIdentifier :: !UID                          , stmtDetails    :: !StmtDetails                          }@@ -53,21 +76,24 @@ instance Ord CompStmt where   compare c1 c2 = compare (stmtIdentifier c1) (stmtIdentifier c2) +instance Hashable CompStmt where+  hashWithSalt s cs = hashWithSalt s (stmtIdentifier cs)+ data StmtDetails-  = StmtCon { stmtCon :: !String-           ,  stmtPretty :: !String}-  | StmtLam { stmtLamArgs :: ![String]-           ,  stmtLamRes :: !String-           ,  stmtPretty :: !String}+  = StmtCon { stmtCon :: Hashed Text+           ,  stmtPretty :: Hashed Text}+  | StmtLam { stmtLamArgs :: [Hashed Text]+           ,  stmtLamRes :: Hashed Text+           ,  stmtPretty :: Hashed Text}   deriving (Generic)  instance NFData StmtDetails -stmtRes :: CompStmt -> String-stmtRes = stmtPretty . stmtDetails+stmtRes :: CompStmt -> Text+stmtRes = unhashed . stmtPretty . stmtDetails  instance Show CompStmt where-  show = stmtRes+  show = unpack . stmtRes   showList eqs eq = unlines (map show eqs) ++ eq  noNewlines :: String -> String@@ -83,32 +109,35 @@ -- Render equations from CDS set  renderCompStmts :: (?statementWidth::Int) => CDSSet -> [CompStmt]-renderCompStmts = concatMap renderCompStmt+renderCompStmts cdss = runMemoM $ concat <$> mapM renderCompStmt cdss  -- renderCompStmt: an observed function can be applied multiple times, each application -- is rendered to a computation statement -renderCompStmt :: (?statementWidth::Int) => CDS -> [CompStmt]-renderCompStmt (CDSNamed name uid set) = statements-  where statements :: [CompStmt]-        statements   = concatMap (renderNamedTop name uid) output-        output       = cdssToOutput set+renderCompStmt :: (?statementWidth::Int) => CDS -> MemoM [CompStmt]+renderCompStmt (CDSNamed name uid set) = do+        let output = cdssToOutput set+        concat <$> mapM (renderNamedTop name uid) output  renderCompStmt other = error $ show other -renderNamedTop :: (?statementWidth::Int) => String -> UID -> Output -> [CompStmt]-renderNamedTop name observeUid (OutData cds) = map f pairs+prettySet cds = prettySet_noid(coerce cds)++prettySet_noid :: (?statementWidth::Int) => [CDSsansUID] -> MemoM(Hashed Text)+prettySet_noid = MemoM . memo (prettyW . renderSet . coerce)++renderNamedTop :: (?statementWidth::Int) => Text -> UID -> Output -> MemoM [CompStmt]+renderNamedTop name observeUid (OutData cds) = mapM f pairs   where     f (args, res, Just i) =-      CompStmt name i $-      StmtLam-        (map (prettyW . renderSet) args)-        (prettyW $ renderSet res)-        (prettyW $ renderNamedFn name (args, res))+      CompStmt name i <$>+      (StmtLam <$> mapM prettySet args <*>+       prettySet res <*>+       pure (prettyW $ renderNamedFn name (args, res)))     f (_, cons, Nothing) =-      CompStmt name observeUid $-      StmtCon (prettyW $ renderSet cons)-              (prettyW $ renderNamedCons name cons)+      CompStmt name observeUid <$>+      (StmtCon <$> prettySet cons <*>+       pure (prettyW $ renderNamedCons name cons))     pairs = (nubSorted . sortOn argAndRes) pairs'     pairs' = findFn [cds]     argAndRes (arg, res, _) = (arg, res)@@ -126,96 +155,97 @@ -- %*                                                                   * -- %************************************************************************ -data CDS = CDSNamed      !String !UID !CDSSet-         | CDSCons       !UID    !String   ![CDSSet]-         | CDSFun        !UID              !CDSSet !CDSSet+data CDS = CDSNamed      !Text !UID    !CDSSet+         | CDSCons       !UID  !Text   ![CDSSet]+         | CDSFun        !UID  !CDSSet !CDSSet          | CDSEntered    !UID          | CDSTerminated !UID+         | CDSChar       !Char   -- only used internally in eventsToCDS          | CDSString     !String -- only used internally in eventsToCDS         deriving (Show,Eq,Ord,Generic)  instance NFData CDS  normalizeCDS :: CDS -> CDS-normalizeCDS (CDSString s) = CDSCons 0 (show s) []+normalizeCDS (CDSString s) = CDSCons 0 (pack $ show s) []+normalizeCDS (CDSChar   s) = CDSCons 0 (pack $ show s) [] normalizeCDS other = other type CDSSet = [CDS] -eventsToCDS :: [Event] -> CDSSet-eventsToCDS pairs = force $ getChild 0 0-   where--     res = (!) out_arr--     bnds = (0, length pairs)+-- Monomorphized [Parent] for compactness+data ParentList = ParentCons !Int !Word8 ParentList | ParentNil+instance IsList ParentList where+  type Item ParentList = Parent+  toList = unfoldr (\case ParentNil -> Nothing ; ParentCons pp pc t -> Just (Parent pp pc,t))+  fromList = foldr (\(Parent pp pc) t -> ParentCons pp pc t) ParentNil -     cons !t !h = h : t+eventsToCDS :: Trace -> CDSSet+eventsToCDS pairs = getChild (-1) 0+   where -     mid_arr :: Array Int [Pair Int CDS]-     mid_arr = accumArray cons [] bnds-                [ (pnode, (pport :!: res node))-                | (Event node (Parent pnode pport) change) <- pairs-                , change /= Enter-                ]+     -- res i = out_arr VG.! i+     res i = getNode'' i (change (pairs VG.! i)) -     out_arr = array bnds       -- never uses 0 index-                [ (node,getNode'' node e change)-                | e@(Event node _ change) <- pairs-                ]+     mid_arr :: V.Vector ParentList+     mid_arr = VG.unsafeAccumulate+                  (\i (Parent pp pc) -> ParentCons pp pc i)+                  (V.replicate (VG.length pairs) ParentNil)+                  ( VG.map (\(node, Event (Parent pnode pport) _) ->+                              (pnode+1, Parent node pport))+                  $ VG.filter (\(_,e) -> change e /= Enter)+                  $ VG.convert+                  $ VG.indexed pairs) -     getNode'' ::  Int -> Event -> Change -> CDS-     getNode'' node _e change =+     getNode'' ::  Int -> Change -> CDS+     getNode'' node change =        case change of         Observe str         -> let chd = normalizeCDS <$> getChild node 0                                in CDSNamed str (getId chd node) chd         Enter               -> CDSEntered node         Fun                 -> CDSFun node (normalizeCDS <$> getChild node 0)                                            (normalizeCDS <$> getChild node 1)-        (Cons portc cons)+        ConsChar char       -> CDSChar char+        Cons portc cons                             -> simplifyCons node cons-                                 [getChild node n | n <- [0 .. portc - 1]]+                                 [ getChild node (fromIntegral n)+                                 | n <- [0::Int .. fromIntegral portc - 1]]       getId []                 i  = i      getId (CDSFun i _ _:_) _    = i      getId (_:cs)             i  = getId cs i -     getChild :: Int -> Int -> CDSSet+     getChild :: Int -> Word8 -> CDSSet      getChild pnode pport =-       [ content-       | pport' :!: content <- (!) mid_arr pnode+       [ res content+       | Parent content pport' <- toList $ mid_arr VG.! succ pnode        , pport == pport'        ] -simplifyCons :: UID -> String -> [CDSSet] -> CDS+simplifyCons :: UID -> Text -> [CDSSet] -> CDS simplifyCons _ "throw" [[CDSCons _ "ErrorCall" set]]   = CDSCons 0 "error" set-simplifyCons _ ":" [[CDSCons _ (matchChar -> Just !ch) []], [CDSCons _ "[]" []]]+simplifyCons _ ":" [[CDSChar !ch], [CDSCons _ "[]" []]]   = CDSString [ch]-simplifyCons _ ":" [[CDSCons _ (matchChar -> Just !ch) []], [CDSString s]]+simplifyCons _ ":" [[CDSChar !ch], [CDSString s]]   = CDSString (ch:s) simplifyCons uid con xx = CDSCons uid con (map (map normalizeCDS) xx) -matchChar :: [Char] -> Maybe Char-matchChar ['\'', ch ,'\''] = Just ch-matchChar special@['\'', _, _ ,'\''] = readMaybe special-matchChar _ = Nothing--render  :: Int -> Bool -> CDS -> Doc+render :: Int -> Bool -> CDS -> Doc render prec par (CDSCons _ ":" [cds1,cds2]) =         if par && not needParen         then doc -- dont use paren (..) because we dont want a grp here!         else paren needParen doc    where-        doc = grp (sep <> renderSet' 5 False cds1 <> " : ") <>+        doc = grp (renderSet' 5 False cds1 <> text " : ") <>               renderSet' 4 True cds2         needParen = prec > 4-render _prec _par (CDSCons _ "," cdss) | not (null cdss) =-        nest 2 ("(" <> foldl1 (\ a b -> a <> ", " <> b)+render prec par (CDSCons _ "," cdss) | length cdss > 0 =+        nest 2 (text "(" <> foldl1 (\ a b -> a <> text ", " <> b)                             (map renderSet cdss) <>-                ")")+                text ")") render prec _par (CDSCons _ name cdss)-  | _:_ <- name-  , (not . isAlpha . head) name && length cdss > 1 = -- render as infix+  | not (T.null name)+  , (not . isAlpha . T.head) name && length cdss > 1 = -- render as infix         paren (prec /= 0)                   (grp                     (renderSet' 10 False (head cdss)@@ -245,13 +275,13 @@ renderSet = renderSet' 0 False  renderSet' :: Int -> Bool -> CDSSet -> Doc-renderSet' _ _      [] = "_"-renderSet' prec par [cons@CDSCons {}]    = render prec par cons-renderSet' _prec _par cdss                   =-         "{ " <> foldl1 (\ a b -> a <> line <>-                                    ", " <> b)+renderSet' _ _      [] = text "_"+renderSet' prec par [cons@(CDSCons {})]    = render prec par cons+renderSet' prec par cdss                   =+        nest 0 (text "{ " <> foldl1 (\ a b -> a <> line <>+                                    text ", " <> b)                                     (map renderFn pairs) <>-                line <> "}"+                line <> text "}")     where         findFn_noUIDs :: CDSSet -> [([CDSSet],CDSSet)]@@ -265,21 +295,21 @@ renderFn :: ([CDSSet],CDSSet) -> Doc renderFn (args, res)         = grp  (nest 3-                ("\\ " <>+                (text "\\ " <>                  foldr (\ a b -> nest 0 (renderSet' 10 False a) <> sp <> b)                        nil-                       args <> sep <>-                 "-> " <> renderSet res+                       args <> softline <>+                 text "-> " <> renderSet' 0 False res                 )                ) -renderNamedCons :: String -> CDSSet -> Doc+renderNamedCons :: Text -> CDSSet -> Doc renderNamedCons name cons   = text name <> nest 2      ( sep <> grp (text "= " <> renderSet cons)      ) -renderNamedFn :: String -> ([CDSSet],CDSSet) -> Doc+renderNamedFn :: Text -> ([CDSSet],CDSSet) -> Doc renderNamedFn name (args,res)   = text name <> nest 2      ( sep <> foldr (\ a b -> grp (renderSet' 10 False a) <> sep <> b) nil args@@ -302,10 +332,10 @@ findFn' other rest = ([],[other], Nothing) : rest  paren :: Bool -> Doc -> Doc-paren False doc = grp doc-paren True  doc = grp ( "(" <> doc <> ")")+paren False doc = grp (nest 0 doc)+paren True  doc = grp (text "(" <> doc <> text ")") -data Output = OutLabel String CDSSet [Output]+data Output = OutLabel Text CDSSet [Output]             | OutData  CDS               deriving (Eq,Ord,Show) @@ -329,7 +359,63 @@ sep :: Doc sep = softline  -- A space, if the following still fits on the current line, otherwise newline. sp :: Doc-sp = " "   -- A space, always.+sp = text " "   -- A space, always. -prettyW :: (?statementWidth::Int) => Doc -> String-prettyW = pretty ?statementWidth+-- TODO fork FPretty to build on Text instead of Strings+text = FPretty.text . unpack++prettyW :: (?statementWidth::Int) => Doc -> (Hashed Text)+prettyW doc = hashed $ pack $ pretty ?statementWidth doc++-- %************************************************************************+-- %*                                                                   *+-- \subsection{Custom Eq and Ord instances for CDS that gloss over UIDs}+-- %*                                                                   *+-- %************************************************************************++newtype CDSsansUID = CDSsansUID CDS++instance Eq CDSsansUID where+  CDSsansUID(CDSNamed t _ xx) == CDSsansUID(CDSNamed t' _ yy) =+    t == t' && coerce xx == (coerce yy :: [CDSsansUID])+  CDSsansUID (CDSCons _ t xx) == CDSsansUID(CDSCons _ t' yy)  =+    t == t'  && coerce xx == (coerce yy :: [[CDSsansUID]])+  CDSsansUID (CDSFun _ res args) == CDSsansUID (CDSFun _ res' args') =+    (coerce res :: [CDSsansUID]) == coerce res' && coerce args == (coerce args' :: [CDSsansUID])+  CDSsansUID x == CDSsansUID y = x == y++instance Ord CDSsansUID where+  CDSsansUID (CDSNamed t _ xx) `compare` CDSsansUID (CDSNamed t' _ yy) =+    (t, coerce xx :: [CDSsansUID]) `compare` (t', coerce yy)+  CDSsansUID (CDSCons _ t xx) `compare` CDSsansUID (CDSCons _ t' yy) =+    (t, coerce xx :: [[CDSsansUID]]) `compare` (t', coerce yy)+  CDSsansUID (CDSFun _ args res) `compare` CDSsansUID (CDSFun _ args' res') =+    (coerce args :: [CDSsansUID], coerce res :: [CDSsansUID]) `compare` (coerce args', coerce res')+  CDSsansUID x `compare` CDSsansUID y = x `compare` y++instance Hashable CDSsansUID where+  s `hashWithSalt` CDSsansUID (CDSNamed t _ xx) = s `hashWithSalt` t `hashWithSalt` (coerce xx :: [CDSsansUID])+  s `hashWithSalt` CDSsansUID (CDSCons _  t xx) = s `hashWithSalt` t `hashWithSalt` (coerce xx :: [[CDSsansUID]])+  s `hashWithSalt` CDSsansUID (CDSFun _ args res) = s `hashWithSalt` (coerce args :: [CDSsansUID]) `hashWithSalt` (coerce res :: [CDSsansUID])+++-- %************************************************************************+-- %*                                                                   *+-- \subsection{Memoization of pretty calls}+-- %*                                                                   *+-- %************************************************************************++newtype MemoM a = MemoM (State (Map [CDSsansUID] (Hashed Text)) a) deriving (Applicative, Functor, Monad)++runMemoM :: MemoM a -> a+runMemoM (MemoM comp) = evalState comp mempty++memo :: Ord a => (a->b) -> a -> State (Map a b) b+memo f a = do+  table <- get+  case Map.lookup a table of+    Just b -> return b+    Nothing -> do+      let b = f a+      modify (Map.insert a b)+      return b
Debug/Hoed/Serialize.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE FlexibleInstances #-} -- This file is part of the Haskell debugger Hoed. -- -- Copyright (c) Maarten Faddegon, 2016@@ -16,8 +17,13 @@ import qualified Prelude as Prelude import Debug.Hoed.CompTree import Debug.Hoed.Render(CompStmt(..), StmtDetails(..))+import Data.Hashable import Data.Serialize+import Data.Serialize.Text+import Data.Vector.Serialize+ import qualified Data.ByteString as BS+import GHC.Exts (IsList(..)) import GHC.Generics import Data.Graph.Libgraph(Judgement(..),AssistedMessage(..),mapGraph,Graph(..),Arc(..)) @@ -35,6 +41,9 @@ instance Serialize Parent instance Serialize Event instance Serialize Change+instance (Hashable a, Serialize a) => Serialize (Hashed a) where+  get = hashed <$> get+  put = put . unhashed  -------------------------------------------------------------------------------- -- Tree
Debug/Hoed/TH.hs view
@@ -6,7 +6,9 @@ import           Control.Monad import           Data.Generics.Uniplate.Data import           Data.List                   (group, nub, sort, (\\))+import           Data.Text (pack) import           Debug.Hoed+import           Debug.Hoed import           Debug.Hoed.Compat import           Language.Haskell.TH import           Language.Haskell.TH.Syntax@@ -40,7 +42,7 @@       FunD n xx -> do         let Just n' = lookup n names             nb = nameBase n-        newDecl <- funD n [clause [] (normalB [| observe nb $(varE n')|]) []]+        newDecl <- funD n [clause [] (normalB [| observe (pack nb) $(varE n')|]) []]         return [newDecl, FunD n' xx]       SigD n ty | Just n' <- lookup n names -> do         dec' <- adjustSig n ty@@ -78,7 +80,7 @@       FunD n clauses -> do         let Just n' = lookup n names             nb = nameBase n-        newDecl <- funD n [clause [] (normalB [| observe nb $(varE n')|]) []]+        newDecl <- funD n [clause [] (normalB [| observe (pack nb) $(varE n')|]) []]         let clauses' = transformBi adjustValD clauses         return [newDecl, FunD n' clauses']       SigD n ty | Just n' <- lookup n names -> do@@ -111,7 +113,7 @@ adjustValD decl@ValD{} = transformBi adjustPat decl adjustValD other       = other -adjustPat (VarP x) = ViewP (VarE 'observe `AppE` toLit x) (VarP x)+adjustPat (VarP x) = ViewP (VarE 'observe `AppE` (VarE 'pack `AppE` toLit x)) (VarP x) adjustPat x        = x  toLit (Name (OccName x) _) = LitE $ StringL x
+ Debug/Hoed/Util.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Debug.Hoed.Util where++import System.Clock+import System.IO++data Verbosity = Verbose | Silent++-- | Conditional output to stderr+condPutStr :: Verbosity -> String -> IO ()+condPutStr Silent _    = return ()+condPutStr Verbose msg = hPutStr stderr msg++-- | Conditional output to stderr+condPutStrLn :: Verbosity -> String -> IO ()+condPutStrLn Silent _    = return ()+condPutStrLn Verbose msg = hPutStrLn stderr msg++--------------------------------------------+-- Measuring elapsed time++newtype Seconds = Seconds Double deriving (Eq, Ord, Num)++instance Show Seconds where+  show (Seconds s) = show s ++ " seconds"++stopWatch :: IO (IO Seconds)+stopWatch  = do+  t <- getTime Monotonic+  return $ do+    t' <- getTime Monotonic+    return (toSecs(diffTimeSpec t t'))+  where+       toSecs :: TimeSpec -> Seconds+       toSecs spec = Seconds $ fromIntegral(sec spec) + fromIntegral(nsec spec) * 1e-9
Hoed.cabal view
@@ -1,5 +1,5 @@ name:                Hoed-version:             0.4.1+version:             0.5.0 synopsis:            Lightweight algorithmic debugging. description:     Hoed is a tracer and debugger for the programming language Haskell.@@ -36,6 +36,8 @@                        , Debug.Hoed.Prop                        , Debug.Hoed.Serialize                        , Debug.Hoed.Span+                       , Debug.Hoed.Util+                       , Data.Rope.Mutable                        , Text.PrettyPrint.FPretty                        , Paths_Hoed   build-depends:       base >= 4 && <5@@ -45,15 +47,24 @@                        , process                        , libgraph == 1.14                        , regex-tdfa+                       , regex-tdfa-text                        , directory-                       , cereal, bytestring+                       , bytestring+                       , cereal, cereal-text, cereal-vector+                       , hashable >= 1.2.5+                       , hashtables                        , QuickCheck+                       , open-browser+                       , primitive                        , semigroups                        , strict                        , template-haskell                        , terminal-size+                       , text+                       , transformers                        , uniplate                        , vector+                       , vector-th-unbox   default-language:    Haskell2010  Test-Suite test-queens@@ -65,6 +76,7 @@                     , QuickCheck                     , Hoed   default-language: Haskell2010+  ghc-options:         -rtsopts  Test-Suite test-1   type:             exitcode-stdio-1.0@@ -112,5 +124,12 @@   type:             exitcode-stdio-1.0   hs-source-dirs:   tests/Pure/   main-is:          t7.hs+  build-depends:    base >= 4 && <5, QuickCheck, Hoed, process+  default-language: Haskell2010++Test-Suite test-th+  type:             exitcode-stdio-1.0+  hs-source-dirs:   tests/TH+  main-is:          quicksort.hs   build-depends:    base >= 4 && <5, QuickCheck, Hoed, process   default-language: Haskell2010
Text/PrettyPrint/FPretty.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE Safe, CPP #-}  -- | @@ -115,6 +116,7 @@ import Prelude hiding ((<$>)) #endif +import Data.Hashable import Data.Maybe (fromJust) import Data.Sequence as Dequeue (Seq, (<|), viewl, viewr, ViewL(..), ViewR(..)) import qualified Data.Sequence as Dequeue (empty)@@ -123,6 +125,7 @@   -- Efficiency probably similar, but Data.Sequence is part of the standard Haskell    -- libraries. import Data.String+import GHC.Generics  infixr 6 <>,<+> infixr 5 <$>,<$$>,</>,<//>@@ -247,7 +250,9 @@          | Group Doc          | Nest Int Doc     -- increase current indentation          | Align Int Doc    -- set indentation to current column plus increment-  deriving Show+  deriving (Eq, Generic, Ord, Show)++instance Hashable Doc  instance IsString Doc where   fromString = text
changelog view
@@ -1,3 +1,15 @@+0.5.0 Maarten Faddegon 28 Jan 2018++  * Improved rendering of parens in computation statements+  * A graph command contributed by Pepe Iborra+  * Space improvements contributed by Pepe Iborra++0.4.1 Maarten Faddegon 20 Jan 2018++  * Improved documentation for the new interface+  * Several bugfixes contributed by Pepe Iborra+  * Performance improvement contributed by Pepe Iborra+ 0.4.0 Maarten Faddegon 9 Sep 2017    * Text based interface to the debugger for closer integration with an interpreter (e.g. ghci)
examples/Queens__with_properties/Queens.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} module Queens where -- The queens problem made famous by Wirth. import Debug.Hoed
tests/Pure/t1.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} import Debug.Hoed import System.Process(system) import System.Exit(exitWith)
tests/Pure/t2.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} import Debug.Hoed import System.Process(system) import System.Exit(exitWith)
tests/Pure/t3.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- Haskell version of the buggy insertion sort as shown in Lee Naish -- A Declarative Debugging Scheme. 
tests/Pure/t4.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- A defective implementation of a parity function with a test property.  import Debug.Hoed
tests/Pure/t5.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} import Debug.Hoed import System.Process(system) import System.Exit(exitWith)
tests/Pure/t6.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveGeneric #-} module Main where import Debug.Hoed
tests/Pure/t7.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} import Debug.Hoed import System.Process(system) import System.Exit(exitWith)
+ tests/TH/quicksort.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TemplateHaskell #-}+import Data.List (partition)+import Debug.Hoed+import Debug.Hoed.TH+import System.Process+import System.Exit++obs [d|+  quicksort :: (a -> a -> Bool) -> [a] -> [a]+  quicksort op [] = []+  quicksort op (x:xs) = quicksort op lt ++ [x] ++ quicksort op gt+    where (lt, gt) = partition (`op` x) xs+        |]++debug [d|+  quicksort' :: (a -> a -> Bool) -> [a] -> [a]+  quicksort' op [] = []+  quicksort' op (x:xs) = quicksort' op lt ++ [x] ++ quicksort' op gt+    where (lt, gt) = partition (`op` x) xs+        |]++main = logO "hoed-tests-th-quicksort.graph" $ do+  print $ quicksort  (<) "haskell"+  print $ quicksort' (<) "haskell"+  exitWith =<< system "diff hoed-tests-th-quicksort.graph tests/ref/hoed-tests-th-quicksort.graph"
tests/ref/hoed-tests-Pure-t1.graph view
@@ -1,9 +1,9 @@ diGraph G {-v3 [label="g 2 = 1"]+v3 [label="f 2 = 1"] v2 [label="f 0 = 0"]-v1 [label="f 2 = 1"]+v1 [label="g 2 = 1"] v0 [label="."shape=none]-v1 -> v3 [label=""]+v3 -> v1 [label=""] v0 -> v2 [label=""]-v0 -> v1 [label=""]+v0 -> v3 [label=""] }
tests/ref/hoed-tests-Pure-t2.graph view
@@ -1,15 +1,15 @@ diGraph G {-v6 [label="n 2 = 2"]-v5 [label="n 1 = 1"]-v4 [label="m 2 = 2"]-v3 [label="m 1 = 1"]-v2 [label="l 1 = 1"]-v1 [label="k 1 = 3"]+v6 [label="k 1 = 3"]+v5 [label="l 1 = 1"]+v4 [label="m 1 = 1"]+v3 [label="m 2 = 2"]+v2 [label="n 1 = 1"]+v1 [label="n 2 = 2"] v0 [label="."shape=none]-v4 -> v6 [label=""]-v3 -> v5 [label=""]-v2 -> v3 [label=""]-v1 -> v4 [label=""]-v1 -> v2 [label=""]-v0 -> v1 [label=""]+v3 -> v1 [label=""]+v4 -> v2 [label=""]+v5 -> v4 [label=""]+v6 -> v3 [label=""]+v6 -> v5 [label=""]+v0 -> v6 [label=""] }
tests/ref/hoed-tests-Pure-t3.graph view
@@ -1,13 +1,13 @@ diGraph G {-v5 [label="insert 2 [] = 2 : []"]-v4 [label="insert 1 ( 2 : []) = 1 : []"]+v5 [label="isort (1 : 2 : []) = 1 : []"]+v4 [label="isort (2 : []) = 2 : []"] v3 [label="isort [] = []"]-v2 [label="isort ( 2 : []) = 2 : []"]-v1 [label="isort ( 1 : 2 : []) = 1 : []"]+v2 [label="insert 1 (2 : []) = 1 : []"]+v1 [label="insert 2 [] = 2 : []"] v0 [label="."shape=none]-v2 -> v3 [label=""]-v2 -> v5 [label=""]-v1 -> v2 [label=""]-v1 -> v4 [label=""]-v0 -> v1 [label=""]+v4 -> v3 [label=""]+v4 -> v1 [label=""]+v5 -> v4 [label=""]+v5 -> v2 [label=""]+v0 -> v5 [label=""] }
tests/ref/hoed-tests-Pure-t4.graph view
@@ -1,11 +1,11 @@ diGraph G {-v4 [label="plusOne 3 = 4"]-v3 [label="mod2 4 = 2"]-v2 [label="isEven 4 = False"]-v1 [label="isOdd 3 = False"]+v4 [label="isOdd 3 = False"]+v3 [label="isEven 4 = False"]+v2 [label="mod2 4 = 2"]+v1 [label="plusOne 3 = 4"] v0 [label="."shape=none]-v2 -> v3 [label=""]-v1 -> v4 [label=""]-v1 -> v2 [label=""]-v0 -> v1 [label=""]+v3 -> v2 [label=""]+v4 -> v1 [label=""]+v4 -> v3 [label=""]+v0 -> v4 [label=""] }
tests/ref/hoed-tests-Pure-t5.graph view
@@ -1,7 +1,7 @@ diGraph G {-v2 [label="g 3 = 6"]-v1 [label="f (Just 3) = 6"]+v2 [label="f (Just 3) = 6"]+v1 [label="g 3 = 6"] v0 [label="."shape=none]-v1 -> v2 [label=""]-v0 -> v1 [label=""]+v2 -> v1 [label=""]+v0 -> v2 [label=""] }
tests/ref/hoed-tests-Pure-t7.graph view
@@ -1,7 +1,7 @@ diGraph G {-v2 [label="apxy { \\ 4 5 -> 9 } 4 5 = 9"]-v1 [label="ap45 { \\ 4 5 -> 9 } = 9"]+v2 [label="ap45 { \\ 4 5 -> 9 } = 9"]+v1 [label="apxy { \\ 4 5 -> 9 } 4 5 = 9"] v0 [label="."shape=none]-v1 -> v2 [label=""]-v0 -> v1 [label=""]+v2 -> v1 [label=""]+v0 -> v2 [label=""] }
+ tests/ref/hoed-tests-th-quicksort.graph view
@@ -0,0 +1,91 @@+diGraph G {+v44 [label="quicksort { \\ 'a' 'h' -> True , \\ 'e' 'a' -> False , \\ 'e' 'h' -> True , \\ 'k' 'h' -> False , \\ 'k' 's' -> True , \\ 'l' 'h' -> False , \\ 'l' 'h' -> False , \\ 'l' 'k' -> False , \\ 'l' 'k' -> False , \\ 'l' 'l' -> False , \\ 'l' 's' -> True , \\ 'l' 's' -> True , \\ 's' 'h' -> False } \"haskell\" = \"aehklls\""]+v43 [label="quicksort { \\ 'e' 'a' -> False } \"ae\" = \"ae\""]+v42 [label="quicksort _ [] = []"]+v41 [label="quicksort _ \"e\" = \"e\""]+v40 [label="quicksort _ [] = []"]+v39 [label="quicksort _ [] = []"]+v38 [label="quicksort { \\ 'k' 's' -> True , \\ 'l' 'k' -> False , \\ 'l' 'k' -> False , \\ 'l' 'l' -> False , \\ 'l' 's' -> True , \\ 'l' 's' -> True } \"skll\" = \"klls\""]+v37 [label="quicksort { \\ 'l' 'k' -> False , \\ 'l' 'k' -> False , \\ 'l' 'l' -> False } \"kll\" = \"kll\""]+v36 [label="quicksort _ [] = []"]+v35 [label="quicksort { \\ 'l' 'l' -> False } \"ll\" = \"ll\""]+v34 [label="quicksort _ [] = []"]+v33 [label="quicksort _ \"l\" = \"l\""]+v32 [label="quicksort _ [] = []"]+v31 [label="quicksort _ [] = []"]+v30 [label="quicksort _ [] = []"]+v29 [label="quicksort' { \\ 'a' 'h' -> True , \\ 'e' 'a' -> False , \\ 'e' 'h' -> True , \\ 'k' 'h' -> False , \\ 'k' 's' -> True , \\ 'l' 'h' -> False , \\ 'l' 'h' -> False , \\ 'l' 'k' -> False , \\ 'l' 'k' -> False , \\ 'l' 'l' -> False , \\ 'l' 's' -> True , \\ 'l' 's' -> True , \\ 's' 'h' -> False } \"haskell\" = \"aehklls\""]+v28 [label="quicksort' { \\ 'e' 'a' -> False } \"ae\" = \"ae\""]+v27 [label="quicksort' _ [] = []"]+v26 [label="quicksort' _ \"e\" = \"e\""]+v25 [label="quicksort' _ [] = []"]+v24 [label="quicksort' _ [] = []"]+v23 [label="quicksort' { \\ 'k' 's' -> True , \\ 'l' 'k' -> False , \\ 'l' 'k' -> False , \\ 'l' 'l' -> False , \\ 'l' 's' -> True , \\ 'l' 's' -> True } \"skll\" = \"klls\""]+v22 [label="quicksort' { \\ 'l' 'k' -> False , \\ 'l' 'k' -> False , \\ 'l' 'l' -> False } \"kll\" = \"kll\""]+v21 [label="quicksort' _ [] = []"]+v20 [label="quicksort' { \\ 'l' 'l' -> False } \"ll\" = \"ll\""]+v19 [label="quicksort' _ [] = []"]+v18 [label="quicksort' _ \"l\" = \"l\""]+v17 [label="quicksort' _ [] = []"]+v16 [label="quicksort' _ [] = []"]+v15 [label="quicksort' _ [] = []"]+v14 [label="lt = \"ae\""]+v13 [label="lt = []"]+v12 [label="gt = \"e\""]+v11 [label="lt = []"]+v10 [label="gt = []"]+v9 [label="gt = \"skll\""]+v8 [label="lt = \"kll\""]+v7 [label="lt = []"]+v6 [label="gt = \"ll\""]+v5 [label="lt = []"]+v4 [label="gt = \"l\""]+v3 [label="lt = []"]+v2 [label="gt = []"]+v1 [label="gt = []"]+v0 [label="."shape=none]+v18 -> v2 [label=""]+v18 -> v16 [label=""]+v18 -> v3 [label=""]+v18 -> v17 [label=""]+v20 -> v4 [label=""]+v20 -> v18 [label=""]+v20 -> v5 [label=""]+v20 -> v19 [label=""]+v22 -> v6 [label=""]+v22 -> v20 [label=""]+v22 -> v7 [label=""]+v22 -> v21 [label=""]+v23 -> v1 [label=""]+v23 -> v15 [label=""]+v23 -> v8 [label=""]+v23 -> v22 [label=""]+v26 -> v10 [label=""]+v26 -> v24 [label=""]+v26 -> v11 [label=""]+v26 -> v25 [label=""]+v28 -> v12 [label=""]+v28 -> v26 [label=""]+v28 -> v13 [label=""]+v28 -> v27 [label=""]+v29 -> v9 [label=""]+v29 -> v23 [label=""]+v29 -> v14 [label=""]+v29 -> v28 [label=""]+v33 -> v31 [label=""]+v33 -> v32 [label=""]+v35 -> v33 [label=""]+v35 -> v34 [label=""]+v37 -> v35 [label=""]+v37 -> v36 [label=""]+v38 -> v30 [label=""]+v38 -> v37 [label=""]+v41 -> v39 [label=""]+v41 -> v40 [label=""]+v43 -> v41 [label=""]+v43 -> v42 [label=""]+v44 -> v38 [label=""]+v44 -> v43 [label=""]+v0 -> v29 [label=""]+v0 -> v44 [label=""]+}