packages feed

Fungi-1.0.6: src/Interpreter.hs

module Interpreter (
    runFungi
  ) where

import Control.Arrow ((&&&))
import Control.Monad.State.Strict

import qualified Data.List.Zipper as Zipper

import System.Exit (ExitCode, exitSuccess)

import Env
import Instruction
import Ip

-----------------------------------------------------------

data RunResult i = RunResult {
    getLiveIps :: [Ip Env i]
  , getResultEnv :: Env i
  }

runFungi :: (I i) => Env i -> IO ExitCode
runFungi = evalStateT $ forever runEnvRound

runEnvRound :: (I i) => Instruction i ()
runEnvRound = do
  noIps <- gets $ Zipper.emptyp . getIps
  if noIps
    then liftIO exitSuccess
    else do
      runResult <- runEnvRound'
      let ips = Zipper.fromList $ getLiveIps runResult
      modify $ \env -> env { getIps = ips }
      advanceIps

runEnvRound' :: (I i) => Instruction i (RunResult i)
runEnvRound' = do
  ips <- gets getIps
  case Zipper.safeCursor ips of
    Nothing -> do
      env <- get
      return RunResult {
          getLiveIps = []
        , getResultEnv = env
        }
    Just _ -> do
      runCurrentInstruction
      (ip, spawnedIps) <- gets $ currentIp &&& getSpawnedIps
      let liveIps = spawnedIps ++ [ip | isAlive ip]
      modify $ \env -> withIps Zipper.right env {
          getSpawnedIps = []
        , getValidIds = [getId ip | not $ isAlive ip] ++ getValidIds env
        }
      runResult <- runEnvRound'
      let liveIps' = liveIps ++ getLiveIps runResult
      return runResult { getLiveIps = liveIps' }

advanceIps :: (I i) => Instruction i ()
advanceIps = do
  ips <- gets getIps
  case Zipper.safeCursor ips of
    Nothing -> modify $ withIps Zipper.start
    Just _ -> do
      trampolineInstr
      modify $ withIps Zipper.right
      advanceIps