module Rattletrap.Get where import qualified Control.Applicative as Applicative import qualified Control.Exception as Exception import qualified Data.Bifunctor as Bifunctor import qualified Rattletrap.Exception.Empty as Empty import qualified Rattletrap.Exception.Fail as Fail newtype Get s m a = Get (s -> m (Either ([String], Exception.SomeException) (s, a))) instance (Functor m) => Functor (Get s m) where fmap f g = Get $ fmap (fmap (fmap f)) . run g instance (Monad m) => Applicative (Get s m) where pure x = Get $ \s -> pure $ Right (s, x) gf <*> gx = Get $ \s1 -> do r <- run gf s1 case r of Left e -> pure $ Left e Right (s2, f) -> run (fmap f gx) s2 instance (Monad m) => Monad (Get s m) where g >>= f = Get $ \s1 -> do r <- run g s1 case r of Left e -> pure $ Left e Right (s2, x) -> run (f x) s2 instance (Monad m) => MonadFail (Get s m) where fail = throw . Fail.Fail instance (Monad m) => Applicative.Alternative (Get s m) where empty = throw Empty.Empty gx <|> gy = Get $ \s -> do r <- run gx s case r of Left _ -> run gy s Right x -> pure $ Right x run :: Get s m a -> s -> m (Either ([String], Exception.SomeException) (s, a)) run (Get f) = f get :: (Applicative m) => Get s m s get = Get $ \s -> pure $ Right (s, s) put :: (Applicative m) => s -> Get s m () put s = Get $ \_ -> pure $ Right (s, ()) lift :: (Functor m) => m a -> Get s m a lift m = Get $ \s -> fmap (\x -> Right (s, x)) m throw :: (Exception.Exception e, Applicative m) => e -> Get s m a throw = Get . const . pure . Left . (,) [] . Exception.toException embed :: (Monad m) => Get s m a -> s -> Get t m a embed g s = do r <- lift $ run g s case r of Left (ls, e) -> labels ls $ throw e Right (_, x) -> pure x labels :: (Functor m) => [String] -> Get s m a -> Get s m a labels ls g = Get $ fmap (Bifunctor.first $ Bifunctor.first (ls <>)) . run g label :: (Functor m) => String -> Get s m a -> Get s m a label = labels . pure