packages feed

ministg-0.1: src/Ministg/State.hs

-----------------------------------------------------------------------------
-- |
-- Module      : Ministg.State
-- Copyright   : (c) 2009 Bernie Pope 
-- License     : BSD-style
-- Maintainer  : bjpop@csse.unimelb.edu.au
-- Stability   : experimental
-- Portability : ghc
--
-- Representation of the state of the ministg evaluator.
-----------------------------------------------------------------------------

module Ministg.State 
   ( Continuation (..)
   , Stack
   , prettyStack 
   , Heap 
   , EvalState (..)
   , Eval
   , initStack
   , initHeap
   , initState
   , pushCallStack
   , setCallStack
   , lookupHeap
   , lookupHeapAtom
   , updateHeap
   , freshVar
   , incStepCount
   , setRule
   , prettyHeapObject
   , isArgCont
   )
   where

import Control.Monad.State 
import Data.Map as Map hiding (map)
import Data.Set as Set hiding (map)
import Ministg.AST
import Ministg.CallStack (CallStack, push, prettyCallStack)
import Ministg.Pretty 
import Ministg.Options 
   ( Flag (..), defaultMaxSteps, defaultTraceDir
   , probeFlagsFirst, existsFlag, getTraceDir, getMaxTraceSteps )

-- | Stack continuations.
data Continuation
   = CaseCont [Alt] CallStack -- ^ The alternatives of a case expression.
   | UpdateCont Var CallStack -- ^ A variable which points to a thunk to be updated.
   | ArgCont Atom             -- ^ A pending argument (used only by the push-enter model).
   | ApplyToArgs [Atom]       -- ^ Apply the returned function to these arguments (eval-apply only).
   deriving (Eq, Show)

instance FreeVars Continuation where
   freeVars (CaseCont alts _cs) = freeVars alts
   freeVars (UpdateCont var _cs) = Set.singleton var
   freeVars (ArgCont arg) = freeVars arg
   freeVars (ApplyToArgs args) = freeVars args

instance Pretty Continuation where
   pretty (CaseCont alts callStack) 
      = text "case *" <+> braces (vcat (punctuate semi (map pretty alts))) $$
        nest 3 (prettyCallStack callStack)
   pretty (UpdateCont var callStack) 
      = text "upd *" <+> text var $$
        nest 3 (prettyCallStack callStack)
   pretty (ArgCont atom) = text "arg" <+> pretty atom 
   pretty (ApplyToArgs atoms) = parens (char '*' <+> hsep (map pretty atoms))

isArgCont :: Continuation -> Bool
isArgCont (ArgCont {}) = True
isArgCont _other = False

-- | The evaluation stack. 
type Stack = [Continuation]

prettyStack :: Stack -> Doc
prettyStack stack = (vcat $ map prettyCont stack)
   where
   prettyCont :: Continuation -> Doc
   prettyCont cont = text "-" <+> pretty cont

-- | The heap (mapping variables to objects).
type Heap = Map.Map Var Object

-- | State to be threaded through evaluation.
data EvalState 
   = EvalState 
     { state_unique :: !Int           -- ^ Unique counter for generating fresh variables.
     , state_callStack :: CallStack   -- ^ Function call stack (for debugging).
     , state_stepCount :: !Integer    -- ^ How many steps have been executed.
     , state_lastRule :: !String      -- ^ The most recent evaluation rule applied.
     , state_trace :: Bool            -- ^ Do we want tracing of evaluation steps? 
     , state_maxTraceSteps :: Integer -- ^ Maximum number of evaluation steps to trace.
     , state_traceDir :: String       -- ^ Name of directory to store trace files.
     , state_gc :: Bool               -- ^ Do we want garbage collection?
     , state_traceCallStack :: Bool   -- ^ Do we want the call stack shown in the trace?
     }

-- | Eval monad. Combines State and IO.
type Eval a = StateT EvalState IO a

initState :: [Flag] -> EvalState
initState flags = 
   EvalState 
   { state_unique = 0
   , state_callStack = []
   , state_stepCount = 0 
   , state_lastRule = ""
   , state_trace = existsFlag flags Trace 
   , state_maxTraceSteps = getMaxTraceSteps flags
   , state_traceDir = getTraceDir flags
   , state_gc = not $ existsFlag flags NoGC 
   , state_traceCallStack = existsFlag flags CallStack
   }

initHeap :: Program -> Heap
initHeap (Program decls) = Map.fromList $ map declToPair decls
   where
   declToPair :: Decl -> (Var, Object)
   declToPair (Decl var obj) = (var, obj)

initStack :: Stack
initStack = []

setRule :: String -> Eval ()
setRule str = do
   lr <- gets state_lastRule
   modify $ \s -> s { state_lastRule = str } 

incStepCount :: Eval ()
incStepCount = do
   sc <- gets state_stepCount
   modify $ \s -> s { state_stepCount = sc + 1 } 

pushCallStack :: String -> Eval ()
pushCallStack str = do
   cs <- gets state_callStack
   modify $ \s -> s { state_callStack = push str cs }

setCallStack :: CallStack -> Eval ()
setCallStack cs = modify $ \s -> s { state_callStack = cs }

-- | Lookup a variable in a heap. If found return the corresponding
-- object, otherwise throw an error (it is a fatal error which can't
-- be recovered from).
lookupHeap :: Var -> Heap -> Object 
lookupHeap var heap = 
   case Map.lookup var heap of
      Nothing -> error $ "undefined variable: " ++ show var
      Just object -> object

-- | Convenience wrapper for lookupHeap, for atoms which happen to be variables.
lookupHeapAtom :: Atom -> Heap -> Object
lookupHeapAtom (Variable var) heap = lookupHeap var heap
lookupHeapAtom other _heap = error $ "lookupHeapAtom called with non variable " ++ show other

-- | Add a new mapping to a heap, or update an existing one.
updateHeap :: Var -> Object -> Heap -> Heap 
updateHeap = Map.insert 

-- | Generate a new unique variable. Uniqueness is guaranteed by using a
-- "$" prefix, which is not allowed in the concrete sytax of ministg programs.
freshVar :: Eval Var
freshVar = do
   u <- gets state_unique
   modify $ \s -> s { state_unique = u + 1 }
   return $ "$" ++ show u

-- XXX not very good for printing large objects, nonetheless it is lazy.
prettyHeapObject :: Heap -> Object -> String 
prettyHeapObject heap (Con constructor args)
   | length args == 0 = constructor
   | otherwise = "(" ++ unwords (constructor : map (prettyHeapAtom heap) args) ++ ")"
prettyHeapObject _heap (Fun {}) = "<function>"
prettyHeapObject _heap (Pap {}) = "<pap>"
prettyHeapObject _heap (Thunk {}) = "<thunk>"
prettyHeapObject _heap BlackHole = "<blackhole>"
prettyHeapObject _heap Error = "<error>"

prettyHeapAtom :: Heap -> Atom -> String 
prettyHeapAtom heap (Literal (Integer i)) = show i
prettyHeapAtom heap (Variable var) = prettyHeapObject heap $ lookupHeap var heap