-- | -- Module : Text.Megaparsec.Perm -- Copyright : © 2015–2016 Megaparsec contributors -- © 2007 Paolo Martini -- © 1999–2001 Daan Leijen -- License : FreeBSD -- -- Maintainer : Mark Karpov <markkarpov@opmbx.org> -- Stability : experimental -- Portability : non-portable -- -- This module implements permutation parsers. 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. {-# LANGUAGE ExistentialQuantification #-} module Text.Megaparsec.Perm ( PermParser , makePermParser , (<$$>) , (<$?>) , (<||>) , (<|?>) ) where import Text.Megaparsec.Combinator (choice) import Text.Megaparsec.Prim #if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>), (<*>)) #endif infixl 1 <||>, <|?> infixl 2 <$$>, <$?> -- | The type @PermParser s m a@ denotes a permutation parser that, -- when converted by the 'makePermParser' function, produces instance of -- 'MonadParsec' @m@ that parses @s@ stream and returns a value of type @a@ -- on success. -- -- Normally, a permutation parser is first build with special operators like -- ('<||>') and than transformed into a normal parser using -- 'makePermParser'. data PermParser s m a = Perm (Maybe a) [Branch s m a] data Branch s m a = forall b. Branch (PermParser s m (b -> a)) (m b) -- | The parser @makePermParser perm@ parses a permutation of parser described -- by @perm@. For example, suppose we want to parse a permutation of: an -- optional string of @a@'s, the character @b@ and an optional @c@. This can -- be described by: -- -- > test = makePermParser $ -- > (,,) <$?> ("", some (char 'a')) -- > <||> char 'b' -- > <|?> ('_', char 'c') makePermParser :: MonadParsec s m t => PermParser s m a -> m a makePermParser (Perm def xs) = choice (fmap branch xs ++ empty) where empty = case def of Nothing -> [] Just x -> [return x] branch (Branch perm p) = flip ($) <$> p <*> makePermParser perm -- | 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. (<$$>) :: MonadParsec s m t => (a -> b) -> m a -> PermParser s m b f <$$> p = newperm f <||> p -- | The expression @f \<$?> (x, 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 -- optional — if it cannot be applied, the default value @x@ will be used -- instead. (<$?>) :: MonadParsec s m t => (a -> b) -> (a, m a) -> PermParser s m b f <$?> xp = newperm f <|?> xp -- | 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@. (<||>) :: MonadParsec s m t => PermParser s m (a -> b) -> m a -> PermParser s m b (<||>) = add -- | 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@. (<|?>) :: MonadParsec s m t => PermParser s m (a -> b) -> (a, m a) -> PermParser s m b perm <|?> (x, p) = addopt perm x p newperm :: MonadParsec s m t => (a -> b) -> PermParser s m (a -> b) newperm f = Perm (Just f) [] add :: MonadParsec s m t => PermParser s m (a -> b) -> m a -> PermParser s m b add perm@(Perm _mf fs) p = Perm Nothing (first : fmap insert fs) where first = Branch perm p insert (Branch perm' p') = Branch (add (mapPerms flip perm') p) p' addopt :: MonadParsec s m t => PermParser s m (a -> b) -> a -> m a -> PermParser s m b addopt perm@(Perm mf fs) x p = Perm (fmap ($ x) mf) (first : fmap insert fs) where first = Branch perm p insert (Branch perm' p') = Branch (addopt (mapPerms flip perm') x p) p' mapPerms :: MonadParsec s m t => (a -> b) -> PermParser s m a -> PermParser s m b mapPerms f (Perm x xs) = Perm (fmap f x) (fmap mapBranch xs) where mapBranch (Branch perm p) = Branch (mapPerms (f .) perm) p