mini-2.0.0.0: src/Mini/Transformers/Parser.hs
-- | Extend a monad with the ability to parse symbol sequences
module Mini.Transformers.Parser (
-- * Type
ParserT (
ParserT
),
runParserT,
-- * Parsers
eof,
item,
look,
noneOf,
oneOf,
peek,
sat,
string,
symbol,
-- * Combinators
accept,
atLeast,
atMost,
between,
chainl1,
chainr1,
findAll,
findAll1,
findFirst,
findLast,
option,
range,
reject,
sepBy,
sepBy1,
till,
till1,
) where
import Control.Applicative (
Alternative,
empty,
many,
some,
(<|>),
)
import Control.Monad (
ap,
liftM,
replicateM,
)
import Control.Monad.IO.Class (
MonadIO,
liftIO,
)
import Data.Bifunctor (
first,
)
import Data.Bool (
bool,
)
import Mini.Data.Recursion (
list,
)
import Mini.Random.Class (
Random,
random,
)
import Mini.Transformers.Class (
MonadTrans,
lift,
)
import Prelude (
Applicative,
Bool (
True
),
Eq,
Foldable,
Functor,
Int,
Maybe (
Just,
Nothing
),
Monad,
MonadFail,
Monoid,
Semigroup,
Traversable,
const,
elem,
fail,
flip,
fmap,
maybe,
mempty,
notElem,
pure,
traverse,
($),
(*>),
(-),
(.),
(<$),
(<$>),
(<*),
(<*>),
(<>),
(==),
(>),
(>>=),
)
-- Type
-- | A transformer parsing symbols /s/, inner monad /m/, return /a/
newtype ParserT s m a = ParserT
{ runParserT :: [s] -> m (Maybe (a, [s]))
-- ^ Unwrap a transformer computation with a sequence of symbols to parse
}
instance (Monad m) => Functor (ParserT s m) where
fmap = liftM
instance (Monad m) => Applicative (ParserT s m) where
pure a = ParserT $ \ss -> pure (Just (a, ss))
(<*>) = ap
instance (Monad m) => Alternative (ParserT s m) where
empty = ParserT . const $ pure Nothing
m <|> n = ParserT $ \ss -> (<|>) <$> runParserT m ss <*> runParserT n ss
instance (Monad m) => Monad (ParserT s m) where
m >>= k =
ParserT $ \ss ->
runParserT m ss
>>= maybe
(pure Nothing)
(\(a, ss') -> runParserT (k a) ss')
instance MonadTrans (ParserT s) where
lift m = ParserT $ \ss -> Just . (\a -> (a, ss)) <$> m
instance (Monad m, Semigroup a) => Semigroup (ParserT s m a) where
m <> n = (<>) <$> m <*> n
instance (Monad m, Monoid a) => Monoid (ParserT s m a) where
mempty = pure mempty
instance (Monad m) => MonadFail (ParserT s m) where
fail = const empty
instance (MonadIO m) => MonadIO (ParserT s m) where
liftIO = lift . liftIO
instance (Monad m, Random a) => Random (ParserT s m a) where
random = first pure . random
-- Parsers
-- | Parse successfully only at end of input
eof :: (Monad m) => ParserT s m ()
eof = reject item
-- | Parse any symbol
item :: (Applicative m) => ParserT s m s
item = sat $ const True
-- | Parse the rest of the input without consuming it
look :: (Applicative m) => ParserT s m [s]
look = ParserT $ \ss -> pure $ Just (ss, ss)
-- | Parse the next symbol if excluded from a collection
noneOf :: (Applicative m, Foldable t, Eq s) => t s -> ParserT s m s
noneOf = sat . flip notElem
-- | Parse the next symbol if included in a collection
oneOf :: (Applicative m, Foldable t, Eq s) => t s -> ParserT s m s
oneOf = sat . flip elem
-- | Parse the next symbol without consuming it
peek :: (Monad m) => ParserT s m s
peek = accept item
-- | Parse the next symbol if it satisfies a predicate
sat :: (Applicative m) => (s -> Bool) -> ParserT s m s
sat p =
ParserT $
pure
. list
Nothing
( \s ss _ ->
bool
Nothing
(Just (s, ss))
$ p s
)
-- | Parse a sequence of symbols
string :: (Monad m, Traversable t, Eq s) => t s -> ParserT s m (t s)
string = traverse symbol
-- | Parse a specific symbol
symbol :: (Applicative m, Eq s) => s -> ParserT s m s
symbol = sat . (==)
-- Combinators
-- | Parse @p@, without consuming input, iff @p@ succeeds via @accept p@
accept :: (Monad m) => ParserT s m a -> ParserT s m a
accept p = ParserT $ \ss -> fmap (\(a, _) -> (a, ss)) <$> runParserT p ss
-- | Parse @n@ or more occurrences of @p@ via @atLeast n p@
atLeast :: (Monad m) => Int -> ParserT s m a -> ParserT s m [a]
atLeast n p = replicateM n p <> many p
-- | Parse up to @n@ occurrences of @p@ via @atMost n p@
atMost :: (Monad m) => Int -> ParserT s m a -> ParserT s m [a]
atMost n p =
bool
(pure [])
(option [] $ (:) <$> p <*> atMost (n - 1) p)
$ n > 0
-- | Parse @p@ enclosed by @a@ and @b@ via @between a b p@
between
:: (Monad m)
=> ParserT s m open
-> ParserT s m close
-> ParserT s m a
-> ParserT s m a
between open close p = open *> p <* close
-- | Parse one or more @p@ left-associatively chained by @f@ via @chainl1 p f@
chainl1
:: (Monad m)
=> ParserT s m a
-> ParserT s m (a -> a -> a)
-> ParserT s m a
chainl1 p f = p >>= go
where
go a = option a $ f <*> pure a <*> p >>= go
-- | Parse one or more @p@ right-associatively chained by @f@ via @chainr1 p f@
chainr1
:: (Monad m)
=> ParserT s m a
-> ParserT s m (a -> a -> a)
-> ParserT s m a
chainr1 p f = go
where
go = p >>= rest
rest a = option a $ f <*> pure a <*> go >>= rest
-- | Find and parse zero or more occurrences of @p@ via @findAll p@
findAll :: (Monad m) => ParserT s m a -> ParserT s m [a]
findAll = many . findFirst
-- | Find and parse one or more occurrences of @p@ via @findAll1 p@
findAll1 :: (Monad m) => ParserT s m a -> ParserT s m [a]
findAll1 = some . findFirst
-- | Find and parse the first occurrence of @p@ via @findFirst p@
findFirst :: (Monad m) => ParserT s m a -> ParserT s m a
findFirst p = p <|> (item *> findFirst p)
-- | Find and parse the last occurrence of @p@ via @findLast p@
findLast :: (Monad m) => ParserT s m a -> ParserT s m a
findLast p = findFirst p >>= flip option (findLast p)
-- | Parse @p@ returning @a@ in case of failure via @option a p@
option :: (Monad m) => a -> ParserT s m a -> ParserT s m a
option a p = p <|> pure a
-- | Parse between @lo@ and @hi@ occurrences of @p@ via @range lo hi p@
range :: (Monad m) => Int -> Int -> ParserT s m a -> ParserT s m [a]
range lo hi p = replicateM lo p <> atMost (hi - lo) p
-- | Parse @p@, without consuming input, iff @p@ fails via @reject p@
reject :: (Monad m) => ParserT s m a -> ParserT s m ()
reject p = ParserT $ \ss ->
runParserT p ss
>>= maybe
(pure $ Just ((), ss))
(const $ pure Nothing)
-- | Parse zero or more @p@ separated by @q@ via @p \`sepBy\` q@
sepBy :: (Monad m) => ParserT s m a -> ParserT s m b -> ParserT s m [a]
sepBy p = option [] . sepBy1 p
-- | Parse one or more @p@ separated by @q@ via @p \`sepBy1\` q@
sepBy1 :: (Monad m) => ParserT s m a -> ParserT s m b -> ParserT s m [a]
sepBy1 p sep = (:) <$> p <*> many (sep *> p)
-- | Parse zero or more @p@ until @q@ succeeds via @p \`till\` q@
till :: (Monad m) => ParserT s m a -> ParserT s m b -> ParserT s m [a]
till p end = ([] <$ end) <|> till1 p end
-- | Parse one or more @p@ until @q@ succeeds via @p \`till1\` q@
till1 :: (Monad m) => ParserT s m a -> ParserT s m b -> ParserT s m [a]
till1 p end = (:) <$> p <*> till p end