megaparsec/Text/MegaParsec/Perm.hs

178 lines
6.3 KiB
Haskell
Raw Normal View History

2008-01-13 20:53:15 +03:00
-- |
2015-07-28 16:32:19 +03:00
-- Module : Text.MegaParsec.Perm
-- Copyright : © 19992001 Daan Leijen, © 2007 Paolo Martini, © 2015 MegaParsec contributors
-- License : BSD3
--
-- Maintainer : Mark Karpov <markkarpov@opmbx.org>
2008-01-13 20:53:15 +03:00
-- Stability : provisional
-- Portability : non-portable (uses existentially quantified data constructors)
2015-07-28 16:32:19 +03:00
--
-- This module implements permutation parsers. The algorithm used is fairly
-- complex since we push the type system to its limits :-) 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.
2008-01-13 20:53:15 +03:00
{-# LANGUAGE ExistentialQuantification #-}
2015-07-28 16:32:19 +03:00
module Text.MegaParsec.Perm
( PermParser
, StreamPermParser -- abstract
2008-01-13 20:53:15 +03:00
, permute
2015-07-28 16:32:19 +03:00
, (<||>)
, (<$$>)
, (<|?>)
, (<$?>) )
where
2008-01-13 20:53:15 +03:00
import Control.Monad.Identity
2015-07-28 16:32:19 +03:00
import Text.MegaParsec
2008-01-13 20:53:15 +03:00
infixl 1 <||>, <|?>
infixl 2 <$$>, <$?>
{---------------------------------------------------------------
test -- parse a permutation of
* an optional string of 'a's
* a required 'b'
* an optional 'c'
---------------------------------------------------------------}
2008-02-13 07:32:24 +03:00
{-
2008-01-13 20:53:15 +03:00
test input
= parse (do{ x <- ptest; eof; return x }) "" input
ptest :: Parser (String,Char,Char)
ptest
= permute $
(,,) <$?> ("",many1 (char 'a'))
<||> char 'b'
<|?> ('_',char 'c')
2008-02-13 07:32:24 +03:00
-}
2008-01-13 20:53:15 +03:00
{---------------------------------------------------------------
Building a permutation parser
---------------------------------------------------------------}
-- | 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
2015-07-28 16:32:19 +03:00
-- new permutation parser that includes @p@.
(<||>) :: (Stream s Identity tok) => StreamPermParser s st (a -> b) -> Parsec s st a -> StreamPermParser s st b
2008-01-13 20:53:15 +03:00
(<||>) perm p = add perm p
-- | 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.
(<$$>) :: (Stream s Identity tok) => (a -> b) -> Parsec s st a -> StreamPermParser s st b
2008-01-13 20:53:15 +03:00
(<$$>) f p = newperm f <||> p
-- | The expression @perm \<||> (x,p)@ adds parser @p@ to the
-- permutation parser @perm@. The parser @p@ is optional - if it can
-- not be applied, the default value @x@ will be used instead. Returns
2015-07-28 16:32:19 +03:00
-- a new permutation parser that includes the optional parser @p@.
2008-01-13 20:53:15 +03:00
(<|?>) :: (Stream s Identity tok) => StreamPermParser s st (a -> b) -> (a, Parsec s st a) -> StreamPermParser s st b
(<|?>) perm (x,p) = addopt perm x p
2008-01-13 20:53:15 +03:00
-- | 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 can not be applied, the default value
2015-07-28 16:32:19 +03:00
-- @x@ will be used instead.
2008-01-13 20:53:15 +03:00
(<$?>) :: (Stream s Identity tok) => (a -> b) -> (a, Parsec s st a) -> StreamPermParser s st b
(<$?>) f (x,p) = newperm f <|?> (x,p)
2008-01-13 20:53:15 +03:00
{---------------------------------------------------------------
The permutation tree
---------------------------------------------------------------}
-- | Provided for backwards compatibility. The tok type is ignored.
type PermParser tok st a = StreamPermParser String st a
-- | The type @StreamPermParser s st a@ denotes a permutation parser that,
2015-07-28 16:32:19 +03:00
-- when converted by the 'permute' function, parses
-- @s@ streams with user state @st@ 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 'permute'.
data StreamPermParser s st a = Perm (Maybe a) [StreamBranch s st a]
2008-02-13 07:32:24 +03:00
-- type Branch st a = StreamBranch String st a
data StreamBranch s st a = forall b. Branch (StreamPermParser s st (b -> a)) (Parsec s st b)
-- | The parser @permute 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 = permute (tuple <$?> ("",many1 (char 'a'))
2015-07-28 16:32:19 +03:00
-- > <||> char 'b'
-- > <|?> ('_',char 'c'))
-- > where
-- > tuple a b c = (a,b,c)
2008-01-13 20:53:15 +03:00
-- transform a permutation tree into a normal parser
permute :: (Stream s Identity tok) => StreamPermParser s st a -> Parsec s st a
2008-01-13 20:53:15 +03:00
permute (Perm def xs)
= choice (map branch xs ++ empty)
where
empty
= case def of
Nothing -> []
Just x -> [return x]
branch (Branch perm p)
= do{ x <- p
; f <- permute perm
; return (f x)
}
-- build permutation trees
newperm :: (Stream s Identity tok) => (a -> b) -> StreamPermParser s st (a -> b)
2008-01-13 20:53:15 +03:00
newperm f
= Perm (Just f) []
add :: (Stream s Identity tok) => StreamPermParser s st (a -> b) -> Parsec s st a -> StreamPermParser s st b
2008-02-13 07:32:24 +03:00
add perm@(Perm _mf fs) p
2008-01-13 20:53:15 +03:00
= Perm Nothing (first:map insert fs)
where
first = Branch perm p
insert (Branch perm' p')
= Branch (add (mapPerms flip perm') p) p'
addopt :: (Stream s Identity tok) => StreamPermParser s st (a -> b) -> a -> Parsec s st a -> StreamPermParser s st b
2008-01-13 20:53:15 +03:00
addopt perm@(Perm mf fs) x p
= Perm (fmap ($ x) mf) (first:map insert fs)
where
first = Branch perm p
insert (Branch perm' p')
= Branch (addopt (mapPerms flip perm') x p) p'
mapPerms :: (Stream s Identity tok) => (a -> b) -> StreamPermParser s st a -> StreamPermParser s st b
2008-01-13 20:53:15 +03:00
mapPerms f (Perm x xs)
2008-02-13 07:32:24 +03:00
= Perm (fmap f x) (map mapBranch xs)
2008-01-13 20:53:15 +03:00
where
2008-02-13 07:32:24 +03:00
mapBranch (Branch perm p)
2008-01-13 20:53:15 +03:00
= Branch (mapPerms (f.) perm) p