pinch-0.4.0.0: src/Pinch/Internal/Pinchable/Parser.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE Rank2Types #-}
-- |
-- Module : Pinch.Internal.Pinchable.Parser
-- Copyright : (c) Abhinav Gupta 2015
-- License : BSD3
--
-- Maintainer : Abhinav Gupta <mail@abhinavg.net>
-- 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
import qualified Control.Monad.Fail as Fail
-- | 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 return #-}
return = pure
{-# INLINE (>>) #-}
(>>) = (*>)
{-# INLINE (>>=) #-}
Parser a' >>= k =
Parser $ \kFail kSuccB ->
a' kFail $ \a ->
unParser (k a) kFail kSuccB
#if !MIN_VERSION_base(4,13,0)
-- Monad(fail) was removed in GHC 8.8.1
{-# INLINE fail #-}
fail = Fail.fail
#endif
instance Fail.MonadFail Parser where
{-# INLINE fail #-}
fail msg = Parser $ \kFail _ -> kFail msg
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 p f g = unParser p f g
{-# INLINE parserCatch #-}