ministg-0.1: src/Ministg/TraceEval.hs
{-# OPTIONS_GHC -XPatternGuards #-}
-----------------------------------------------------------------------------
-- |
-- Module : Ministg.TraceEval
-- Copyright : (c) 2009 Bernie Pope
-- License : BSD-style
-- Maintainer : bjpop@csse.unimelb.edu.au
-- Stability : experimental
-- Portability : ghc
--
-- Trace the evaluation steps of the interpreter and generate HTML output.
-----------------------------------------------------------------------------
module Ministg.TraceEval (traceEval, traceEnd) where
import System.FilePath ((<.>), (</>))
import Control.Monad (when, join)
import Control.Monad.Trans (liftIO)
import Control.Monad.State (gets)
import Control.Applicative ((<$>))
import Text.XHtml.Transitional as Html
import Text.XHtml.Table hiding ((</>))
import Data.Map as Map (toList)
import Ministg.AST
import Ministg.CallStack (CallStack, push, showCallStack)
import Ministg.Pretty as Pretty (pretty, Doc, ($$), nest, render, text)
import Ministg.State
import Data.List as List (sortBy)
traceEval :: Exp -> Stack -> Heap -> Eval ()
traceEval exp stack heap = do
traceOn <- gets state_trace
when traceOn $ do
count <- gets state_stepCount
maxSteps <- gets state_maxTraceSteps
when (count <= maxSteps) $ do
join (writeTraceFile <$> makeHtml exp stack heap)
when (count == maxSteps + 1) $ lastTracePage "Maximum trace steps exceeded"
traceEnd :: Eval ()
traceEnd = do
traceOn <- gets state_trace
when traceOn $ lastTracePage "The computation has completed"
lastTracePage :: String -> Eval ()
lastTracePage msg = join (writeTraceFile <$> lastPage msg)
writeTraceFile :: Html -> Eval ()
writeTraceFile html = do
traceFile <- nextTraceFileName
liftIO $ writeFile traceFile $ renderHtml html
nextTraceFileName :: Eval FilePath
nextTraceFileName = do
traceDir <- gets state_traceDir
count <- gets state_stepCount
return $ traceDir </> mkHtmlFileName count
lastPage :: String -> Eval Html
lastPage msg = do
count <- gets state_stepCount
return (theHead +++ theBody count)
where
theHead = header << thetitle << msg
theBody count
= body << (mainHeading +++ navigation)
where
mainHeading = h1 << msg
navigation = paragraph ((anchor << "previous") ! [href $ mkHtmlFileName (count - 1)])
makeHtml :: Exp -> Stack -> Heap -> Eval Html
makeHtml exp stack heap = do
count <- gets state_stepCount
rule <- gets state_lastRule
callStack <- gets state_callStack
wantCallStack <- gets state_traceCallStack
return $ headAndBody count rule
(if wantCallStack then Just callStack else Nothing)
where
headAndBody count rule maybeCallStack = theHead +++ theBody
where
stepStr = "Step " ++ show count
theHead = header << thetitle << stepStr
theBody =
body << (mainHeading +++ navigation +++ ruleSection +++ expStackSection +++ heapSection)
where
mainHeading = h1 << stepStr
navigation = paragraph (previous +++ " " +++ next)
previous = if count == 0 then noHtml
else (anchor << "previous") ! [href $ mkHtmlFileName (count - 1)]
next = (anchor << "next") ! [href $ mkHtmlFileName (count + 1)]
ruleSection = if null rule then noHtml
else (h3 << "Most recent rule applied") +++ (paragraph << rule)
expStackSection = (h3 << "Stack and Code") +++ expStackCallTable exp stack maybeCallStack
heapSection = (h3 << "Heap") +++ heapTable heap
expStackCallTable :: Exp -> Stack -> Maybe CallStack -> Html
expStackCallTable exp stack maybeCallStack
= simpleTable [border 3, cellpadding 10] [thestyle "vertical-align:top"] rows
where
rows | Just callStack <- maybeCallStack =
[stackExprHeading ++ callStackHeading, stackExprData ++ callStackData callStack]
| otherwise = [stackExprHeading, stackExprData]
where
stackExprHeading = [stringToHtml "Stack", stringToHtml "Expression"]
stackExprData = [stackTable stack, pre << expStr]
callStackHeading = [stringToHtml "Call Stack"]
callStackData callStack = [callStackTable callStack]
expStr = render $ pretty exp
stackTable :: Stack -> Html
stackTable [] = noHtml
stackTable stack
= simpleTable [border 1, cellpadding 5, cellspacing 0]
[] (map stackRow stack)
where
stackRow :: Continuation -> [Html]
stackRow cont = [ pre << (render $ pretty cont) ]
callStackTable :: CallStack -> Html
callStackTable [] = noHtml
callStackTable stack
= simpleTable [border 1, cellpadding 5, cellspacing 0]
[] (map stackRow stack)
where
stackRow :: String -> [Html]
stackRow str = [ pre << str ]
heapTable :: Heap -> Html
heapTable heap
= simpleTable [border 3, cellpadding 5, cellspacing 0]
[] (headingRow : map heapRow mappings)
where
headingRow = [stringToHtml "Variable", stringToHtml "Object"]
mappings = List.sortBy (\(x,_) (y,_) -> compare x y) $ Map.toList heap
heapRow :: (Var, Object) -> [Html]
heapRow (var, obj) = [pre << var, pre << render (pretty obj)]
mkHtmlFileName :: Integer -> FilePath
mkHtmlFileName count = "step" ++ show count <.> "html"