module Data.Machine.Stack
  ( Stack(..)
  , stack
  , peek
  , pop
  , push
  ) where
import Data.Machine.Plan
import Data.Machine.Type
data Stack a r where
  Push :: a -> Stack a ()
  Pop  ::      Stack a a
peek :: Plan (Stack a) b a
peek = do
  a <- pop
  push a
  return a
push :: a -> Plan (Stack a) b ()
push a = awaits (Push a)
pop :: Plan (Stack a) b a
pop = awaits Pop
stack :: Monad m => MachineT m k a -> MachineT m (Stack a) o -> MachineT m k o
stack up down =
  stepMachine down $ \stepD     ->
  case stepD of
    Stop                     -> stopped
    Yield o down'            -> encased (Yield o (up `stack` down'))
    Await down' (Push a) _   -> encased (Yield a up) `stack` down' ()
    Await down' Pop ffD      ->
      stepMachine up $ \stepU   ->
      case stepU of
        Stop                 -> stopped `stack` ffD
        Yield o up'          -> up'     `stack` down' o
        Await up' req ffU    -> encased (Await (\a -> up' a `stack` encased stepD) req
                                               (      ffU   `stack` encased stepD))