toml-parser-1.0.0.0: src/Toml/FromValue/Matcher.hs
{-|
Module : Toml.FromValue.Matcher
Description : A type for building results while tracking scopes
Copyright : (c) Eric Mertens, 2023
License : ISC
Maintainer : emertens@gmail.com
-}
module Toml.FromValue.Matcher (
Matcher,
runMatcher,
withScope,
getScope,
warning,
Result(..),
) where
import Control.Applicative (Alternative(..))
import Control.Monad (MonadPlus(..))
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (asks, local, ReaderT(..))
import Control.Monad.Trans.Writer.CPS (runWriterT, tell, WriterT)
-- | Computations that result in a 'Result' and which track a list
-- of nested contexts to assist in generating warnings and error
-- messages.
--
-- Use 'withScope' to run a 'Matcher' in a new, nested scope.
newtype Matcher a = Matcher (ReaderT [String] (WriterT (DList String) (Either String)) a)
deriving (Functor, Applicative, Monad)
type DList a = [a] -> [a]
-- | Computation outcome with error and warning messages.
data Result a
= Failure String -- error message
| Success [String] a -- warnings and result
deriving (Read, Show, Eq, Ord)
-- | Run a 'Matcher' with an empty scope.
runMatcher :: Matcher a -> Result a
runMatcher (Matcher m) =
case runWriterT (runReaderT m []) of
Left e -> Failure e
Right (x,w) -> Success (w []) x
-- | Run a 'Matcher' with a locally extended scope.
withScope :: String -> Matcher a -> Matcher a
withScope ctx (Matcher m) = Matcher (local (ctx:) m)
-- | Get the current list of scopes.
getScope :: Matcher [String]
getScope = Matcher (asks reverse)
-- | Emit a warning mentioning the current scope.
warning :: String -> Matcher ()
warning w =
do loc <- getScope
Matcher (lift (tell ((w ++ " in top" ++ concat loc):)))
-- | Fail with an error message annotated to the current location.
instance MonadFail Matcher where
fail e =
do loc <- getScope
Matcher (lift (lift (Left (e ++ " in top" ++ concat loc))))