JustParse 1.0 → 2.0
raw patch · 8 files changed
+875/−457 lines, 8 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
- Data.JustParse: (<?>) :: Parser s a -> String -> Parser s a
- Data.JustParse: Fail :: [String] -> Maybe s -> Result s a
- Data.JustParse: Match :: String -> [Match] -> Match
- Data.JustParse: alpha :: Stream s Char => Parser s Char
- Data.JustParse: alphaNum :: Stream s Char => Parser s Char
- Data.JustParse: anyChar :: Stream s Char => Parser s Char
- Data.JustParse: anyToken :: Stream s t => Parser s t
- Data.JustParse: ascii :: Stream s Char => Parser s Char
- Data.JustParse: atLeast :: Int -> Parser s a -> Parser s [a]
- Data.JustParse: char :: Stream s Char => Char -> Parser s Char
- Data.JustParse: control :: Stream s Char => Parser s Char
- Data.JustParse: data Match
- Data.JustParse: digit :: Stream s Char => Parser s Char
- Data.JustParse: eof :: (Eq s, Monoid s) => Parser s ()
- Data.JustParse: eol :: Stream s Char => Parser s String
- Data.JustParse: exactly :: Int -> Parser s a -> Parser s [a]
- Data.JustParse: greedy :: Stream s t => Parser s a -> Parser s a
- Data.JustParse: groups :: Match -> [Match]
- Data.JustParse: hexDigit :: Stream s Char => Parser s Char
- Data.JustParse: isDone :: Result s a -> Bool
- Data.JustParse: isFail :: Result s a -> Bool
- Data.JustParse: isPartial :: Result s a -> Bool
- Data.JustParse: justParse :: Stream s t => Parser s a -> s -> Maybe a
- Data.JustParse: latin1 :: Stream s Char => Parser s Char
- Data.JustParse: lookAhead :: Parser s a -> Parser s a
- Data.JustParse: lower :: Stream s Char => Parser s Char
- Data.JustParse: mN :: Int -> Int -> Parser s a -> Parser s [a]
- Data.JustParse: many :: Parser s a -> Parser s [a]
- Data.JustParse: many1 :: Parser s a -> Parser s [a]
- Data.JustParse: manyN :: Int -> Parser s a -> Parser s [a]
- Data.JustParse: matched :: Match -> String
- Data.JustParse: messages :: Result s a -> [String]
- Data.JustParse: noneOf :: (Eq t, Stream s t) => [t] -> Parser s t
- Data.JustParse: octDigit :: Stream s Char => Parser s Char
- Data.JustParse: oneOf :: (Eq t, Stream s t) => [t] -> Parser s t
- Data.JustParse: option :: a -> Parser s a -> Parser s a
- Data.JustParse: print :: Stream s Char => Parser s Char
- Data.JustParse: regex :: Stream s Char => String -> Parser s Match
- Data.JustParse: regex' :: Stream s Char => String -> Parser s String
- Data.JustParse: rename :: String -> Parser s a -> Parser s a
- Data.JustParse: satisfy :: Stream s t => (t -> Bool) -> Parser s t
- Data.JustParse: space :: Stream s Char => Parser s Char
- Data.JustParse: string :: Stream s Char => String -> Parser s String
- Data.JustParse: test :: Parser s a -> Parser s Bool
- Data.JustParse: upper :: Stream s Char => Parser s Char
+ Data.JustParse: parseOnly :: Stream s t => Parser s a -> s -> Maybe a
+ Data.JustParse.Char: alpha :: Stream s Char => Parser s Char
+ Data.JustParse.Char: alphaNum :: Stream s Char => Parser s Char
+ Data.JustParse.Char: anyChar :: Stream s Char => Parser s Char
+ Data.JustParse.Char: ascii :: Stream s Char => Parser s Char
+ Data.JustParse.Char: caseInsensitiveChar :: Stream s Char => Char -> Parser s Char
+ Data.JustParse.Char: caseInsensitiveString :: Stream s Char => String -> Parser s String
+ Data.JustParse.Char: char :: Stream s Char => Char -> Parser s Char
+ Data.JustParse.Char: control :: Stream s Char => Parser s Char
+ Data.JustParse.Char: digit :: Stream s Char => Parser s Char
+ Data.JustParse.Char: eol :: Stream s Char => Parser s String
+ Data.JustParse.Char: hexDigit :: Stream s Char => Parser s Char
+ Data.JustParse.Char: instance [safe] Eq t => Stream [t] t
+ Data.JustParse.Char: latin1 :: Stream s Char => Parser s Char
+ Data.JustParse.Char: lower :: Stream s Char => Parser s Char
+ Data.JustParse.Char: octDigit :: Stream s Char => Parser s Char
+ Data.JustParse.Char: print :: Stream s Char => Parser s Char
+ Data.JustParse.Char: space :: Stream s Char => Parser s Char
+ Data.JustParse.Char: string :: Stream s Char => String -> Parser s String
+ Data.JustParse.Char: upper :: Stream s Char => Parser s Char
+ Data.JustParse.Combinator: (<|>) :: Stream s t => Parser s a -> Parser s a -> Parser s a
+ Data.JustParse.Combinator: (<||>) :: Parser s a -> Parser s a -> Parser s a
+ Data.JustParse.Combinator: anyToken :: Stream s t => Parser s t
+ Data.JustParse.Combinator: assert :: Stream s t => Bool -> Parser s ()
+ Data.JustParse.Combinator: branch :: Parser s a -> Parser s a -> Parser s a
+ Data.JustParse.Combinator: chainl :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a
+ Data.JustParse.Combinator: chainl1 :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> Parser s a
+ Data.JustParse.Combinator: chainl1_ :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> Parser s a
+ Data.JustParse.Combinator: chainl_ :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a
+ Data.JustParse.Combinator: chainr :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a
+ Data.JustParse.Combinator: chainr1 :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> Parser s a
+ Data.JustParse.Combinator: chainr1_ :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> Parser s a
+ Data.JustParse.Combinator: chainr_ :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a
+ Data.JustParse.Combinator: choice :: Stream s t => [Parser s a] -> Parser s a
+ Data.JustParse.Combinator: choice_ :: Stream s t => [Parser s a] -> Parser s a
+ Data.JustParse.Combinator: count :: Stream s t => Int -> Parser s a -> Parser s [a]
+ Data.JustParse.Combinator: eitherP :: Stream s t => Parser s a -> Parser s b -> Parser s (Either a b)
+ Data.JustParse.Combinator: eitherP_ :: Stream s t => Parser s a -> Parser s b -> Parser s (Either a b)
+ Data.JustParse.Combinator: endBy :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: endBy1 :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: endBy1_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: endBy_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: eof :: Stream s t => Parser s ()
+ Data.JustParse.Combinator: exactly :: Stream s t => Int -> Parser s a -> Parser s [a]
+ Data.JustParse.Combinator: greedy :: Stream s t => Parser s a -> Parser s a
+ Data.JustParse.Combinator: guard :: Stream s t => Bool -> Parser s ()
+ Data.JustParse.Combinator: lookAhead :: Stream s t => Parser s a -> Parser s a
+ Data.JustParse.Combinator: mN :: Stream s t => Int -> Int -> Parser s a -> Parser s [a]
+ Data.JustParse.Combinator: mN_ :: Stream s t => Int -> Int -> Parser s a -> Parser s [a]
+ Data.JustParse.Combinator: many :: Stream s t => Parser s a -> Parser s [a]
+ Data.JustParse.Combinator: many1 :: Stream s t => Parser s a -> Parser s [a]
+ Data.JustParse.Combinator: many1_ :: Parser s a -> Parser s [a]
+ Data.JustParse.Combinator: many_ :: Parser s a -> Parser s [a]
+ Data.JustParse.Combinator: noneOf :: (Eq t, Stream s t) => [t] -> Parser s t
+ Data.JustParse.Combinator: notFollowedBy :: Stream s t => Parser s a -> Parser s ()
+ Data.JustParse.Combinator: oneOf :: (Eq t, Stream s t) => [t] -> Parser s t
+ Data.JustParse.Combinator: option :: Stream s t => a -> Parser s a -> Parser s a
+ Data.JustParse.Combinator: option_ :: Stream s t => a -> Parser s a -> Parser s a
+ Data.JustParse.Combinator: optional :: Stream s t => Parser s a -> Parser s (Maybe a)
+ Data.JustParse.Combinator: optional_ :: Stream s t => Parser s a -> Parser s (Maybe a)
+ Data.JustParse.Combinator: perm :: Stream s t => [Parser s a] -> Parser s [a]
+ Data.JustParse.Combinator: perm_ :: Stream s t => [Parser s a] -> Parser s [a]
+ Data.JustParse.Combinator: satisfy :: Stream s t => (t -> Bool) -> Parser s t
+ Data.JustParse.Combinator: select :: Stream s t => [Parser s a] -> Parser s (Int, a)
+ Data.JustParse.Combinator: select_ :: Stream s t => [Parser s a] -> Parser s (Int, a)
+ Data.JustParse.Combinator: sepBy :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: sepBy1 :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: sepBy1_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: sepBy_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: sepEndBy :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: sepEndBy1 :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: sepEndBy1_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: sepEndBy_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]
+ Data.JustParse.Combinator: skipMany :: Stream s t => Parser s a -> Parser s ()
+ Data.JustParse.Combinator: skipMany1 :: Stream s t => Parser s a -> Parser s ()
+ Data.JustParse.Combinator: skipMany1_ :: Stream s t => Parser s a -> Parser s ()
+ Data.JustParse.Combinator: skipMany_ :: Stream s t => Parser s a -> Parser s ()
+ Data.JustParse.Combinator: takeWhile :: Stream s t => (t -> Bool) -> Parser s [t]
+ Data.JustParse.Combinator: takeWhile_ :: Stream s t => (t -> Bool) -> Parser s [t]
+ Data.JustParse.Combinator: test :: Stream s t => Parser s a -> Parser s Bool
+ Data.JustParse.Combinator: token :: (Eq t, Stream s t) => t -> Parser s t
+ Data.JustParse.Language: Match :: String -> [Match] -> Match
+ Data.JustParse.Language: data Match
+ Data.JustParse.Language: groups :: Match -> [Match]
+ Data.JustParse.Language: instance [safe] Monoid Match
+ Data.JustParse.Language: instance [safe] Show Match
+ Data.JustParse.Language: matched :: Match -> String
+ Data.JustParse.Language: regex :: Stream s Char => String -> Parser s Match
+ Data.JustParse.Language: regex' :: Stream s Char => String -> Parser s String
+ Data.JustParse.Language: regex_ :: Stream s Char => String -> Parser s Match
+ Data.JustParse.Language: regex_' :: Stream s Char => String -> Parser s String
+ Data.JustParse.Numeric: decFloat :: Stream s Char => Parser s Float
+ Data.JustParse.Numeric: decFloat_ :: Stream s Char => Parser s Float
+ Data.JustParse.Numeric: decInt :: Stream s Char => Parser s Int
+ Data.JustParse.Numeric: decInt_ :: Stream s Char => Parser s Int
+ Data.JustParse.Numeric: hexInt :: Stream s Char => Parser s Int
+ Data.JustParse.Numeric: hexInt_ :: Stream s Char => Parser s Int
Files
- JustParse.cabal +14/−6
- src/Data/JustParse.hs +38/−78
- src/Data/JustParse/Char.hs +115/−0
- src/Data/JustParse/Combinator.hs +478/−0
- src/Data/JustParse/Common.hs +0/−284
- src/Data/JustParse/Internal.hs +61/−58
- src/Data/JustParse/Language.hs +68/−31
- src/Data/JustParse/Numeric.hs +101/−0
JustParse.cabal view
@@ -1,5 +1,5 @@ name: JustParse-version: 1.0+version: 2.0 synopsis: A simple and comprehensive Haskell parsing library description: A simple and comprehensive Haskell parsing library homepage: https://github.com/grantslatton/JustParse@@ -13,8 +13,16 @@ 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+ exposed-modules: + Data.JustParse,+ Data.JustParse.Combinator,+ Data.JustParse.Numeric,+ Data.JustParse.Language,+ Data.JustParse.Char+ other-modules: + Data.JustParse.Internal+ build-depends:+ base >= 4.2 && < 5+ hs-source-dirs: src+ extensions: MultiParamTypeClasses, Rank2Types, FlexibleInstances, FlexibleContexts, Safe, TupleSections, UndecidableInstances+ ghc-options: -O2 -Wall
src/Data/JustParse.hs view
@@ -1,16 +1,14 @@ {-| Module : Data.JustParse-Description : The one-stop import for the library+Description : Parsing functions and types Copyright : Copyright Waived License : PublicDomain Maintainer : grantslatton@gmail.com Stability : experimental Portability : portable--A simple and comprehensive Haskell parsing library. -} -{-# LANGUAGE Safe #-}+--{-# LANGUAGE Safe #-} module Data.JustParse ( -- * Overview -- $overview@@ -24,67 +22,17 @@ -- *** 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(..)-+ runParser,+ parseOnly,+ extend,+ finalize,+ Parser,+ Result(..),+ Stream(..) ) where -import Data.JustParse.Language as L-import Data.JustParse.Common as C+import Data.JustParse.Internal+import Data.JustParse.Combinator -- $overview -- @@ -92,13 +40,7 @@ -- -- * 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+-- * Allows for a parser to return a 'Partial' result -- -- * Returns a list of all possible parses -- @@ -112,8 +54,8 @@ --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+-- ex \<- char \'!\' \-\-Parses the character \'!\'+-- return ([h]++rest++[ex]) \-\-Returns all of the above concatenated together -- @ -- $quickstart2@@ -123,19 +65,37 @@ -- -- @ --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+-- string \"hello w\" \-\-Parses the string \"hello w\"+-- os \<- many1 (char \'o\') \-\-Applies the parser \"char \'o\'\" one or more times+-- 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+-- This parser will turn a string of comma separated values into a list of +-- them. Note that we could use the 'sepBy' parser, but this demonstrates a +-- recursive parser. -- -- @ --csv = do --- v \<- greedy (many (noneOf \",\")) \-\-Parses as many non-comma characters as possible+-- v \<- 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 -- @++-- | Supplies the input to the 'Parser'. Returns all 'Result' types, +-- including 'Partial' results.+runParser :: Parser s a -> s -> [Result s a]+runParser p = parse p . Just++-- | This 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'. This is useful when you are+-- parsing something that you know will have no 'Partial's and you just+-- want an answer.+parseOnly :: Stream s t => Parser s a -> s -> Maybe a+parseOnly p s = + case finalize (parse (greedy p) (Just s)) of+ [] -> Nothing+ (Done v _:_) -> Just v
+ src/Data/JustParse/Char.hs view
@@ -0,0 +1,115 @@+{-|+Module : Data.JustParse.Common+Description : Common char parsers+Copyright : Copyright Waived+License : PublicDomain+Maintainer : grantslatton@gmail.com+Stability : experimental+Portability : portable++Several useful parsers for dealing with 'String's.+-}++--{-# LANGUAGE Safe #-}+module Data.JustParse.Char (+ char,+ anyChar,+ caseInsensitiveChar,+ ascii,+ latin1,+ control,+ space,+ lower,+ upper,+ alpha,+ alphaNum,+ print,+ digit,+ octDigit,+ hexDigit,+ string,+ caseInsensitiveString,+ eol+) where++import Prelude hiding (print)+import Data.Char+import Data.JustParse.Internal+import Data.JustParse.Combinator++-- | Parse a specic char.+char :: Stream s Char => Char -> Parser s Char+char = token++-- | Parse any char.+anyChar :: Stream s Char => Parser s Char+anyChar = anyToken++-- | Parse a specific char, ignoring case.+caseInsensitiveChar :: Stream s Char => Char -> Parser s Char+caseInsensitiveChar c = char (toUpper c) <|> char (toLower c)++-- | Parse any char that is ASCII.+ascii :: Stream s Char => Parser s Char+ascii = satisfy isAscii++-- | Parse any char that is Latin-1.+latin1 :: Stream s Char => Parser s Char+latin1 = satisfy isLatin1++-- | Parse a control character.+control :: Stream s Char => Parser s Char+control = satisfy isControl++-- | Parse a space.+space :: Stream s Char => Parser s Char+space = satisfy isSpace++-- | Parse a lowercase character.+lower :: Stream s Char => Parser s Char+lower = satisfy isLower++-- | Parse an uppercase character.+upper :: Stream s Char => Parser s Char+upper = satisfy isUpper++-- | Parse an alphabetic character.+alpha :: Stream s Char => Parser s Char+alpha = satisfy isAlpha++-- | Parse an alphanumeric character.+alphaNum :: Stream s Char => Parser s Char+alphaNum = satisfy isAlphaNum++-- | Parse a printable (non-control) character.+print :: Stream s Char => Parser s Char+print = satisfy isPrint++-- | Parse a digit.+digit :: Stream s Char => Parser s Char+digit = satisfy isDigit++-- | Parse an octal digit.+octDigit :: Stream s Char => Parser s Char+octDigit = satisfy isOctDigit++-- | Parse a hexadeciaml digit.+hexDigit :: Stream s Char => Parser s Char+hexDigit = satisfy isHexDigit++-- | Parse a specific string.+string :: Stream s Char => String -> Parser s String+string = mapM char ++-- | Parse a specfic string, ignoring case.+caseInsensitiveString :: Stream s Char => String -> Parser s String+caseInsensitiveString = mapM caseInsensitiveChar ++-- | Parses until a newline, carriage return + newline, or newline + carriage return.+eol :: Stream s Char => Parser s String+eol = choice [string "\r\n", string "\n\r", string "\n"]++-- | Makes common types such as 'String's into a Stream.+instance Eq t => Stream [t] t where+ uncons [] = Nothing+ uncons (x:xs) = Just (x, xs)
+ src/Data/JustParse/Combinator.hs view
@@ -0,0 +1,478 @@+{-|+Module : Data.JustParse.Common+Description : Common parser combinators+Copyright : Copyright Waived+License : PublicDomain+Maintainer : grantslatton@gmail.com+Stability : experimental+Portability : portable++The bread and butter of combinatory parsing.+-}++--{-# LANGUAGE Safe #-}+--{-# LANGUAGE TupleSections #-}+module Data.JustParse.Combinator (+ -- * Utility Parsers+ assert,+ eof,+ eitherP,+ greedy,+ guard,+ lookAhead,+ notFollowedBy,+ option,+ optional,+ test,+ (<|>),++ -- * Token Parsers+ anyToken,+ noneOf,+ oneOf,+ satisfy,+ token,++ -- * Repetetive Parsers+ chainl,+ chainl1,+ chainr,+ chainr1,+ count,+ endBy,+ endBy1,+ exactly,+ many,+ many1,+ mN,+ sepBy,+ sepBy1,+ skipMany,+ skipMany1,+ sepEndBy,+ sepEndBy1,+ takeWhile,++ -- * Group Parsers+ choice,+ perm,+ select,++ -- * Branching Parsers+ branch,+ (<||>),+ chainl_,+ chainr_,+ chainl1_,+ chainr1_,+ choice_,+ eitherP_,+ endBy_,+ endBy1_,+ many_,+ many1_,+ mN_,+ option_,+ optional_,+ perm_,+ select_,+ sepBy_,+ sepBy1_,+ sepEndBy_,+ sepEndBy1_,+ skipMany_,+ skipMany1_,+ takeWhile_+) where++import Prelude hiding ( print, length, takeWhile )+import Data.JustParse.Internal ( + Stream(..), Parser(..), Result(..), extend, finalize, isDone, + isPartial, toPartial, streamAppend )+import Data.Monoid ( mempty, Monoid, mappend )+import Data.Maybe ( fromMaybe )+import Data.List ( minimumBy, foldl1', foldl' )+import Data.Ord ( comparing )+import qualified Control.Monad as M+import qualified Control.Applicative as A++-- | Parse a token that satisfies a predicate.+satisfy :: Stream s t => (t -> Bool) -> Parser s t+satisfy f = Parser $ \s -> + case s of+ Nothing -> []+ Just s' -> case uncons s' of+ Nothing -> [Partial $ parse (satisfy f)]+ Just (x, xs) -> [Done x (Just xs) | f x]++-- | A parser that succeeds on 'True' and fails on 'False'. +guard :: Stream s t => Bool -> Parser s ()+guard = M.guard++-- | Synonym of 'guard'.+assert :: Stream s t => Bool -> Parser s ()+assert = guard++-- | Only succeeds when supplied with 'Nothing'.+eof :: Stream s t => Parser s ()+eof = notFollowedBy anyToken++-- | Parse a token that is a member of the list of tokens.+oneOf :: (Eq t, Stream s t) => [t] -> Parser s t+oneOf ts = satisfy (`elem` ts)++-- | Parse a token that is not a member of the list of tokens.+noneOf :: (Eq t, Stream s t) => [t] -> Parser s t+noneOf ts = satisfy (`notElem` ts)++-- | Parse a specific token.+token :: (Eq t, Stream s t) => t -> Parser s t+token t = satisfy (==t)++-- | Parse any token.+anyToken :: Stream s t => Parser s t+anyToken = satisfy (const True)++-- | Parse tokens while a predicate remains true.+takeWhile :: Stream s t => (t -> Bool) -> Parser s [t]+takeWhile = many . satisfy++-- | Branches every iteration where one branch stops and one branch +-- continues.+takeWhile_ :: Stream s t => (t -> Bool) -> Parser s [t]+takeWhile_ = many_ . satisfy++-- | Splits the current parse branch between the two parsers.+branch :: Parser s a -> Parser s a -> Parser s a+branch a b = Parser $ \s -> parse a s ++ parse b s++infixr 1 <||>+-- | Infix version of 'branch'.+(<||>) :: Parser s a -> Parser s a -> Parser s a+(<||>) = branch++-- | @mN m n p@ parses between @m@ and @n@ occurences of @p@, inclusive.+mN :: Stream s t => Int -> Int -> Parser s a -> Parser s [a]+mN _ 0 _ = Parser $ \s -> [Done [] s]+mN 0 n p = M.liftM2 (:) p (mN 0 (n-1) p) A.<|> return []+mN m n p = M.liftM2 (:) p (mN (m-1) (n-1) p)++-- | Branches every iteration where one branch stops and one branch +-- continues.+mN_ :: Stream s t => Int -> Int -> Parser s a -> Parser s [a]+mN_ _ 0 _ = Parser $ \s -> [Done [] s]+mN_ 0 n p = M.liftM2 (:) p (mN 0 (n-1) p) <||> return []+mN_ m n p = M.liftM2 (:) p (mN (m-1) (n-1) p)++-- | Synonym of 'count'.+exactly :: Stream s t => Int -> Parser s a -> Parser s [a]+exactly n = mN n n++-- | Applies a parser at least @n@ times.+atLeast :: Stream s t => Int -> Parser s a -> Parser s [a]+atLeast n = mN n (-1)++-- | Branches every iteration where one branch stops and one branch +-- continues.+atLeast_ :: Stream s t => Int -> Parser s a -> Parser s [a]+atLeast_ n = mN_ n (-1)++-- | Applies a parser at most @n@ times.+atMost :: Stream s t => Int -> Parser s a -> Parser s [a]+atMost = mN 0++-- | Branches every iteration where one branch stops and one branch +-- continues.+atMost_ :: Stream s t => Int -> Parser s a -> Parser s [a]+atMost_ = mN 0++-- | Applies a parser zero or more times.+many :: Stream s t => Parser s a -> Parser s [a]+many = A.many++-- | Branches every iteration where one branch stops and one branch +-- continues.+many_ :: Parser s a -> Parser s [a]+many_ p = return [] <||> M.liftM2 (:) p (many_ p)++-- | Applies a parser one or more times.+many1 :: Stream s t => Parser s a -> Parser s [a]+many1 p = M.liftM2 (:) p (many p)++-- | Branches every iteration where one branch stops and one branch +-- continues.+many1_ :: Parser s a -> Parser s [a]+many1_ p = M.liftM2 (:) p (many_ p)++-- | Return 'True' if the parser would succeed if one were to apply it,+-- otherwise, it returns 'False'. It does not consume input.+test :: Stream s t => Parser s a -> Parser s Bool+test p = + do + a <- optional (lookAhead p)+ case a of+ Nothing -> return False+ _ -> return True++infixr 1 <|>+-- | @a \<|\> b@ is equivalent to @'choice' [a,b]@. That is, first @a@ is+-- tried, and if it yields no results, @b@ is tried.+(<|>) :: Stream s t => Parser s a -> Parser s a -> Parser s a+(<|>) = (A.<|>)++-- | Attempt to apply each parser in the list in order until one succeeds.+choice :: Stream s t => [Parser s a] -> Parser s a+choice = foldl1' (A.<|>) ++-- | Given a list of parsers, split off a branch for each one.+choice_ :: Stream s t => [Parser s a] -> Parser s a+choice_ = foldl1' (<||>)++-- | Like 'choice', but returns the index of the successful parser as well+-- as the result.+select :: Stream s t => [Parser s a] -> Parser s (Int, a)+select [] = M.mzero+select (p:ps) = M.liftM (0,) p <|> M.liftM (\(x,y) -> (x+1,y)) (select ps)++-- | Like 'choice_', but returns the index of the successful parser.+select_ :: Stream s t => [Parser s a] -> Parser s (Int, a)+select_ [] = M.mzero+select_ (p:ps) = M.liftM (0,) p <||> M.liftM (\(x,y) -> (x+1,y)) (select_ ps)++-- | Modifies a parser so that it will ony return the most consumptive+-- succesful results. +greedy :: Stream s t => Parser s a -> Parser s a+greedy (Parser p) = Parser $ \s -> g (p s) + where+ f Nothing = 0+ f (Just s) = length s+ g [] = []+ g xs + | all isDone xs = [minimumBy (comparing (f . leftover)) xs]+ | otherwise = [Partial $ \s -> g $ extend s xs] ++-- | Attempts to apply a parser and returns a default value if it fails.+option :: Stream s t => a -> Parser s a -> Parser s a+option v p = + do+ r <- A.optional p+ case r of+ Nothing -> return v+ Just v' -> return v'++-- | Splits off two branches, one where the parse is attempted, and one +-- where it is not.+option_ :: Stream s t => a -> Parser s a -> Parser s a+option_ v p = option v p <||> return v++-- | Attempts to apply the parser, returning 'Nothing' upon failure, or+-- the result wrapped in a 'Just'.+optional :: Stream s t => Parser s a -> Parser s (Maybe a)+optional = A.optional++-- | Splits off two branches, one where the parse is attempted, and one +-- where it is not.+optional_ :: Stream s t => Parser s a -> Parser s (Maybe a)+optional_ p = M.liftM Just p <||> return Nothing++-- | @sepBy1 p s@ parses many occurences of @p@ separated by @s@.+sepBy :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+sepBy p s = sepBy1 p s A.<|> return []++-- | Branches every iteration where one branch stops and one branch +-- continues.+sepBy_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+sepBy_ p s = sepBy1_ p s <||> return []++-- | @sepBy1 p s@ parses one or more occurences of @p@ separated by @s@.+sepBy1 :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+sepBy1 p s = M.liftM2 (:) p (many (s >> p))++-- | Branches every iteration where one branch stops and one branch +-- continues.+sepBy1_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+sepBy1_ p s = M.liftM2 (:) p (many_ (s >> p))++-- | Applies the parser and returns its result, but resets+-- the 'Stream' as if it consumed nothing.+lookAhead :: Stream s t => Parser s a -> Parser s a+lookAhead v@(Parser p) = Parser $ \s -> + let + g (Done a _) = Done a s+ g (Partial p') = Partial $ \s' -> + case s' of+ Nothing -> finalize (p' s)+ _ -> parse (lookAhead v) (streamAppend s s')+ in+ map g (p s)++-- | @count n p@ parses exactly @n@ occurences of @p@.+count :: Stream s t => Int -> Parser s a -> Parser s [a]+count = exactly++-- | Identical to 'many' except the result is discarded.+skipMany :: Stream s t => Parser s a -> Parser s ()+skipMany = M.void . many++-- | Branches every iteration where one branch stops and one branch +-- continues.+skipMany_ :: Stream s t => Parser s a -> Parser s ()+skipMany_ = M.void . many_++-- | Identical to 'many1' except the result is discarded.+skipMany1 :: Stream s t => Parser s a -> Parser s ()+skipMany1 = M.void . many1++-- | Branches every iteration where one branch stops and one branch +-- continues.+skipMany1_ :: Stream s t => Parser s a -> Parser s ()+skipMany1_ = M.void . many1_++-- | @endBy p s@ parses multiple occurences of @p@ separated and ended by+-- @s@.+endBy :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+endBy p s = many (p A.<* s)++-- | Branches every iteration where one branch stops and one branch +-- continues.+endBy_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+endBy_ p s = many_ (p A.<* s)++-- | @endBy1 p s@ parses one or more occurences of @p@ separated and ended +-- by @s@.+endBy1 :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+endBy1 p s = many1 (p A.<* s)++-- | Branches every iteration where one branch stops and one branch +-- continues.+endBy1_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+endBy1_ p s = many1_ (p A.<* s)++-- | @sepEndBy p s@ parses multiple occurences of @p@ separated and +-- optionally ended by @s@.+sepEndBy :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+sepEndBy p s = sepBy p s A.<* optional s++-- | Branches every iteration where one branch stops and one branch +-- continues.+sepEndBy_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+sepEndBy_ p s = sepBy_ p s A.<* optional s++-- | @sepEndBy p s@ parses one or more occurences of @p@ separated and +-- optionally ended by @s@.+sepEndBy1 :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+sepEndBy1 p s = sepBy1 p s A.<* optional s++-- | Branches every iteration where one branch stops and one branch +-- continues.+sepEndBy1_ :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+sepEndBy1_ p s = sepBy1_ p s A.<* optional s++-- | @chainr p o x@ parses zero or more occurences of @p@ separated by @o@. +-- The result is the left associative application of the functions to the+-- values. If @p@ succeeds zero times, @x@ is returned.+chainl :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a+chainl p o x = chainl1 p o <|> return x++-- | Branches every iteration where one branch stops and one branch +-- continues.+chainl_ :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a+chainl_ p o x = chainl1_ p o <||> return x++-- | Like 'chainl', but a minimum of one occurence of @p@ must be parsed.+chainl1 :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> Parser s a+chainl1 p o = p >>= f+ where+ f x =+ do+ g <- o+ y <- p+ f (g x y)+ <|> return x++-- | Branches every iteration where one branch stops and one branch +-- continues.+chainl1_ :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> Parser s a+chainl1_ p o = p >>= f+ where+ f x =+ do+ g <- o+ y <- p+ f (g x y)+ <||> return x++-- | Like 'chainl', but right associative.+chainr :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a+chainr p o x = chainr1 p o <|> return x++-- | Branches every iteration where one branch stops and one branch +-- continues.+chainr_ :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a+chainr_ p o x = chainr1_ p o <||> return x++-- | Like 'chainl1', but right associative.+chainr1 :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> Parser s a+chainr1 p o = p >>= f+ where+ f x = + do+ g <- o+ y <- chainr1 p o+ return (g x y)+ <|> return x++-- | Branches every iteration where one branch stops and one branch +-- continues.+chainr1_ :: Stream s t => Parser s a -> Parser s (a -> a -> a) -> Parser s a+chainr1_ p o = p >>= f+ where+ f x = + do+ g <- o+ y <- chainr1_ p o+ return (g x y)+ <||> return x++-- | Only succeeds when the given parser fails. Consumes no input.+notFollowedBy :: Stream s t => Parser s a -> Parser s ()+notFollowedBy p = test p >>= assert . not++-- | @manyTill a b@ parses multiple occurences of @a@ until @b@ would +-- succeed if tried.+manyTill :: Stream s t => Parser s a -> Parser s b -> Parser s [a]+manyTill p e = + do+ b <- test e+ if b + then return []+ else M.liftM2 (:) p (manyTill p e)++-- | Does nothing -- only used for @Parsec@ compatability.+try :: Stream s t => Parser s a -> Parser s a+try = id++-- | @eitherP a b@ returns the result wrapped in a 'Left' if @a@ succeeds or+-- a 'Right' if @b@ succeeds+eitherP :: Stream s t => Parser s a -> Parser s b -> Parser s (Either a b)+eitherP a b = M.liftM Left a <|> M.liftM Right b++-- | Like 'eitherP', but tries both @a@ and @b@.+eitherP_ :: Stream s t => Parser s a -> Parser s b -> Parser s (Either a b)+eitherP_ a b = M.liftM Left a <||> M.liftM Right b++-- | Parses a sequence of parsers in any order.+perm :: Stream s t => [Parser s a] -> Parser s [a]+perm [] = return []+perm ps = + do+ (i, r) <- select ps+ M.liftM (r:) (perm (let (a,b) = splitAt i ps in a ++ tail b)) ++-- | Parses a sequence of parsers in all possible orders.+perm_ :: Stream s t => [Parser s a] -> Parser s [a]+perm_ [] = return []+perm_ ps = + do+ (i, r) <- select_ ps+ M.liftM (r:) (perm_ (let (a,b) = splitAt i ps in a ++ tail b))
− src/Data/JustParse/Common.hs
@@ -1,284 +0,0 @@-{-|-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
@@ -11,21 +11,21 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE Safe #-}+--{-# LANGUAGE Safe #-} module Data.JustParse.Internal (- finalize,- extend, Stream (..), Parser (..), Result (..), isDone,- isFail, isPartial,- rename,- (<?>)+ toPartial,+ finalize,+ extend,+ streamAppend ) where import Prelude hiding ( length )@@ -35,15 +35,17 @@ 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.+-- type @t@, which must be determinable by the stream. A minimal complete+-- definition only needs to define @uncons@. class (Eq s, Monoid s) => Stream s t | s -> t where- -- | @uncons@ returns @Nothing@ if the @Stream@ is empty, otherwise it+ -- | @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@.+ -- 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.+ -- | 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@@ -55,18 +57,18 @@ parse :: Maybe s -> [Result s a] } -instance Monoid (Parser s a) where+instance Stream s t => 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)+ fmap f (Parser p) = Parser $ map (fmap f) . p instance Applicative (Parser s) where pure = return (<*>) = ap -instance Alternative (Parser s) where+instance Stream s t => Alternative (Parser s) where empty = mzero (<|>) = mplus @@ -74,31 +76,47 @@ 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+instance Stream s t => MonadPlus (Parser s) where mzero = Parser $ const []- mplus (Parser p1) (Parser p2) = Parser (\s -> p1 s ++ p2 s)+ mplus a b = Parser $ \s ->+ let+ g [] = parse b s+ g xs + | any isDone xs = xs+ | otherwise = [Partial $ \s' -> + -- case needed for proper finalization+ case s' of+ -- if finalized+ Nothing -> + case finalize (parse a s) of+ -- if parser a doesn't yield, try b+ [] -> finalize (parse b s)+ -- otherwise give what a yielded+ r -> r+ -- if not finalized+ _ -> parse (mplus a b) (streamAppend s s')] + in+ g (parse a 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.+ -- | 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.+ -- | 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@@ -108,50 +126,35 @@ isPartial (Partial _) = True isPartial _ = False -isFail :: Result s a -> Bool-isFail (Fail _ _) = True-isFail _ = False+-- | Lifts a parser into the result space+toPartial :: Parser s a -> [Result s a]+toPartial (Parser p) = [Partial p] 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@ takes a list of results (presumably returned from a +-- 'Parser' or 'Partial', and supplies 'Nothing' to any remaining 'Partial' +-- values, so that only '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@ takes a @'Maybe' s@ as input, and supplies the input to all +-- values in the 'Result' list. For 'Done' 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))+extend s rs = rs >>= g 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-+ g (Done a s') = [Done a (streamAppend s' s)] -infixl 0 <?>--- | The infix version of 'rename'-(<?>) :: Parser s a -> String -> Parser s a-p <?> s = rename s p+streamAppend :: (Eq s, Monoid s) => Maybe s -> Maybe s -> Maybe s+streamAppend Nothing _ = Nothing +streamAppend (Just s) Nothing = if s == mempty then Nothing else Just s +streamAppend s s' = mappend s s'
src/Data/JustParse/Language.hs view
@@ -1,47 +1,55 @@ {-| Module : Data.JustParse.Language-Description : Regular Expressions and Grammars in JustParse+Description : Regular expressions 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.+Allows for conversion from a regular expression and a 'Parser'. -} {-# LANGUAGE Safe #-} module Data.JustParse.Language ( Match (..), regex,- regex'+ regex_,+ 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 Data.JustParse+import Data.JustParse.Internal+import Data.JustParse.Combinator+import Data.JustParse.Numeric+import Data.JustParse.Char+ import Control.Monad ( liftM, mzero ) import Data.Monoid ( Monoid, mconcat, mempty, mappend )-import Data.Maybe ( isJust )+import Data.Maybe ( isJust, fromMaybe ) 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.+-- If the regex is invalid, it returns a Parser that will always fail.+-- The returned parser is greedy. 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))+regex = greedy . fromMaybe mzero . parseOnly regular +-- | Like 'regex', but returns a branching (non-greedy) parser.+regex_ :: Stream s Char => String -> Parser s Match+regex_ = fromMaybe mzero . parseOnly regular+ -- | The same as 'regex', but only returns the full matched text. regex' :: Stream s Char => String -> Parser s String regex' = liftM matched . regex +-- | 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 {@@ -67,19 +75,48 @@ } regular :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)-regular = liftM (liftM mconcat . sequence) (greedy $ many parser)+regular = liftM (liftM mconcat . sequence) (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+parser = choice [+ asterisk,+ mn,+ pipe,+ plus,+ question,+ group,+ character,+ charClass,+ negCharClass,+ period+ ] parserNP :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)-parserNP = character <|> charClass <|> negCharClass <|> question <|> group <|> asterisk <|> plus <|> mn <|> period +parserNP = choice [+ asterisk,+ mn,+ plus,+ question,+ group,+ character,+ charClass,+ negCharClass,+ period+ ] ++ restricted :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match)-restricted = character <|> charClass <|> negCharClass <|> group <|> period+restricted = choice [+ character,+ charClass,+ negCharClass,+ group,+ period+ ] unreserved :: Stream s Char => Parser s Char -unreserved = (char '\\' >> anyChar ) <|> noneOf "()[]\\*+{}^?:<>|."+unreserved = (char '\\' >> anyChar ) <|> noneOf "()[]\\*+{}^?|." character :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match) character = @@ -93,7 +130,7 @@ charClass = do char '['- c <- greedy (many1 unreserved)+ c <- many1 unreserved char ']' return $ do c' <- oneOf c@@ -103,7 +140,7 @@ negCharClass = do string "[^"- c <- greedy (many1 unreserved)+ c <- many1 unreserved char ']' return $ do c' <- noneOf c@@ -123,12 +160,12 @@ do p <- restricted char '?'- return $ liftM mconcat (mN 0 1 p)+ return $ liftM mconcat (mN_ 0 1 p) group :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match) group = do- string "("+ char '(' p <- regular char ')' return $ do@@ -140,25 +177,25 @@ do p <- restricted char '*'- return $ liftM mconcat (many p)+ 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)+ 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)+ l <- option 0 decInt char ','- r <- optional (many1 digit)+ r <- option (-1) decInt char '}'- return $ liftM mconcat (mN (maybe 0 read l) (maybe (-1) read r) p)+ return $ liftM mconcat (mN_ l r p) pipe :: (Stream s0 Char, Stream s1 Char) => Parser s0 (Parser s1 Match) pipe = @@ -166,4 +203,4 @@ p <- parserNP char '|' p' <- parser- return $ p <|> p'+ return $ p <||> p'
+ src/Data/JustParse/Numeric.hs view
@@ -0,0 +1,101 @@+{-|+Module : Data.JustParse.Numeric+Description : Numeric parsing+Copyright : Copyright Waived+License : PublicDomain+Maintainer : grantslatton@gmail.com+Stability : experimental+Portability : portable++Parsers for dealing with signed and unsigned 'Int's and 'Float's.+-}++module Data.JustParse.Numeric (+ decFloat,+ decFloat_,+ decInt,+ decInt_,+ hexInt,+ hexInt_+) where++--{-# LANGUAGE Safe #-}+import Data.JustParse.Combinator+import Data.JustParse.Internal+import Data.JustParse.Char+import Control.Monad ( liftM, liftM2 )+import Data.Char ( ord, digitToInt, toUpper, isDigit, isHexDigit )++-- | Reads many decimal digits and returns them as an @Int@.+decInt :: Stream s Char => Parser s Int+decInt = + do+ sign <- optional (oneOf "-+")+ num <- liftM read (many1 digit)+ case sign of+ Just '-' -> return (-num)+ _ -> return num++-- | Branching version of decInt.+decInt_ :: Stream s Char => Parser s Int+decInt_ = + do+ sign <- optional (oneOf "-+")+ num <- liftM read (many1_ digit)+ case sign of+ Just '-' -> return (-num)+ _ -> return num++-- | Parse a float. If a decimal point is present, it must have at +-- least one digit before and after the decimal point.+decFloat :: Stream s Char => Parser s Float+decFloat = + do+ sign <- optional (oneOf "-+")+ whole <- many1 digit+ fractional <- option ".0" (liftM2 (:) (char '.') (many1 digit))+ case sign of+ Just '-' -> return (-(read (whole ++ fractional)))+ _ -> return (read (whole ++ fractional))++-- | Branching version of decFloat.+decFloat_ :: Stream s Char => Parser s Float+decFloat_ = + do+ sign <- optional (oneOf "-+")+ whole <- many1_ digit+ fractional <- option_ ".0" (liftM2 (:) (char '.') (many1_ digit))+ case sign of+ Just '-' -> return (-(read (whole ++ fractional)))+ _ -> return (read (whole ++ fractional))+++-- | Reads many hexidecimal digits and returns them as an @Int@.+hexInt :: Stream s Char => Parser s Int+hexInt = + do+ sign <- optional (oneOf "-+")+ num <- liftM (f . reverse) (many1 hexDigit)+ case sign of+ Just '-' -> return (-num)+ _ -> return num+ where+ f [] = 0+ f (x:xs) + | isDigit x = digitToInt x + 16 * f xs + | otherwise = ord (toUpper x) - ord 'A' + 16 * f xs++-- | Branching versino of 'hexInt'.+hexInt_ :: Stream s Char => Parser s Int+hexInt_ = + do+ sign <- optional (oneOf "-+")+ num <- liftM (f . reverse) (many1_ hexDigit)+ case sign of+ Just '-' -> return (-num)+ _ -> return num+ where+ f [] = 0+ f (x:xs) + | isDigit x = digitToInt x + 16 * f xs + | otherwise = ord (toUpper x) - ord 'A' + 16 * f xs