{-# LANGUAGE RankNTypes, GADTs, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, UndecidableInstances, NoMonomorphismRestriction #-} -- | This module contains a large variety of combinators for list-like structures. the extension @_ng@ indicates that -- that variant is the non-greedy variant. -- See the "Text.ParserCombinators.UU.Demo.Examples" module for some examples of their use. module Text.ParserCombinators.UU.Derived where import Text.ParserCombinators.UU.Core -- * Some aliases for oft occurring constructs -- | @`pReturn`@ is defined for upwards compatibility -- pReturn :: Applicative p => a -> p a pReturn = pure -- | @`pFail`@ is defined for upwards compatibility, and is the unit for @<|>@ -- pFail :: Alternative p => p a pFail = empty -- | `pMaybe` greedily recognises its argument. If not @Nothing@ is returned. -- pMaybe :: IsParser p => p a -> p (Maybe a) pMaybe p = must_be_non_empty "pMaybe" p (Just <$> p `opt` Nothing) -- | `pEither` recognises either one of its arguments. -- pEither :: IsParser p => p a -> p b -> p (Either a b) pEither p q = Left <$> p <|> Right <$> q -- | `<$$>` is the version of `<$>` which flips the function argument -- (<$$>) :: IsParser p => (a -> b -> c) -> p b -> p (a -> c) f <$$> p = flip f <$> p -- | `` parses an optional postfix element and applies its result to its left hand result -- () :: IsParser p => p a -> p (a -> a) -> p a p q = must_be_non_empty "" q (p <**> (q `opt` id)) infixl 4 -- | `pMany` is equivalent to the `many` from "Control.Applicative". We want however all our parsers to start with a lower case @p@. pMany :: IsParser p => p a -> p [a] pMany p = pList p -- | `pSome` is equivalent to the `some` from "Control.Applicative". We want however all our parsers to start with a lower case @p@. pSome :: (IsParser f) => f a -> f [a] pSome p = (:) <$> p <*> pList p -- | @`pPacked`@ surrounds its third parser with the first and the second one, returning only the middle result pPacked :: IsParser p => p b1 -> p b2 -> p a -> p a pPacked l r x = l *> x <* r -- * Iterating combinators, all in a greedy (default) and a non-greedy (ending with @_ng@) variant -- ** Recognising list like structures pFoldr :: IsParser p => (a -> a1 -> a1, a1) -> p a -> p a1 pFoldr alg@(op,e) p = must_be_non_empty "pFoldr" p pfm where pfm = (op <$> p <*> pfm) `opt` e pFoldr_ng :: IsParser p => (a -> a1 -> a1, a1) -> p a -> p a1 pFoldr_ng alg@(op,e) p = must_be_non_empty "pFoldr_ng" p pfm where pfm = (op <$> p <*> pfm) <|> pure e pFoldr1 :: IsParser p => (v -> b -> b, b) -> p v -> p b pFoldr1 alg@(op,e) p = must_be_non_empty "pFoldr1" p (op <$> p <*> pFoldr alg p) pFoldr1_ng :: IsParser p => (v -> b -> b, b) -> p v -> p b pFoldr1_ng alg@(op,e) p = must_be_non_empty "pFoldr1_ng" p (op <$> p <*> pFoldr_ng alg p) list_alg :: (a -> [a] -> [a], [a1]) list_alg = ((:), []) pList :: IsParser p => p a -> p [a] pList p = must_be_non_empty "pList" p (pFoldr list_alg p) pList_ng :: IsParser p => p a -> p [a] pList_ng p = must_be_non_empty "pList_ng" p (pFoldr_ng list_alg p) pList1 :: IsParser p => p a -> p [a] pList1 p = must_be_non_empty "pList" p (pFoldr1 list_alg p) pList1_ng :: IsParser p => p a -> p [a] pList1_ng p = must_be_non_empty "pList_ng" p (pFoldr1_ng list_alg p) -- * Recognising list structures with separators pFoldrSep :: IsParser p => (v -> b -> b, b) -> p a -> p v -> p b pFoldrSep alg@(op,e) sep p = must_be_non_empties "pFoldrSep" sep p (op <$> p <*> pFoldr alg sepp `opt` e) where sepp = sep *> p pFoldrSep_ng :: IsParser p => (v -> b -> b, b) -> p a -> p v -> p b pFoldrSep_ng alg@(op,e) sep p = must_be_non_empties "pFoldrSep" sep p (op <$> p <*> pFoldr_ng alg sepp <|> pure e) where sepp = sep *> p pFoldr1Sep :: IsParser p => (a -> b -> b, b) -> p a1 ->p a -> p b pFoldr1Sep alg@(op,e) sep p = must_be_non_empties "pFoldr1Sep" sep p pfm where pfm = op <$> p <*> pFoldr alg (sep *> p) pFoldr1Sep_ng :: IsParser p => (a -> b -> b, b) -> p a1 ->p a -> p b pFoldr1Sep_ng alg@(op,e) sep p = must_be_non_empties "pFoldr1Sep_ng" sep p pfm where pfm = op <$> p <*> pFoldr_ng alg (sep *> p) pListSep :: IsParser p => p a1 -> p a -> p [a] pListSep sep p = must_be_non_empties "pListSep" sep p (pFoldrSep list_alg sep p) pListSep_ng :: IsParser p => p a1 -> p a -> p [a] pListSep_ng sep p = must_be_non_empties "pListSep_ng" sep p pFoldrSep_ng list_alg sep p pList1Sep :: IsParser p => p a1 -> p a -> p [a] pList1Sep s p = must_be_non_empties "pListSep" s p (pFoldr1Sep list_alg s p) pList1Sep_ng :: IsParser p => p a1 -> p a -> p [a] pList1Sep_ng s p = must_be_non_empties "pListSep_ng" s p (pFoldr1Sep_ng list_alg s p) -- * Combinators for chained structures -- ** Treating the operator as right associative pChainr :: IsParser p => p (c -> c -> c) -> p c -> p c pChainr op x = must_be_non_empties "pChainr" op x r where r = x (flip <$> op <*> r) pChainr_ng :: IsParser p => p (c -> c -> c) -> p c -> p c pChainr_ng op x = must_be_non_empties "pChainr_ng" op x r where r = x <**> ((flip <$> op <*> r) <|> pure id) -- ** Treating the operator as left associative pChainl :: IsParser p => p (c -> c -> c) -> p c -> p c pChainl op x = must_be_non_empties "pChainl" op x (f <$> x <*> pList (flip <$> op <*> x)) where f x [] = x f x (func:rest) = f (func x) rest pChainl_ng :: IsParser p => p (c -> c -> c) -> p c -> p c pChainl_ng op x = must_be_non_empties "pChainl_ng" op x (f <$> x <*> pList_ng (flip <$> op <*> x)) where f x [] = x f x (func:rest) = f (func x) rest -- * Repeating parsers -- | `pExact` recognises a specified number of elements pExact :: (IsParser f) => Int -> f a -> f [a] pExact n p | n == 0 = pure [] | n > 0 = (:) <$> p <*> pExact (n-1) p pBetween :: (IsParser f) => Int -> Int -> f a -> f [a] pBetween m n p | n < 0 || m <0 = error "negative arguments to pBwteeen" | m > n = empty | otherwise = (++) <$> pExact m p <*> pAtMost (n-m) p pAtLeast :: (IsParser f) => Int -> f a -> f [a] pAtLeast n p = (++) <$> pExact n p <*> pList p pAtMost :: (IsParser f) => Int -> f a -> f [a] pAtMost n p | n > 0 = (:) <$> p <*> pAtMost (n-1) p `opt` [] | n == 0 = pure [] -- * Counting Parser -- | Count the number of times @p@ has succeeded pCount :: (IsParser p, Num b) => p a -> p b pCount p = (\_ b -> b+1) <$> p <*> pCount p `opt` 0 -- * Miscelleneous -- | Build a parser for each element in the argument list and try them all. pAny :: IsParser p => (a -> p a1) -> [a] -> p a1 pAny f l = foldr (<|>) pFail (map f l) -- | pSym was removed because the class Provides was eliminated -- pAnySym :: Provides st s s => [s] -> P st s -- pAnySym = pAny pSym