packages feed

comparse-0.2.0.0: Control/Monad/Parser/Class.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}

module Control.Monad.Parser.Class
  ( module Control.Monad.Parser.Class,
  )
where

import Control.Applicative ((<**>))
import Data.Kind (Type)
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NonEmpty
import Data.Stream

infixl 3 <|>

infixl 1 <?>

-- | A monad with parsing capabilities.
class (Monad m, Stream (Input m)) => MonadParser m where
  type Input m :: Type

  -- | The current input stream.
  parseStream :: m (Input m)

  -- | Replace the input stream.
  setParseStream :: Input m -> m ()

  -- | A parser that always fails.
  noParse :: m a

  -- | A parser that returns the next item.
  item :: m (Item (Input m))

  -- | @followedBy p@ is a parser that succeeds if @p@ succeeds, but it does not
  -- consume any input.
  followedBy :: m a -> m ()

  -- | @notFollowedBy p@ is a parser that only succeeds if @p@ fails. This
  -- parser will not consume any input.
  notFollowedBy :: m a -> m ()

  -- | @try p@ is a parser that does everything like @p@, except it forcefully
  -- resets the position of any error reported by @p@ to the current position.
  try :: m a -> m a

  -- | @p <|> q@ is a parser that is equivalent to @p@ when @p@ succeeds and
  -- @q@ when @p@ fails to parse anything.
  (<|>) :: m a -> m a -> m a

  -- | @p <?> msg@ is a parser that behaves like @p@, but when @p@ fails, it
  -- reports an error indicating that @msg@ was the expected input.
  (<?>) :: m a -> String -> m a

-- | Parser that succeeds if the stream is empty. Does not consume any items.
eof :: MonadParser m => m ()
eof = notFollowedBy item <?> "end of input"

-- | Fail with an "expected" message.
expected :: MonadParser m => String -> m a
expected s = noParse <?> s

-- | Succeeds only if the value parsed by the parser satisfies the predicate.
satisfy :: MonadParser m => m a -> (a -> Bool) -> m a
satisfy p f = try $ do
  i <- p
  if f i
    then return i
    else noParse

-- | Parse a single item satisfying the given predicate.
match :: MonadParser m => (Item (Input m) -> Bool) -> m (Item (Input m))
match = satisfy item

-- | Make a parser optional.
optional :: MonadParser m => m a -> m (Maybe a)
optional p = Just <$> p <|> pure Nothing

-- | Try a series of parsers in order, returning the first one that succeeds.
choice :: MonadParser m => [m a] -> m a
choice = foldr (<|>) noParse

-- | Try to run the given parser as many times as possible.
many :: MonadParser m => m a -> m [a]
many p = ((:) <$> p <*> many p) <|> pure []

-- | Try to run the given parser as many times as possible, but at least once.
-- The result is returned as a regular list, but is guaranteed to be non-empty.
many1 :: MonadParser m => m a -> m [a]
many1 p = (:) <$> p <*> many p

-- | Try to run the given parser as many times as possible, but at least once.
some :: MonadParser m => m a -> m (NonEmpty a)
some p = (:|) <$> p <*> many p

-- | Parse a non-empty series of @a@ separated by @b@s (without a trailing @b@).
sepBy1 :: MonadParser m => m a -> m b -> m (NonEmpty a)
sepBy1 a b = (:|) <$> a <*> many (b *> a)

-- | Parse a potentially empty series of @a@ separated by @b@s (without a
-- trailing @b@).
sepBy :: MonadParser m => m a -> m b -> m [a]
sepBy a b = NonEmpty.toList <$> sepBy1 a b <|> pure []

-- | Parse any value equal to @a@.
like ::
  (MonadParser m, Eq (Item (Input m)), Show (Item (Input m))) =>
  Item (Input m) ->
  m (Item (Input m))
like a = item `satisfy` (== a) <?> show a

-- | Parse any value not equal to @a@.
unlike ::
  (MonadParser m, Eq (Item (Input m)), Show (Item (Input m))) =>
  Item (Input m) ->
  m (Item (Input m))
unlike a = item `satisfy` (/= a) <?> "anything but " ++ show a

-- | Parse a continuous sequence of items equal to the given one.
string ::
  (MonadParser m, Eq (Item (Input m)), Show (Item (Input m))) =>
  [Item (Input m)] ->
  m [Item (Input m)]
string [] = return []
string (x : xs) = like x >> string xs >> return (x : xs)

-- | Parse any value equal to at least one element of the given list.
oneOf ::
  (MonadParser m, Eq (Item (Input m)), Show (Item (Input m))) =>
  [Item (Input m)] ->
  m (Item (Input m))
oneOf l = item `satisfy` (`elem` l) <?> "one of " ++ show l

-- | Parse any value not equivalent to any element of the given list.
-- For a version that accepts non-Show items, see @noneOf'@.
noneOf ::
  (MonadParser m, Eq (Item (Input m)), Show (Item (Input m))) =>
  [Item (Input m)] ->
  m (Item (Input m))
noneOf l = item `satisfy` (`notElem` l) <?> "none of " ++ show l

-- | @chainl1 p op@ Parse a chain of *one* or more occurrences of @p@,
-- separated by @op@. Return a value obtained by a left associative application
-- of all functions returned by @op@ to the values returned by @p@.
--
-- This is particularly useful for parsing left associative infix operators.
chainl1 :: MonadParser m => m a -> m (a -> a -> a) -> m a
chainl1 p op = scan
  where
    scan = p <**> rst
    rst = (\f y g x -> g (f x y)) <$> op <*> p <*> rst <|> pure id

-- | @chainr1 p op@ Parse a chain of *one* or more occurrences of @p@,
-- separated by @op@. Return a value obtained by a right associative application
-- of all functions returned by @op@ to the values returned by @p@.
--
-- This is particularly useful for parsing right associative infix operators.
chainr1 :: MonadParser m => m a -> m (a -> a -> a) -> m a
chainr1 p op = scan
  where
    scan = p <**> rst
    rst = (flip <$> op <*> scan) <|> pure id

-- | Run a parser on a different stream of items.
withInput :: MonadParser m => Input m -> m a -> m (a, Input m)
withInput s' p = do
  s <- parseStream
  setParseStream s'
  x <- p
  s'' <- parseStream
  setParseStream s
  return (x, s'')