ddci-core-0.2.0.1: DDCI/Core/Command/Eval.hs
module DDCI.Core.Command.Eval
( cmdStep
, cmdEval
, evalExp)
where
import DDCI.Core.Stats.Trace
import DDCI.Core.Command.Check
import DDCI.Core.IO
import DDCI.Core.State
import DDC.Core.Eval.Env
import DDC.Core.Eval.Step
import DDC.Core.Eval.Name
import DDC.Core.Check
import DDC.Core.Exp
import DDC.Core.Pretty
import DDC.Core.Collect
import DDC.Type.Equiv
import DDC.Type.Subsumes
import DDC.Type.Compounds
import Control.Monad
import DDC.Core.Eval.Store (Store)
import qualified DDC.Core.Eval.Store as Store
import qualified Data.Set as Set
-- | Parse, check, and single step evaluate an expression.
cmdStep :: State -> Int -> String -> IO ()
cmdStep state lineStart str
= cmdParseCheckExp state lineStart str >>= goStore
where
-- Expression had a parse or type error.
goStore Nothing
= return ()
-- Expression is well-typed.
goStore (Just (x, tX, effX, cloX))
= let -- Create the initial store.
store = startingStoreForExp x
in goStep store x tX effX cloX
goStep store x tX effX cloX
= do _ <- forcePrint state store x tX effX cloX
return ()
-- | Parse, check, and fully evaluate an expression.
cmdEval :: State -> Int -> String -> IO ()
cmdEval state lineStart str
= cmdParseCheckExp state lineStart str >>= goEval
where
-- Expression had a parse or type error.
goEval Nothing
= return ()
-- Expression is well-typed.
goEval (Just expr)
= evalExp state expr
-- | Evaluate an already parsed and type-checked expression.
-- Exported so transforms can test with it.
evalExp :: State -> (Exp () Name, Type Name, Effect Name, Closure Name) -> IO ()
evalExp state (x, tX, effX, cloX)
= do -- Create the initial store.
let store = startingStoreForExp x
-- Print starting expression.
when (Set.member TraceEval $ stateModes state)
$ outDocLn state (text "* STEP: " <> ppr x)
-- Print starting store.
when (Set.member TraceStore $ stateModes state)
$ do putStrLn $ renderIndent $ ppr store
outStr state "\n"
goStep store x
where
goStep store x0
= do
mResult <- forcePrint state store x0 tX effX cloX
case mResult of
Nothing -> return ()
Just (store', x0') -> goStep store' x0'
-- | Create a starting store for the given expression.
-- We pre-allocate any region handles in the expression,
-- and ensure that the next region to be allocated is higher
-- than all of these.
startingStoreForExp :: Exp () Name -> Store
startingStoreForExp xx
= let
-- Gather up all the region handles already in the expression.
rs = [ r | UPrim (NameRgn r) _ <- Set.toList $ collectBound xx]
-- Decide what new region should be allocated first.
-- Region 0 is reserved for thunks.
iFirstAlloc
| _ : _ <- rs
, Rgn iR <- maximum rs
= iR + 1
| otherwise
= 1
in Store.empty
{ Store.storeNextRgn = iFirstAlloc
, Store.storeRegions = Set.fromList (Rgn 0 : rs)
, Store.storeGlobal = Set.fromList (Rgn 0 : rs) }
-- | Perform a single step of evaluation and print what happened.
forcePrint
:: State
-> Store
-> Exp () Name
-> Type Name
-> Effect Name
-> Closure Name
-> IO (Maybe (Store, Exp () Name))
forcePrint state store x tX effX cloX
= case force store x of
StepProgress store' x'
-> case checkExp primDataDefs primKindEnv primTypeEnv x' of
Left err
-> do
-- Print intermediate expression.
when (Set.member TraceEval $ stateModes state)
$ do outDocLn state $ text "* STEP: " <> ppr x'
-- Print intermediate store
when (Set.member TraceStore $ stateModes state)
$ do putStrLn $ renderIndent $ ppr store'
outStr state "\n"
putStrLn $ renderIndent
$ vcat [ text "* OFF THE RAILS!"
, ppr err
, empty ]
return $ Nothing
Right (_, tX', effX', cloX')
-> do
-- Print intermediate expression.
when (Set.member TraceEval $ stateModes state)
$ do outDocLn state $ text "* STEP: " <> ppr x'
-- Print intermediate store
when (Set.member TraceStore $ stateModes state)
$ do putStrLn $ renderIndent $ ppr store'
putStr "\n"
-- Check expression has the same type as before,
-- and that the effect and closure are no greater.
let deathT = not $ equivT tX tX'
let deathE = not $ subsumesT kEffect effX effX'
let deathC = not $ subsumesT kClosure cloX cloX'
let death = deathT || deathE || deathC
when deathT
$ putStrLn $ renderIndent
$ vcat [ text "* OFF THE RAILS!"
, ppr tX
, ppr tX' ]
if death
then return Nothing
else return $ Just (store', x')
StepDone
-> do -- Load the final expression back from the store to display.
outDocLn state $ ppr $ traceStore store x
return Nothing
StepStuck
-> do outDocLn state
$ vcat [ text "* STUCK!"
, empty]
return Nothing
StepStuckMistyped err
-> do putStrLn $ renderIndent
$ vcat [ ppr "* OFF THE RAILS!"
, ppr err
, empty]
return Nothing