{-# LANGUAGE Rank2Types #-} -- | -- Module : Pinch.Internal.Pinchable.Parser -- Copyright : (c) Abhinav Gupta 2015 -- License : BSD3 -- -- Maintainer : Abhinav Gupta -- Stability : experimental -- -- Implements a continuation based version of the @Either e@ monad. -- module Pinch.Internal.Pinchable.Parser ( Parser , runParser , parserCatch ) where import Control.Applicative import Control.Monad -- | Failure continuation. Called with the failure message. type Failure r = String -> r type Success a r = a -> r -- ^ Success continuation. Called with the result. -- | A simple continuation-based parser. -- -- This is just @Either e a@ in continuation-passing style. newtype Parser a = Parser { unParser :: forall r. Failure r -- Failure continuation -> Success a r -- Success continuation -> r } -- TODO can probably track position in the struct instance Functor Parser where {-# INLINE fmap #-} fmap f (Parser g) = Parser $ \kFail kSucc -> g kFail (kSucc . f) instance Applicative Parser where {-# INLINE pure #-} pure a = Parser $ \_ kSucc -> kSucc a {-# INLINE (<*>) #-} Parser f' <*> Parser a' = Parser $ \kFail kSuccB -> f' kFail $ \f -> a' kFail $ \a -> kSuccB (f a) instance Alternative Parser where {-# INLINE empty #-} empty = Parser $ \kFail _ -> kFail "Alternative.empty" {-# INLINE (<|>) #-} Parser l' <|> Parser r' = Parser $ \kFail kSucc -> l' (\_ -> r' kFail kSucc) kSucc instance Monad Parser where {-# INLINE fail #-} fail msg = Parser $ \kFail _ -> kFail msg {-# INLINE return #-} return = pure {-# INLINE (>>) #-} (>>) = (*>) {-# INLINE (>>=) #-} Parser a' >>= k = Parser $ \kFail kSuccB -> a' kFail $ \a -> unParser (k a) kFail kSuccB instance MonadPlus Parser where {-# INLINE mzero #-} mzero = empty {-# INLINE mplus #-} mplus = (<|>) -- | Run a @Parser@ and return the result inside an @Either@. runParser :: Parser a -> Either String a runParser p = unParser p Left Right -- | Allows handling parse errors. parserCatch :: Parser a -> (String -> Parser b) -> (a -> Parser b) -> Parser b parserCatch = unParser {-# INLINE parserCatch #-}