packages feed

stgi-1.1: app/Stg/RunForPager.hs

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE MultiWayIf        #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes        #-}

-- | Run a STG program with output suitable for use in a pager, such as @less@.
module Stg.RunForPager (runForPager, Renderer(..)) where



import           Control.Monad
import           Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NE
import           Data.Monoid
import           Data.Text          (Text)
import qualified Data.Text          as T
import qualified Data.Text.IO       as T

import Stg.Language
import Stg.Machine
import Stg.Machine.Types
import Stg.Util


data Renderer = Renderer
    { renderProgram   :: Program   -> Text
    , renderState     :: StgState  -> Text
    , renderInfo      :: Info      -> Text
    , renderInfoShort :: InfoShort -> Text
    }

runForPager
    :: Renderer
    -> Maybe Int -- ^ Steps to show. Negative numbers count from the end.
    -> Int       -- ^ Verbosity level
    -> Program
    -> IO StgState
runForPager renderer showSteps verbosity prog =
    let allStates = evalsUntil RunIndefinitely
                               (HaltIf (const False))
                               (PerformGc (const (Just triStateTracing)))
                               (initialState "main" prog)
        states = case showSteps of
            Just n | n > 0 -> NE.fromList (NE.take n allStates)
                   | n < 0 -> unsafeTakeLast (abs n) allStates
            _else -> allStates
        line = T.replicate 80 "-"
        fatLine = T.replicate 80 "="
    in do
        T.putStrLn fatLine
        T.putStrLn "Program:"
        T.putStrLn line
        T.putStrLn (renderProgram renderer prog)
        let loop (state :| rest) = do
                T.putStrLn fatLine
                printInfo renderer verbosity state line
                T.putStrLn (renderState renderer state)
                case rest of
                    [] -> pure state
                    (s:ss) -> loop (s:|ss)
        finalState <- loop states
        T.putStrLn fatLine
        pure finalState


printInfo
    :: Renderer
    -> Int
    -> StgState
    -> Text -- ^ Line
    -> IO ()
printInfo renderer verbosity state line =
    when (verbosity > 0)
        (do T.putStr (show' (stgSteps state) <> ". ")
            T.putStrLn
                (if | verbosity == 2 -> renderInfo renderer (stgInfo state)
                    | verbosity == 1 -> renderInfoShort renderer (let Info shortInfo _ = stgInfo state
                                                                  in shortInfo ))
            T.putStrLn line )

-- | Take the last N elements of a list (in original order).
--
-- Number of takes must be at least 1!
unsafeTakeLast :: Int -> NonEmpty a -> NonEmpty a
unsafeTakeLast n _ | n <= 0 = error "unsafeTakeLast: argument must be >= 1"
unsafeTakeLast n list
  = let list' = NE.toList list
    in NE.fromList (zipOverflow (drop n list') list')
  where
    zipOverflow (_:xs) (_:ys) = zipOverflow xs ys
    zipOverflow xs [] = xs
    zipOverflow [] ys = ys