packages feed

Fungi-1.0: Semantics.hs

module Semantics (
    Semantics
  , mkSemantics
  , lookup
  , pushFingerprint
  , popFingerprint
  , toggleOverlay
  , addOverlay
  , removeOverlay
  ) where

import Prelude hiding (lookup)

import Control.Monad.State.Strict

import Data.Char (ord)
import Data.I
import Data.Labeled
import Data.Map (Map)
import qualified Data.Map as Map

import Mode

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

type Instruction env i = StateT (env i) IO

type InfMap k v = (k -> Maybe v, Map k (Maybe v))

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

data Semantics env i = S {
    baseInstrs :: Map i (Instruction env i ())
  , fingerInstrs :: Map i [Instruction env i ()]
  , overlayInstrs :: [Labeled Mode (InfMap i (Instruction env i ()))]
  }

mkSemantics :: (I i) => Map i (Instruction env i ()) -> Semantics env i
mkSemantics base = S {
    baseInstrs = base
  , fingerInstrs = Map.fromList $ zip [fromIntegral $ ord c | c <- ['A'..'Z']] $ repeat []
  , overlayInstrs = []
  }

lookup :: (I i) => i -> Semantics env i -> Maybe (Instruction env i ())
lookup i sem = case lookupOverlay i $ map unlabel $ overlayInstrs sem of
  Just instr -> Just instr
  Nothing -> case lookupFinger i $ fingerInstrs sem of
    Just instr -> Just instr
    Nothing -> lookupBase i $ baseInstrs sem

lookupOverlay :: (I i) => i -> [InfMap i (Instruction env i ())] -> Maybe (Instruction env i ())
lookupOverlay _ [] = Nothing
lookupOverlay i ((f, m) : ms) = case Map.lookup i m of
  Just mInstr -> case mInstr of
    Just instr -> Just instr
    Nothing -> lookupOverlay i ms
  Nothing -> case f i of
    Just instr -> Just instr
    Nothing -> lookupOverlay i ms

lookupFinger :: (I i) => i -> Map i [Instruction env i ()] -> Maybe (Instruction env i ())
lookupFinger i m = case Map.lookup i m of
  Nothing -> Nothing
  Just [] -> Nothing
  Just (instr:_) -> Just instr

lookupBase :: (I i) => i -> Map i (Instruction env i ()) -> Maybe (Instruction env i ())
lookupBase = Map.lookup

pushFingerprint :: (I i) => [(i, Instruction env i ())] -> Semantics env i -> Semantics env i
pushFingerprint assocs sem = sem { fingerInstrs = m' }
  where
    m = fingerInstrs sem
    m' = foldr add m assocs
    add (i, instr) = Map.adjust (instr:) i

popFingerprint :: (I i) => [(i, Instruction env i ())] -> Semantics env i -> Semantics env i
popFingerprint assocs sem = sem { fingerInstrs = m' }
  where
    m = fingerInstrs sem
    m' = foldr (remove . fst) m assocs
    remove = Map.adjust tail

addOverlay :: (I i)
        => Mode
        -> (i -> Maybe (Instruction env i ()), Map i (Maybe (Instruction env i ())))
        -> Semantics env i
        -> Semantics env i
addOverlay mode f_m sem = if mode `elem` map getLabel (overlayInstrs sem)
  then sem
  else addOverlay' mode f_m sem

addOverlay' :: (I i)
        => Mode
        -> (i -> Maybe (Instruction env i ()), Map i (Maybe (Instruction env i ())))
        -> Semantics env i
        -> Semantics env i
addOverlay' mode f_m sem = sem { overlayInstrs = imap : overlayInstrs sem }
  where
    imap = label mode f_m

removeOverlay :: (I i) => Mode -> Semantics env i -> Semantics env i
removeOverlay mode sem = sem { overlayInstrs = removeOverlay' mode $ overlayInstrs sem }

removeOverlay' :: (I i)
        => Mode
        -> [Labeled Mode (InfMap i (Instruction env i ()))]
        -> [Labeled Mode (InfMap i (Instruction env i ()))]
removeOverlay' _ [] = []
removeOverlay' mode (m:ms) = if getLabel m == mode
  then ms
  else m : removeOverlay' mode ms

toggleOverlay :: (I i)
        => Mode
        -> (i -> Maybe (Instruction env i ()), Map i (Maybe (Instruction env i ())))
        -> Semantics env i
        -> Semantics env i
toggleOverlay mode f_m sem = if mode `elem` map getLabel (overlayInstrs sem)
  then removeOverlay mode sem
  else addOverlay' mode f_m sem