-- | -- Module : Control.Applicative.Permutations -- Copyright : © 2017 Mark Karpov -- License : BSD 3 clause -- -- Maintainer : Mark Karpov <markkarpov92@gmail.com> -- Stability : experimental -- Portability : portable -- -- This module is a generalization of the package @parsec-permutation@ -- authored by Samuel Hoffstaetter: -- -- https://hackage.haskell.org/package/parsec-permutation -- -- This module also takes inspiration from the algorithm is described in: -- /Parsing Permutation Phrases/, by Arthur Baars, Andres Loh and Doaitse -- Swierstra. Published as a functional pearl at the Haskell Workshop 2001. -- -- From these two works we derive a flexible and general method for parsing -- permutations over an 'Applicative' structure. Quite useful in conjunction -- with \"Free\" constructions of Applicatives, Monads, etc. -- -- Other permutation parsing libraries tend towards using special \"almost -- applicative\" combinators for construction which denies the library user -- the ability to lift and unlift permutation parsing into any 'Applicative' -- computational context. We redefine these combinators as convenience -- operators here alongside the equivalent 'Applicative' instance. -- -- For example, suppose we want to parse a permutation of: an optional -- string of @a@'s, the character @b@ and an optional @c@. Using a standard -- parsing library combinator @char@, this can be described using the -- 'Applicative' instance by: -- -- > test = runPermutation $ -- > (,,) <$> toPermutationWithDefault "" (some (char 'a')) -- > <*> toPermutation (char 'b') -- > <*> toPermutationWithDefault '_' (char 'c') -- -- Equivalently, this can also be describe using the convenience operators -- reminiscent of other parsing libraries: -- -- > test = runPermutation $ -- > (,,) <$?> ("", some (char 'a')) -- > <||> char 'b' -- > <|?> ('_', char 'c') -- -- @since 0.2.0 module Control.Applicative.Permutations ( -- ** Permutation type Permutation -- ** Permutation evaluators , runPermutation , intercalateEffect -- ** Permutation constructors , toPermutation , toPermutationWithDefault -- ** Convenience operators , (<$$>) , (<$?>) , (<||>) , (<|?>) ) where import Control.Applicative -- | An 'Applicative' wrapper-type for constructing permutation parsers. data Permutation m a = P (Maybe a) (m (Permutation m a)) instance Functor m => Functor (Permutation m) where fmap f (P v p) = P (f <$> v) (fmap f <$> p) instance Alternative m => Applicative (Permutation m) where pure value = P (Just value) empty lhs@(P f v) <*> rhs@(P g w) = P (f <*> g) (lhsAlt <|> rhsAlt) where lhsAlt = (<*> rhs) <$> v rhsAlt = (lhs <*>) <$> w -- | \"Unlifts\" a permutation parser into a parser to be evaluated. runPermutation :: ( Alternative m , Monad m) => Permutation m a -- ^ Permutation specification -> m a -- ^ Resulting base monad capable of handling the permutation runPermutation (P value parser) = optional parser >>= f where f Nothing = maybe empty pure value f (Just p) = runPermutation p -- | \"Unlifts\" a permutation parser into a parser to be evaluated with an -- intercalated effect. Useful for separators between permutation elements. -- -- For example, suppose that similar to above we want to parse a permutation of: -- an optional string of @a@'s, the character @b@ and an optional @c@. /However/, -- we also want each element of the permutation to be separated by a colon. -- Using a standard parsing library combinator @char@, this can be described -- using the 'Applicative' instance by: -- -- > test = intercalateEffect (char ':') $ -- > (,,) <$?> ("", some (char 'a')) -- > <||> char 'b' -- > <|?> ('_', char 'c') -- -- This will accept strings such as: \"a:b:c\", \"b:c:a\", \"b:aa\", \"b\", etc. -- -- Note that the effect is intercalated /between/ permutation components and that: -- -- - There is never an effect parsed preceeding the first component of the permutation -- -- - There is never an effect parsed following the last component of the permutation -- -- - No effects are intercalated between missing components with a default value. intercalateEffect :: ( Alternative m , Monad m) => m b -- ^ Effect to be intercalated between permutation components -> Permutation m a -- ^ Permutation specification -> m a -- ^ Resulting base monad capable of handling the permutation intercalateEffect = run noEffect where noEffect = pure () run :: (Alternative m, Monad m) => m c -> m b -> Permutation m a -> m a run headSep tailSep (P value parser) = optional headSep >>= f where f Nothing = maybe empty pure value f (Just _) = optional parser >>= g g Nothing = maybe empty pure value g (Just p) = run tailSep tailSep p -- | \"Lifts\" a parser to a permutation parser. toPermutation :: Alternative m => m a -- ^ Permutation component -> Permutation m a toPermutation p = P Nothing $ pure <$> p -- | \"Lifts\" a parser with a default value to a permutation parser. -- -- If no permutation containing the supplied parser can be parsed from the input, -- then the supplied default value is returned in lieu of a parse result. toPermutationWithDefault :: Alternative m => a -- ^ Default Value -> m a -- ^ Permutation component -> Permutation m a toPermutationWithDefault v p = P (Just v) $ pure <$> p infixl 1 <||>, <|?> infixl 2 <$$>, <$?> -- | The expression @f \<$$> p@ creates a fresh permutation parser -- consisting of parser @p@. The the final result of the permutation parser -- is the function @f@ applied to the return value of @p@. The parser @p@ is -- not allowed to accept empty input—use the optional combinator ('<$?>') -- instead. -- -- If the function @f@ takes more than one parameter, the type variable @b@ -- is instantiated to a functional type which combines nicely with the adds -- parser @p@ to the ('<||>') combinator. This results in stylized code -- where a permutation parser starts with a combining function @f@ followed -- by the parsers. The function @f@ gets its parameters in the order in -- which the parsers are specified, but actual input can be in any order. (<$$>) :: Alternative m => (a -> b) -- ^ Function to use on result of parsing -> m a -- ^ Normal parser -> Permutation m b -- ^ Permutation parser build from it f <$$> c = toPermutation $ f <$> c -- | The expression @f \<$?> (x, p)@ creates a fresh permutation parser -- consisting of parser @p@. The final result of the permutation parser is -- the function @f@ applied to the return value of @p@. The parser @p@ is -- optional—if it cannot be applied, the default value @x@ will be used -- instead. (<$?>) :: Alternative m => (a -> b) -- ^ Function to use on result of parsing -> (a, m a) -- ^ Default value and parser -> Permutation m b -- ^ Permutation parser f <$?> (v,c) = f <$> toPermutationWithDefault v c -- | The expression @perm \<||> p@ adds parser @p@ to the permutation parser -- @perm@. The parser @p@ is not allowed to accept empty input—use the -- optional combinator ('<|?>') instead. Returns a new permutation parser -- that includes @p@. (<||>) :: Alternative m => Permutation m (a -> b) -- ^ Given permutation parser -> m a -- ^ Parser to add (should not accept empty input) -> Permutation m b -- ^ Resulting parser p <||> c = p <*> toPermutation c -- | The expression @perm \<||> (x, p)@ adds parser @p@ to the permutation -- parser @perm@. The parser @p@ is optional—if it cannot be applied, the -- default value @x@ will be used instead. Returns a new permutation parser -- that includes the optional parser @p@. (<|?>) :: Alternative m => Permutation m (a -> b) -- ^ Given permutation parser -> (a, m a) -- ^ Default value and parser -> Permutation m b -- ^ Resulting parser p <|?> (v,c) = p <*> toPermutationWithDefault v c