packages feed

Fungi-1.0: Env.hs

module Env (
    Env (..)
  , mkEnv
  , withDebugger
  , withIp
  , withIps
  , withSpace
  , withSs
  , withToss
  , withSemantics
  , currentCell
  , currentIp
  , currentSs
  , currentToss
  , addSpawnedIp
  , addBreakPoint
  , removeBreakPoint
  ) where

import Control.Monad.State.Strict

import Data.Deque
import Data.I
import Data.List (genericTake)
import Data.List.Zipper (Zipper)
import qualified Data.List.Zipper as Zipper
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.MaybeBounded
import qualified Data.Set as Set
import Data.Stack
import qualified Data.Stack as Stack
import Data.Vector

import Debug.Debugger

import Space.Cell
import Space.Space

import Ip
import Semantics
import UnknownInstruction


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

type Instruction i = StateT (Env i) IO

type Fingerprints i = Map Integer [(i, Instruction i ())]

data Env i = Env {
    getIps :: Zipper (Ip Env i)
  , getDebugger :: Debugger Env i
  , getDim :: Int
  , getSpace :: Space i
  , getSpawnedIps :: [Ip Env i]
  , getValidIds :: [i]
  , getUnshowableChar :: Char
  , getUnknownMode :: UnknownInstruction
  , getProgName :: String
  , getFungeArgs :: [String]
  , getFingerprints :: Fingerprints i
  , getValidReferences :: [i]
  , getReferenceMap :: Map i (Vector i)
  }

mkEnv :: (I i) => Int
        -> Space i 
        -> UnknownInstruction 
        -> Debugger Env i 
        -> String 
        -> [String] 
        -> Fingerprints i 
        -> Map i (Instruction i ())
        -> Env i
mkEnv dim space unknownMode debugger progName args fingerprints baseSemantics = Env {
    getIps = Zipper.fromList [mkIp dim ident baseSemantics]
  , getDebugger = debugger
  , getDim = dim
  , getSpace = space
  , getSpawnedIps = []
  , getValidIds = idents
  , getUnshowableChar = '®'
  , getUnknownMode = unknownMode
  , getProgName = progName
  , getFungeArgs = args
  , getFingerprints = fingerprints
  , getValidReferences = uniqueVals
  , getReferenceMap = Map.empty
  }
  where
    ident : idents = uniqueVals

uniqueVals :: (I i) => [i]
uniqueVals = case maybeMaxBound of
  Nothing -> [0 ..]
  Just maxN -> [0 .. maxN] ++ case maybeMinBound of 
    Nothing -> iterate (subtract 1) (-1)
    Just minN -> init [minN .. 0]

currentIp :: Env i -> Ip Env i
currentIp = Zipper.cursor . getIps

setCurrentIp :: Ip Env i -> Env i -> Env i
setCurrentIp ip = withIps $ Zipper.replace ip

withDebugger :: (Debugger Env i -> Debugger Env i) -> Env i -> Env i
withDebugger f env = env { getDebugger = f $ getDebugger env }

withIp :: (Ip Env i -> Ip Env i) -> Env i -> Env i
withIp f env = setCurrentIp (f $ currentIp env) env

withIps :: (Zipper (Ip Env i) -> Zipper (Ip Env i)) -> Env i -> Env i
withIps f env = env { getIps = f $ getIps env }

withSemantics :: (Semantics Env i -> Semantics Env i) -> Env i -> Env i
withSemantics f = withIp $ \ip -> ip { getSemantics = f $ getSemantics ip }

withSpace :: (Space i -> Space i) -> Env i -> Env i
withSpace f env = env { getSpace = f $ getSpace env }

withSs :: (Stack (Deque i) -> Stack (Deque i)) -> Env i -> Env i
withSs f = withIp $ \ip -> ip { getSs = f $ getSs ip }

withToss :: (Deque i -> Deque i) -> Env i -> Env i
withToss f = withSs $ \ss -> case Stack.top ss of
  Just s -> Stack.push (f s) $ Stack.pop ss
  Nothing -> mkStack1 $ f mkDeque

currentSs :: Env i -> Stack (Deque i)
currentSs = getSs . currentIp

currentToss :: Env i -> Deque i
currentToss = fromMaybe mkDeque . Stack.top . currentSs

currentCell :: (Integral i) => Env i -> Cell i
currentCell env = cellAt s pos
  where
    s = getSpace env
    pos = getPos . currentIp $ env

addSpawnedIp :: Ip Env i -> Env i -> Env i
addSpawnedIp ip env = env { getSpawnedIps = ip : getSpawnedIps env }

addBreakPoint :: (I i) => [i] -> Env i -> Env i
addBreakPoint pos env = withDebugger (withBreakPoints $ Set.insert pos') env
  where
    pos' = genericTake (getDim env) $ pos ++ repeat 0

removeBreakPoint :: (I i) => [i] -> Env i -> Env i
removeBreakPoint pos env = withDebugger (withBreakPoints $ Set.delete pos') env
  where
    pos' = genericTake (getDim env) $ pos ++ repeat 0