packages feed

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"