packages feed

JustParse (empty) → 1.0

raw patch · 7 files changed

+797/−0 lines, 7 filesdep +basesetup-changed

Dependencies added: base

Files

+ JustParse.cabal view
@@ -0,0 +1,20 @@+name:                JustParse+version:             1.0+synopsis:            A simple and comprehensive Haskell parsing library+description:         A simple and comprehensive Haskell parsing library+homepage:            https://github.com/grantslatton/JustParse+license:             PublicDomain+license-file:        LICENSE+author:              Grant Slatton+maintainer:          grantslatton@gmail.com+-- copyright:           +category:            Text+build-type:          Simple+cabal-version:       >=1.8++library+   exposed-modules: Data.JustParse+   other-modules: Data.JustParse.Internal, Data.JustParse.Language, Data.JustParse.Common+  build-depends:       base ==4.6.*+  hs-source-dirs: src+  extensions: MultiParamTypeClasses, Rank2Types, FlexibleInstances, FlexibleContexts, Safe
+ LICENSE view
@@ -0,0 +1,24 @@+This is free and unencumbered software released into the public domain.++Anyone is free to copy, modify, publish, use, compile, sell, or+distribute this software, either in source code form or as a compiled+binary, for any purpose, commercial or non-commercial, and by any+means.++In jurisdictions that recognize copyright laws, the author or authors+of this software dedicate any and all copyright interest in the+software to the public domain. We make this dedication for the benefit+of the public at large and to the detriment of our heirs and+successors. We intend this dedication to be an overt act of+relinquishment in perpetuity of all present and future rights to this+software under copyright law.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.++For more information, please refer to <http://unlicense.org>
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Data/JustParse.hs view
@@ -0,0 +1,141 @@+{-|+Module      : Data.JustParse+Description : The one-stop import for the library+Copyright   : Copyright Waived+License     : PublicDomain+Maintainer  : grantslatton@gmail.com+Stability   : experimental+Portability : portable++A simple and comprehensive Haskell parsing library.+-} ++{-# LANGUAGE Safe #-}+module Data.JustParse (+    -- * Overview+    -- $overview++    -- * Quickstart Examples+    -- *** Simple char and string parsing+    -- $quickstart1++    -- *** Basic combinatorial parsing+    -- $quickstart2++    -- *** Recursive combinatorial parsing+    -- $quickstart3++    -- * General Parsing+    C.Stream(..),+    C.Result(..),+    C.Parser( parse ),+    C.justParse,+    C.runParser,+    C.finalize,+    C.extend,+    C.isDone,+    C.isFail,+    C.isPartial,+    C.rename,+    (C.<?>),++    -- * Generic Parsers+    C.test,+    C.greedy,+    C.option,+    C.satisfy,+    C.mN,+    C.many,+    C.many1,+    C.manyN,+    C.atLeast,+    C.exactly,+    C.eof,+    C.oneOf,+    C.noneOf,+    C.anyToken,+    C.lookAhead,++    -- * Char Parsers+    C.char,+    C.anyChar,+    C.ascii,+    C.latin1,+    C.control,+    C.space,+    C.lower,+    C.upper,+    C.alpha,+    C.alphaNum,+    C.print,+    C.digit,+    C.octDigit,+    C.hexDigit,+    C.eol,++    -- * String Parsers+    C.string,++    -- * Regex Parsers+    L.regex,+    L.regex',+    L.Match(..)++) where++import Data.JustParse.Language as L+import Data.JustParse.Common as C++-- $overview+-- +-- * Allows for parsing arbitrary 'Stream' types+-- +-- * Makes extensive use of combinators+-- +-- * Returns relatively verbose 'Fail'ure messages.+-- +-- * Allows one to 'rename' a 'Parser'. +-- +-- * Allows for a parser to return a 'Partial' 'Result'+-- +-- * Non-greedy parsing+-- +-- * Returns a list of all possible parses+-- +-- * Allows for conversion of a 'regex' to a parser++-- $quickstart1+--+-- This parser will only accept the string @\"hello world\"@+--+-- @+--p = do                +--    h \<- char \'h\'                   \-\-Parses the character \'h\'+--    rest \<- string \"ello world\"     \-\-Parses the string \"ello world\"+--    exp \<- char \'!\'                 \-\-Parses the character \'!\'+--    return ([h]++rest++[exp])       \-\-Returns all of the above concatenated together+-- @++-- $quickstart2+--+-- This parser will accept the string @\"hello woooooorld\"@ with any number of @o@\'s.+-- It returns the number of @o@\'s.+--+-- @+--p = do+--    first \<- string \"hello w\"         \-\-Parses the string \"hello w\"+--    os \<- many1 (char \'o\')            \-\-Applies the parser \"char \'o\'\" one or more times+--    second \<- string \"rld\"            \-\-Parses the string \"rld\"+--    return (length os)                \-\-Return the number of o\'s parsed+-- @++-- $quickstart3+-- +-- This parser will turn a string of comma separated values into a list of them+--+-- @+--csv = do  +--    v \<- greedy (many (noneOf \",\"))       \-\-Parses as many non-comma characters as possible+--    vs \<- option [] (char \',\' >> csv)     \-\-Optionally parses a comma and a csv, returning the empty list upon failure+--    return (v:vs)                         \-\-Concatenates and returns the full list+-- @
+ src/Data/JustParse/Common.hs view
@@ -0,0 +1,284 @@+{-|+Module      : Data.JustParse.Common+Description : Common Parser Combinators+Copyright   : Copyright Waived+License     : PublicDomain+Maintainer  : grantslatton@gmail.com+Stability   : experimental+Portability : portable++Many common parsing needs in one place.+-}++{-# LANGUAGE Safe #-}+module Data.JustParse.Common (+-- Parsing+    Stream(..),+    Result(..),+    Parser( parse ),+    finalize,+    extend,+    justParse,+    runParser,+    isDone,+    isFail,+    isPartial,+    rename,+    (<?>),++-- Primitive parsers+    satisfy,+    mN,++-- Derived Parsers++-- Generic Parsers+    test,+    greedy,+    option,+    many,+    many1,+    manyN,+    atLeast,+    exactly,+    eof,+    oneOf,+    noneOf,+    anyToken,+    lookAhead,++-- Char Parsers+    char,+    anyChar,+    ascii,+    latin1,+    control,+    space,+    lower,+    upper,+    alpha,+    alphaNum,+    print,+    digit,+    octDigit,+    hexDigit,+    eol,++-- String Parsers+    string,++) where++import Prelude hiding ( print, length )+import Data.JustParse.Internal ( Stream(..), Parser(..), Result(..), extend, finalize, isDone, isPartial, isFail, rename, (<?>) )+import Data.Monoid ( mempty, Monoid )+import Data.Maybe ( fromMaybe )+import Data.List ( minimumBy )+import Data.Char ( isControl, isSpace, isLower, isUpper, isAlpha, isAlphaNum, isPrint, +                   isDigit, isOctDigit, isHexDigit, isLetter, isMark, isNumber, isPunctuation, +                   isSymbol, isSeparator, isAscii, isLatin1, isAsciiUpper, isAsciiLower )+import Data.Ord ( comparing )+import Control.Monad ( void, (>=>), liftM )+import Control.Applicative ( (<|>), optional, (<*) )++-- | Supplies the input to the 'Parser'. Returns all 'Result' types, +-- including 'Partial' and 'Fail' types.+runParser :: Parser s a -> s -> [Result s a]+runParser p = parse p . Just++-- | This is a \"newbie\" command that one should probably only use out of frustration.+-- It runs the 'Parser' greedily over the input, 'finalize's all the results, and returns+-- the first successful result. If there are no successful results, it returns Nothing.+justParse :: Stream s t => Parser s a -> s -> Maybe a+justParse p s = +    case finalize (parse (greedy p) (Just s)) of+        [] -> Nothing+        (Done v _:_) -> Just v+        (Fail m _:_) -> Nothing++-- | Parse a token that satisfies a predicate.+satisfy :: Stream s t => (t -> Bool) -> Parser s t+satisfy f = Parser $ \s -> +    case s of+        Nothing -> [Fail ["satisfy"] s]+        Just s' -> case uncons s' of+            Nothing -> [Partial $ parse (satisfy f)]+            Just (x, xs) -> +                if f x +                    then [Done x (Just xs)]+                    else [Fail ["satisfy"] s]++-- | Parse from @m@ to @n@ occurences of a 'Parser'. Let @n@ be negative+-- if one wishes for no upper bound.+mN :: Int -> Int -> Parser s a -> Parser s [a]+mN m n p = mN' m n p <?> "mN"++mN' :: Int -> Int -> Parser s a -> Parser s [a]+mN' _ 0 _ = Parser $ \s -> [Done [] s] +mN' m n p = Parser $ \s -> +    if m == 0 +        then Done [] s : (parse p s >>= g)+        else             parse p s >>= g+    where+        m' = if m == 0 then 0 else m-1+        g (Done a s) = parse (mN' m' (n-1) p) s >>= h a+        g (Partial p') = [Partial $ p' >=> g]+        g (Fail m l) = [Fail m l]+        h a (Done as s) = [Done (a:as) s]+        h a (Partial p') = [Partial $ p' >=> h a]+        h a (Fail m l) = [Fail m l]++-- | Return @True@ if the 'Parser' would succeed if one were to apply it,+-- otherwise, @False@.+test :: Parser s a -> Parser s Bool+test p = +    do +        a <- optional (lookAhead p)+        case a of+            Nothing -> return False+            _ -> return True++-- | Modifies a 'Parser' so that it will ony return the most consumptive+-- succesful results. If there are no successful results, it will only+-- return the most consumptive failures. One can use @greedy@ to emulate+-- parsers from @Parsec@ or @attoparsec@.+greedy :: Stream s t => Parser s a -> Parser s a+greedy (Parser p) = Parser $ \s -> g (p s) +    where+        b (Done _ _) = True+        b (Fail _ _) = True+        b _ = False+        f Nothing = 0+        f (Just s) = length s+        g [] = []+        g xs +            | all b xs = +                let+                    ds = filter isDone xs+                    dm = minimum (map (f . leftover) ds)+                    fs = filter isFail xs+                    fm = minimum (map (f . leftover) fs)+                in+                    if not (null ds)+                        then filter ((dm==) . f . leftover) ds+                        else filter ((fm==) . f . leftover) fs+            | otherwise = [Partial $ \s -> g $ extend s xs] ++-- | Attempts to apply a parser and returns a default value if it fails.+option :: a -> Parser s a -> Parser s a+option v p = +    do+        r <- optional p+        case r of+            Nothing -> return v+            Just v' -> return v'++-- | Parse any number of occurences of the 'Parser'. Equivalent to @'mN' 0 (-1)@.+many :: Parser s a -> Parser s [a]+many p = rename "many" (mN 0 (-1) p)++-- | Parse one or more occurence of the 'Parser'. Equivalent to @'mN' 1 (-1)@.+many1 :: Parser s a -> Parser s [a]+many1 p = rename "many1" (mN 1 (-1) p)++-- | Parse at least @n@ occurences of the 'Parser'. Equivalent to @'mN' n (-1)@.+manyN :: Int -> Parser s a -> Parser s [a]+manyN n p = rename "manyN" (mN n (-1) p)++-- | Identical to 'manyN', just a more intuitive name.+atLeast :: Int -> Parser s a -> Parser s [a]+atLeast n p = rename "atLeast" (mN n (-1) p)++-- | Parse exactly @n@ occurences of the 'Parser'. Equivalent to @'mN' n n@.+exactly :: Int -> Parser s a -> Parser s [a]+exactly n p = rename "exactly" (mN n n p)++-- | Only succeeds when supplied with @Nothing@.+eof :: (Eq s, Monoid s) => Parser s ()+eof = Parser $ \s ->+    case s of+        Nothing -> [Done () s]+        Just s' -> +            if s' == mempty+                then [Partial $ parse eof]+                else [Fail ["eof"] (Just s')]++oneOf :: (Eq t, Stream s t) => [t] -> Parser s t+oneOf ts = rename "oneOf" (satisfy (`elem` ts))++noneOf :: (Eq t, Stream s t) => [t] -> Parser s t+noneOf ts = rename "noneOf" (satisfy (not . (`elem` ts)))++-- | Parse a specific token.+token :: (Eq t, Stream s t) => t -> Parser s t+token t = rename "token" (satisfy (==t))++anyToken :: Stream s t => Parser s t+anyToken = rename "anyToken" (satisfy (const True))++-- | Applies the parser and returns its result, but resets+-- the leftovers as if it consumed nothing.+lookAhead :: Parser s a -> Parser s a+lookAhead (Parser p) = rename "lookAhead" $ Parser $ \s -> +    let +        g (Done a _) = [Done a s]+        g (Partial p') = [Partial $ p' >=> g]+        g (Fail m _) = [Fail m s]+    in+        p s >>= g++-- | Parse a specic char.+char :: Stream s Char => Char -> Parser s Char+char c = rename ("char "++[c]) (token c)++anyChar :: Stream s Char =>  Parser s Char+anyChar = rename "anyChar" anyToken++ascii :: Stream s Char =>  Parser s Char+ascii = rename "ascii" (satisfy isAscii)++latin1 :: Stream s Char =>  Parser s Char+latin1 = rename "latin1" (satisfy isLatin1)++control :: Stream s Char =>  Parser s Char+control = rename "control" (satisfy isControl)++space :: Stream s Char =>  Parser s Char+space = rename "space" (satisfy isSpace)++lower :: Stream s Char =>  Parser s Char+lower = rename "lower" (satisfy isLower)++upper :: Stream s Char =>  Parser s Char+upper = rename "upper" (satisfy isUpper)++alpha :: Stream s Char =>  Parser s Char+alpha = rename "alpha" (satisfy isAlpha)++alphaNum :: Stream s Char =>  Parser s Char+alphaNum = rename "alphaNum" (satisfy isAlphaNum)++print :: Stream s Char =>  Parser s Char+print = rename "print" (satisfy isPrint)++digit :: Stream s Char =>  Parser s Char+digit = rename "digit" (satisfy isDigit)++octDigit :: Stream s Char =>  Parser s Char+octDigit = rename "octDigit" (satisfy isOctDigit)++hexDigit :: Stream s Char =>  Parser s Char+hexDigit = rename "hexDigit" (satisfy isHexDigit)++-- | Parse a specific string.+string :: Stream s Char => String -> Parser s String+string s = rename ("string "++s) (mapM char s)++-- | Parses until a newline, carriage return + newline, or newline + carriage return.+eol :: Stream s Char => Parser s String+eol = rename "eol" (string "\r\n" <|> string "\n\r" <|> string "\n")++-- | Makes common types such as Strings into a Stream.+instance (Eq t) => Stream [t] t where+    uncons [] = Nothing+    uncons (x:xs) = Just (x, xs)
+ src/Data/JustParse/Internal.hs view
@@ -0,0 +1,157 @@+{-|+Module      : Data.JustParse.Internal+Description : The engine behind the JustParse library+Copyright   : Copyright Waived+License     : PublicDomain+Maintainer  : grantslatton@gmail.com+Stability   : experimental+Portability : portable+-}++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE Safe #-}++module Data.JustParse.Internal (+    finalize,+    extend,+    Stream (..),+    Parser (..),+    Result (..),+    isDone,+    isFail,+    isPartial,+    rename,+    (<?>)+) where++import Prelude hiding ( length )+import Control.Monad ( MonadPlus, mzero, mplus, (>=>), ap )+import Control.Applicative ( Alternative, Applicative, pure, (<*>), empty, (<|>) )+import Data.Monoid ( Monoid, mempty, mappend )+import Data.List ( intercalate )++-- | A @Stream@ instance has a stream of type @s@, made up of tokens of +-- type @t@, which must be determinable by the stream.+class (Eq s, Monoid s) => Stream s t | s -> t where+    -- | @uncons@ returns @Nothing@ if the @Stream@ is empty, otherwise it+    -- returns the first token of the stream, followed by the remainder+    -- of the stream, wrapped in a @Just@.+    uncons :: Stream s t => s -> Maybe (t, s)+    -- | The default @length@ implementation is O(n). If your stream provides+    -- a more efficient method for determining the length, it is wise to+    -- override this. The @length@ method is only used by the 'greedy' parser.+    length :: Stream s t => s -> Int+    length s = +        case uncons s of+            Nothing -> 0+            Just (x, xs) -> 1 + length xs++newtype Parser s a = +    Parser { +        parse :: Maybe s -> [Result s a]+    }++instance Monoid (Parser s a) where+    mempty = mzero+    mappend = mplus++instance Functor (Parser s) where+    fmap f (Parser p) = Parser $ \s -> map (fmap f) (p s)++instance Applicative (Parser s) where+    pure = return +    (<*>) = ap++instance Alternative (Parser s) where+    empty = mzero+    (<|>) = mplus++instance Monad (Parser s) where+    return v = Parser $ \s -> [Done v s] +    (Parser p) >>= f = Parser $ p >=> g+        where+            g (Fail m l) = [Fail m l]+            g (Done a s) = parse (f a) s +            g (Partial p) = [Partial $ p >=> g] ++instance MonadPlus (Parser s) where+    mzero = Parser $ const []+    mplus (Parser p1) (Parser p2) = Parser (\s -> p1 s ++ p2 s)++data Result s a +    -- | A @Partial@ wraps the same function as a Parser. Supply it with a @Just@+    -- and it will continue parsing, or with a @Nothing@ and it will terminate.+    =+    Partial {+        continue    :: Maybe s -> [Result s a]+    } |+    -- | A @Done@ contains the resultant @value@, and the @leftover@ stream, if any.+    Done {+        value       :: a,+        leftover    :: Maybe s+    } |+    -- | A @Fail@ contains a stack of error messages, and the @lftover@ stream, if any.+    Fail {+        messages    :: [String],+        leftover    :: Maybe s+    }++isDone :: Result s a -> Bool+isDone (Done _ _) = True+isDone _ = False++isPartial :: Result s a -> Bool+isPartial (Partial _) = True+isPartial _ = False++isFail :: Result s a -> Bool+isFail (Fail _ _) = True+isFail _ = False++instance Functor (Result s) where+    fmap f (Partial p) = Partial $ map (fmap f) . p+    fmap f (Done a s) = Done (f a) s+    fmap f (Fail m l) = Fail m l++instance Show a => Show (Result s a) where+    show (Partial _) = "Partial"+    show (Done a _) = show a+    show (Fail m l) = "Fail: \nIn: " ++ intercalate "\nIn: " m++-- | @finalize@ takes a list of results (presumably returned from a 'Parser' or 'Partial',+-- and supplies @Nothing@ to any remaining @Partial@ values, so that only 'Fail' and 'Done'+-- values remain.+finalize :: (Eq s, Monoid s) => [Result s a] -> [Result s a]+finalize = extend Nothing++-- | @extend@ takes a @Maybe s@ as input, and supplies the input to all values+-- in the 'Result' list. For 'Done' and 'Fail' values, it appends the @stream@ +-- to the 'leftover' portion, and for 'Partial' values, it runs the continuation,+-- adding in any new 'Result' values to the output.+extend :: (Eq s, Monoid s) => Maybe s -> [Result s a] -> [Result s a]+extend s rs = rs >>= g --`prnt` (show (map i rs, map i (rs >>= g), h s))+    where+        g (Fail m l) = [Fail m (f l s)]+        g (Partial p) = p s+        g (Done a s') = [Done a (f s' s)]+        f Nothing _ = Nothing+        f (Just s) Nothing = if s == mempty then Nothing else Just s+        f s s' = mappend s s'++-- | @rename@ pushes a new error message onto the stack in case of failure.+-- This is particularly useful when debugging a complex 'Parser'.+rename :: String -> Parser s a -> Parser s a+rename s p = Parser (map g . parse p)+    where+        g v@(Fail m l) = Fail (s:m) l+        g v = v+++infixl 0 <?>+-- | The infix version of 'rename'+(<?>) :: Parser s a -> String -> Parser s a+p <?> s = rename s p
+ src/Data/JustParse/Language.hs view
@@ -0,0 +1,169 @@+{-|+Module      : Data.JustParse.Language+Description : Regular Expressions and Grammars in JustParse+Copyright   : Copyright Waived+License     : PublicDomain+Maintainer  : grantslatton@gmail.com+Stability   : experimental+Portability : portable++Takes ideas from the field of Formal Languages and imports them into the parsing library.+-}++{-# LANGUAGE Safe #-}+module Data.JustParse.Language (+    Match (..),+    regex,+    regex'+) where++import Data.JustParse.Common ( char, string, many1, digit, Stream, noneOf, oneOf, greedy, many, mN, anyChar, leftover, value, finalize, parse, Result(..), justParse, isFail )+import Data.JustParse.Internal( Parser (..) )+import Control.Applicative ( (<|>), optional )+import Control.Monad ( liftM, mzero )+import Data.Monoid ( Monoid, mconcat, mempty, mappend )+import Data.Maybe ( isJust )+import Data.List ( intercalate )++-- | @regex@ takes a regular expression in the form of a 'String' and,+-- if the regex is valid, returns a 'Parser' that parses that regex.+-- If the regex is invalid, it returns a Parser that will only return+-- 'Fail' with an \"Invalid Regex\" message.+regex :: Stream s Char => String -> Parser s Match+regex s +    | null r = Parser $ \s -> [Fail ["Invalid Regex"] s]+    | isFail $ head r = Parser $ \s -> [Fail ["Invalid Regex"] s]+    | isJust $ leftover $ head r = Parser $ \s -> [Fail ["Invalid Regex"] s]+    | otherwise = value $ head r+    where+        r = finalize (parse (greedy regular) (Just s))++-- | The same as 'regex', but only returns the full matched text.+regex' :: Stream s Char => String -> Parser s String+regex' = liftM matched . regex ++-- | The result of a 'regex'+data Match = +    Match {+        -- | The complete text matched within the regex+        matched :: String,+        -- | Any submatches created by using capture groups+        groups :: [Match]+    } ++instance Show Match where+    show = show' ""+        where+            show' i (Match m []) = i ++ m+            show' i (Match m gs) = i ++ m ++ "\n" ++ intercalate "\n" (map (show' ('\t':i)) gs)++-- mconcat makes things very nice for concatenating the results of subregexes+instance Monoid Match where+    mempty = Match "" []+    mappend (Match m g) (Match m' g') = +        Match {+            matched = m ++ m',+            groups = g ++ g'+        }++regular :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+regular = liftM (liftM mconcat . sequence) (greedy $ many parser)++parser :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+parser = character <|> charClass <|> negCharClass <|> question <|> group <|> asterisk <|> plus <|> mn <|> period <|> pipe++parserNP :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+parserNP = character <|> charClass <|> negCharClass <|> question <|> group <|> asterisk <|> plus <|> mn <|> period ++restricted :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+restricted = character <|> charClass <|> negCharClass <|> group <|> period++unreserved :: Stream s Char => Parser s Char +unreserved = (char '\\' >> anyChar ) <|> noneOf "()[]\\*+{}^?:<>|."++character :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+character = +    do+        c <- unreserved+        return $ do+            c' <- char c+            return $ Match [c] []++charClass :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+charClass = +    do+        char '['+        c <- greedy (many1 unreserved)+        char ']'+        return $ do+            c' <- oneOf c+            return $ Match [c'] []++negCharClass :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+negCharClass = +    do+        string "[^"+        c <- greedy (many1 unreserved)+        char ']'+        return $ do+            c' <- noneOf c+            return $ Match [c'] []++period :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+period = +    do+        char '.'+        return $ do+            c <- noneOf "\n\r"+            return $ Match [c] []+++question :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+question = +    do+        p <- restricted+        char '?'+        return $ liftM mconcat (mN 0 1 p)++group :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+group = +    do+        string "("+        p <- regular+        char ')'+        return $ do+            r <- p+            return $ r { groups = [r] } ++asterisk :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+asterisk = +    do+        p <- restricted+        char '*'+        return $ liftM mconcat (many p)++plus :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+plus = +    do+        p <- restricted+        char '+'+        return $ liftM mconcat (many1 p)++mn :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+mn = +    do+        p <- restricted+        char '{'+        l <- optional (many1 digit)+        char ','+        r <- optional (many1 digit)+        char '}'+        return $ liftM mconcat (mN (maybe 0 read l) (maybe (-1) read r) p)++pipe :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)+pipe = +    do+        p <- parserNP+        char '|'+        p' <- parser+        return $ p <|> p'