{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_HADDOCK hide #-}
module Byline.Internal.Simulation
( Simulated (..),
SimulationFunction,
SimulationState (..),
BylineT (..),
runBylineT,
)
where
import Byline.Internal.Completion
import Byline.Internal.Eval (MonadByline (..))
import Byline.Internal.Prim
import Byline.Internal.Stylized
import Control.Monad.Catch (MonadCatch, MonadThrow)
import Control.Monad.Cont (MonadCont)
import Control.Monad.Except (MonadError)
import qualified Control.Monad.Trans.Free.Church as Free
import qualified Data.Text as Text
data Simulated
=
SimulatedInput Text
|
SimulatedEOF
type SimulationFunction m = StateT (SimulationState m) m Simulated
data SimulationState m = SimulationState
{
precedingPrompt :: Text,
simulationFunction :: SimulationFunction m,
completionFunctions :: [CompletionFunc IO]
}
newtype BylineT m a = BylineT
{unBylineT :: MaybeT (StateT (SimulationState m) m) a}
deriving newtype
( Functor,
Applicative,
Monad,
MonadIO,
MonadReader r,
MonadError e,
MonadCont,
MonadThrow,
MonadCatch
)
instance MonadState s m => MonadState s (BylineT m) where
state = lift . state
instance MonadTrans BylineT where
lift = BylineT . lift . lift
instance Monad m => MonadByline (BylineT m) where
liftByline = evalPrimF
evalPrimF :: forall m a. Monad m => Free.F PrimF a -> BylineT m a
evalPrimF = Free.iterM go
where
go :: PrimF (BylineT m a) -> BylineT m a
go = \case
Say _ k -> k
AskLn s d k -> simulate s $ \t ->
if Text.null t
then k (fromMaybe t d)
else k t
AskChar s k -> simulate s $ \t ->
if Text.null t
then BylineT empty
else k (Text.head t)
AskPassword s _ k -> simulate s k
PushCompFunc f k ->
BylineT
( lift . modify $ \st ->
st {completionFunctions = f : completionFunctions st}
)
>> k
PopCompFunc k ->
BylineT
( lift . modify $ \st ->
case completionFunctions st of
[] -> st {completionFunctions = []}
_ : xs -> st {completionFunctions = xs}
)
>> k
simulate :: Stylized Text -> (Text -> BylineT m b) -> BylineT m b
simulate s f = do
BylineT (modify $ \st -> st {precedingPrompt = renderText Plain s})
simfun <- BylineT (gets simulationFunction)
BylineT (lift simfun) >>= \case
SimulatedInput t -> f t
SimulatedEOF -> BylineT empty
runBylineT :: Monad m => SimulationFunction m -> BylineT m a -> m (Maybe a)
runBylineT f =
unBylineT
>>> runMaybeT
>>> (`evalStateT` SimulationState "" f [])