@@ -1,5 +1,3 @@
4-- Module : Streamly.Internal.Data.Parser.ParserD
2-- Module : Streamly.Internal.Data.Parser.ParserD
5-- Copyright : (c) 2020 Composewell Technologies
3-- Copyright : (c) 2020 Composewell Technologies
@@ -8,7 +6,42 @@
8-- Stability : experimental
6-- Stability : experimental
9-- Portability : GHC
7-- Portability : GHC
11−-- Direct style parser implementation with stream fusion.
9+-- Fast backtracking parsers with stream fusion and native streaming
12+-- 'Applicative' and 'Control.Applicative.Alternative' type class based
13+-- combinators from the
14+-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>
15+-- package can also be used with the 'Parser' type. However, there are two
16+-- important differences between @parser-combinators@ and the equivalent ones
17+-- provided in this module in terms of performance:
19+-- 1) @parser-combinators@ use plain Haskell lists to collect the results, in a
20+-- strict Monad like IO, the results are necessarily buffered before they can
21+-- be consumed. This may not perform optimally in streaming applications
22+-- processing large amounts of data. Equivalent combinators in this module can
23+-- consume the results of parsing using a 'Fold', thus providing a scalability
24+-- and a composable consumer.
26+-- 2) Several combinators in this module can be many times faster because of
27+-- stream fusion. For example, 'Streamly.Internal.Data.Parser.many' combinator
28+-- in this module is much faster than the 'Control.Applicative.many' combinator
29+-- of 'Control.Applicative.Alternative' type class.
33+-- Failing parsers in this module throw the 'D.ParseError' exception.
37+-- As far as possible, try that the names of the combinators in this module are
40+-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>
41+-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>
42+-- * <http://hackage.haskell.org/package/megaparsec megaparsec>
43+-- * <http://hackage.haskell.org/package/attoparsec attoparsec>
44+-- * <http://hackage.haskell.org/package/parsec parsec>
13module Streamly.Internal.Data.Parser.ParserD
46module Streamly.Internal.Data.Parser.ParserD
@@ -16,7 +49,6 @@ module Streamly.Internal.Data.Parser.ParserD
16 , ParseError (..)
49 , ParseError (..)
17 , Step (..)
50 , Step (..)
18 , Initial (..)
51 , Initial (..)
21 -- * Conversion to/from ParserK
53 -- * Conversion to/from ParserK
22 , fromParserK
54 , fromParserK
@@ -40,17 +72,26 @@ module Streamly.Internal.Data.Parser.ParserD
40 , postscan
72 , postscan
43 -- * Element parsers
78 -- * Element parsers
81+ -- All of these can be expressed in terms of either
51− -- * Sequence parsers
92+ -- * Sequence parsers (tokenizers)
53− -- Parsers chained in series, if one parser terminates the composition
94+ -- | Parsers chained in series, if one parser terminates the composition
54 -- terminates. Currently we are using folds to collect the output of the
95 -- terminates. Currently we are using folds to collect the output of the
55 -- parsers but we can use Parsers instead of folds to make the composition
96 -- parsers but we can use Parsers instead of folds to make the composition
56 -- more powerful. For example, we can do:
97 -- more powerful. For example, we can do:
@@ -58,41 +99,54 @@ module Streamly.Internal.Data.Parser.ParserD
58 -- takeEndByOrMax cond n p = takeEndBy cond (take n p)
99 -- takeEndByOrMax cond n p = takeEndBy cond (take n p)
59 -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)
100 -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)
60 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
101 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
62− -- Grab a sequence of input elements without inspecting them
105+ -- | Grab a sequence of input elements without inspecting them
63 , takeBetween
106 , takeBetween
64− -- , take -- take -- takeBetween 0 n
107+ -- , take -- takeBetween 0 n
65 , takeEQ -- takeBetween n n
108 , takeEQ -- takeBetween n n
66 , takeGE -- takeBetween n maxBound
109 , takeGE -- takeBetween n maxBound
67 -- , takeGE1 -- take1 -- takeBetween 1 n
110 -- , takeGE1 -- take1 -- takeBetween 1 n
70 -- Grab a sequence of input elements by inspecting them
113 -- Grab a sequence of input elements by inspecting them
71− , lookAhead
114+ -- ** Exact match
72 , takeWhile
121 , takeWhile
73 , takeWhileP
122 , takeWhileP
74 , takeWhile1
123 , takeWhile1
76− -- Separators
126+ -- ** Separators
77 , takeEndBy
127 , takeEndBy
78 , takeEndBy_
128 , takeEndBy_
79 , takeEndByEsc
129 , takeEndByEsc
80 , takeStartBy
131 , takeStartBy
139+ , groupByRollingEither
81 , takeFramedBy_
143 , takeFramedBy_
82 , takeFramedByEsc_
144 , takeFramedByEsc_
83 , takeFramedByGeneric
145 , takeFramedByGeneric
85− -- Words and grouping
87 , wordFramedBy
146 , wordFramedBy
88 , wordQuotedBy
147 , wordQuotedBy
91− , groupByRollingEither
93 -- Matching strings
149 -- Matching strings
96 -- , prefixOf -- match any prefix of a given string
150 -- , prefixOf -- match any prefix of a given string
97 -- , suffixOf -- match any suffix of a given string
151 -- , suffixOf -- match any suffix of a given string
98 -- , infixOf -- match any substring of a given string
152 -- , infixOf -- match any substring of a given string
@@ -121,6 +175,25 @@ module Streamly.Internal.Data.Parser.ParserD
121 -- Use two folds, run a primary parser, its rejected values go to the
175 -- Use two folds, run a primary parser, its rejected values go to the
122 -- secondary parser.
176 -- secondary parser.
123 , deintercalate
177 , deintercalate
178+ -- , deintercalatePrefix
179+ -- , deintercalateSuffix
181+ -- *** Special cases
182+ -- | TODO: traditional implmentations of these may be of limited use. For
183+ -- example, consider parsing lines separated by @\\r\\n@. The main parser
184+ -- will have to detect and exclude the sequence @\\r\\n@ anyway so that we
185+ -- can apply the "sep" parser.
187+ -- We can instead implement these as special cases of deintercalate.
126 -- ** Sequential Alternative
199 -- ** Sequential Alternative
@@ -132,6 +205,7 @@ module Streamly.Internal.Data.Parser.ParserD
132 , longest
205 , longest
133 -- , fastest
206 -- , fastest
135 -- * N-ary Combinators
209 -- * N-ary Combinators
136 -- ** Sequential Collection
210 -- ** Sequential Collection
137 , sequence
211 , sequence
@@ -142,25 +216,12 @@ module Streamly.Internal.Data.Parser.ParserD
142 , countBetween
216 , countBetween
143 -- , countBetweenTill
217 -- , countBetweenTill
148 , manyTill
223 , manyTill
150− -- -- ** Special cases
151− -- XXX traditional implmentations of these may be of limited use. For
152− -- example, consider parsing lines separated by "\r\n". The main parser
153− -- will have to detect and exclude the sequence "\r\n" anyway so that we
154− -- can apply the "sep" parser.
156− -- We can instead implement these as special cases of deintercalate.
165 -- -- * Distribution
226 -- -- * Distribution
@@ -171,24 +232,32 @@ module Streamly.Internal.Data.Parser.ParserD
171 -- -- ** Distribute to collection
232 -- -- ** Distribute to collection
172 -- -- ** Distribute to repetition
233 -- -- ** Distribute to repetition
174− -- -- ** Interleaved collection
235+ -- ** Interleaved collection
175− -- Round robin
238+ -- 1. Round robin
176− -- Priority based
239+ -- 2. Priority based
177 -- -- ** Interleaved repetition
242 -- -- ** Interleaved repetition
178 -- repeat one parser and when it fails run an error recovery parser
243 -- repeat one parser and when it fails run an error recovery parser
179 -- e.g. to find a key frame in the stream after an error
244 -- e.g. to find a key frame in the stream after an error
181 -- ** Collection of Alternatives
246 -- ** Collection of Alternatives
182 -- , shortestN
250 -- , shortestN
183 -- , longestN
251 -- , longestN
184 -- , fastestN -- first N successful in time
252 -- , fastestN -- first N successful in time
185 -- , choiceN -- first N successful in position
253 -- , choiceN -- first N successful in position
186 -- , choice -- first successful in position
255 -- , choice -- first successful in position
188− -- -- ** Repeated Alternatives
257+ -- ** Repeated Alternatives
189− -- , retryMax -- try N times
258+ , retryMaxTotal
190− -- , retryUntil -- try until successful
259+ , retryMaxSuccessive
191− -- , retryUntilN -- try until successful n times
260+ , retry
193 -- ** Zipping Input
262 -- ** Zipping Input
194 , zipWithM
263 , zipWithM
@@ -196,9 +265,13 @@ module Streamly.Internal.Data.Parser.ParserD
196 , indexed
265 , indexed
197 , makeIndexFilter
266 , makeIndexFilter
198 , sampleFromthen
267 , sampleFromthen
202#include "assert.hs"
275#include "assert.hs"
204import Control.Monad (when)
277import Control.Monad (when)
@@ -209,14 +282,16 @@ import Streamly.Internal.Data.SVar.Type (defState)
209import Streamly.Internal.Data.Either.Strict (Either'(..))
282import Streamly.Internal.Data.Either.Strict (Either'(..))
210import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
283import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
211import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
284import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
285+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
287+import qualified Data.Foldable as Foldable
213import qualified Streamly.Internal.Data.Fold.Type as FL
288import qualified Streamly.Internal.Data.Fold.Type as FL
214import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
289import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
215import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
290import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
217import Prelude hiding
292import Prelude hiding
218 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span
293 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span
219− , zip, filter)
294+ , zip, filter, dropWhile)
220-- import Streamly.Internal.Data.Parser.ParserD.Tee
295-- import Streamly.Internal.Data.Parser.ParserD.Tee
221import Streamly.Internal.Data.Parser.ParserD.Type
296import Streamly.Internal.Data.Parser.ParserD.Type
@@ -224,19 +299,27 @@ import Streamly.Internal.Data.Parser.ParserD.Type
226-- >>> import Prelude hiding ()
301-- >>> import Prelude hiding ()
302+-- >>> import Control.Applicative
303+-- >>> import Data.Char (isSpace)
304+-- >>> import qualified Data.Foldable as Foldable
227-- >>> import qualified Data.Maybe as Maybe
305-- >>> import qualified Data.Maybe as Maybe
228-- >>> import qualified Streamly.Data.Stream as Stream
306-- >>> import qualified Streamly.Data.Stream as Stream
229-- >>> import qualified Streamly.Internal.Data.Stream as Stream
307-- >>> import qualified Streamly.Internal.Data.Stream as Stream
230−-- >>> import qualified Streamly.Data.Fold as Fold
308+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
231-- >>> import qualified Streamly.Internal.Data.Parser as Parser
309-- >>> import qualified Streamly.Internal.Data.Parser as Parser
233-------------------------------------------------------------------------------
311-------------------------------------------------------------------------------
234-- Downgrade a parser to a Fold
312-- Downgrade a parser to a Fold
235-------------------------------------------------------------------------------
313-------------------------------------------------------------------------------
237−-- | See 'Streamly.Internal.Data.Parser.toFold'.
315+-- | Make a 'Fold' from a 'Parser'. The fold just throws an exception if the
316+-- parser fails or tries to backtrack.
239−-- /Internal/
318+-- This can be useful in combinators that accept a Fold and we know that a
319+-- Parser cannot fail or failure exception is acceptable as there is no way to
241{-# INLINE toFold #-}
324{-# INLINE toFold #-}
242toFold :: Monad m => Parser a m b -> Fold m a b
325toFold :: Monad m => Parser a m b -> Fold m a b
@@ -280,10 +363,8 @@ toFold (Parser pstep pinitial pextract) = Fold step initial extract
280-------------------------------------------------------------------------------
363-------------------------------------------------------------------------------
281-- Upgrade folds to parses
364-- Upgrade folds to parses
282-------------------------------------------------------------------------------
365-------------------------------------------------------------------------------
284−-- | See 'Streamly.Internal.Data.Parser.fromFold'.
367+-- | Make a 'Parser' from a 'Fold'.
288{-# INLINE fromFold #-}
369{-# INLINE fromFold #-}
289fromFold :: Monad m => Fold m a b -> Parser a m b
370fromFold :: Monad m => Fold m a b -> Parser a m b
@@ -307,7 +388,9 @@ fromFold (Fold fstep finitial fextract) = Parser step initial extract
308 extract = fmap (Done 0) . fextract
389 extract = fmap (Done 0) . fextract
310−-- | Convert Maybe returning folds to error returning parsers.
391+-- | Convert a Maybe returning fold to an error returning parser. The first
392+-- argument is the error message that the parser would return when the fold
312-- /Pre-release/
395-- /Pre-release/
@@ -348,9 +431,15 @@ fromFoldMaybe errMsg (Fold fstep finitial fextract) =
348-- Failing Parsers
431-- Failing Parsers
349-------------------------------------------------------------------------------
432-------------------------------------------------------------------------------
351−-- | See 'Streamly.Internal.Data.Parser.peek'.
434+-- | Peek the head element of a stream, without consuming it. Fails if it
435+-- encounters end of input.
353−-- /Pre-release/
437+-- >>> Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]
441+-- peek = lookAhead (satisfy True)
355{-# INLINE peek #-}
444{-# INLINE peek #-}
356peek :: Monad m => Parser a m a
445peek :: Monad m => Parser a m a
@@ -364,9 +453,10 @@ peek = Parser step initial extract
365 extract () = return $ Error "peek: end of input"
454 extract () = return $ Error "peek: end of input"
367−-- | See 'Streamly.Internal.Data.Parser.eof'.
456+-- | Succeeds if we are at the end of input, fails otherwise.
369−-- /Pre-release/
458+-- >>> Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]
371{-# INLINE eof #-}
461{-# INLINE eof #-}
372eof :: Monad m => Parser a m ()
462eof :: Monad m => Parser a m ()
@@ -380,10 +470,12 @@ eof = Parser step initial extract
381 extract () = return $ Done 0 ()
471 extract () = return $ Done 0 ()
383−-- | See 'Streamly.Internal.Data.Parser.next'.
473+-- | Return the next element of the input. Returns 'Nothing'
474+-- on end of input. Also known as 'head'.
385-- /Pre-release/
476-- /Pre-release/
478+{-# DEPRECATED next "Please use \"fromFold Fold.one\" instead" #-}
387{-# INLINE next #-}
479{-# INLINE next #-}
388next :: Monad m => Parser a m (Maybe a)
480next :: Monad m => Parser a m (Maybe a)
389next = Parser step initial extract
481next = Parser step initial extract
@@ -396,7 +488,9 @@ next = Parser step initial extract
397 extract () = pure $ Done 0 Nothing
489 extract () = pure $ Done 0 Nothing
399−-- | See 'Streamly.Internal.Data.Parser.either'.
491+-- | Map an 'Either' returning function on the next element in the stream. If
492+-- the function returns 'Left err', the parser fails with the error message
493+-- @err@ otherwise returns the 'Right' value.
401-- /Pre-release/
495-- /Pre-release/
@@ -415,11 +509,15 @@ either f = Parser step initial extract
416 extract () = return $ Error "end of input"
510 extract () = return $ Error "end of input"
418−-- | See 'Streamly.Internal.Data.Parser.maybe'.
512+-- | Map a 'Maybe' returning function on the next element in the stream. The
513+-- parser fails if the function returns 'Nothing' otherwise returns the 'Just'
420-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
516-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
421-- >>> maybe f = Parser.either (toEither . f)
517-- >>> maybe f = Parser.either (toEither . f)
519+-- >>> maybe f = Parser.fromFoldMaybe "maybe: predicate failed" (Fold.maybe f)
423-- /Pre-release/
521-- /Pre-release/
425{-# INLINE maybe #-}
523{-# INLINE maybe #-}
@@ -438,13 +536,14 @@ maybe parserF = Parser step initial extract
439 extract () = return $ Error "maybe: end of input"
537 extract () = return $ Error "maybe: end of input"
441−-- | See 'Streamly.Internal.Data.Parser.satisfy'.
539+-- | Returns the next element if it passes the predicate, fails otherwise.
541+-- >>> Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]
443-- >>> toMaybe f x = if f x then Just x else Nothing
544-- >>> toMaybe f x = if f x then Just x else Nothing
444-- >>> satisfy f = Parser.maybe (toMaybe f)
545-- >>> satisfy f = Parser.maybe (toMaybe f)
448{-# INLINE satisfy #-}
547{-# INLINE satisfy #-}
449satisfy :: Monad m => (a -> Bool) -> Parser a m a
548satisfy :: Monad m => (a -> Bool) -> Parser a m a
450-- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)
549-- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)
@@ -461,6 +560,67 @@ satisfy predicate = Parser step initial extract
462 extract () = return $ Error "satisfy: end of input"
561 extract () = return $ Error "satisfy: end of input"
563+-- | Consume one element from the head of the stream. Fails if it encounters
566+-- >>> one = Parser.satisfy $ const True
569+one :: Monad m => Parser a m a
570+one = satisfy $ const True
572+-- Alternate names: "only", "onlyThis".
574+-- | Match a specific element.
576+-- >>> oneEq x = Parser.satisfy (== x)
579+oneEq :: (Monad m, Eq a) => a -> Parser a m a
580+oneEq x = satisfy (== x)
582+-- Alternate names: "exclude", "notThis".
584+-- | Match anything other than the supplied element.
586+-- >>> oneNotEq x = Parser.satisfy (/= x)
588+{-# INLINE oneNotEq #-}
589+oneNotEq :: (Monad m, Eq a) => a -> Parser a m a
590+oneNotEq x = satisfy (/= x)
592+-- | Match any one of the elements in the supplied list.
594+-- >>> oneOf xs = Parser.satisfy (`Foldable.elem` xs)
596+-- When performance matters a pattern matching predicate could be more
597+-- efficient than a 'Foldable' datatype:
608+-- GHC may use a binary search instead of linear search in the list.
609+-- Alternatively, you can also use an array instead of list for storage and
613+oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
614+oneOf xs = satisfy (`Foldable.elem` xs)
616+-- | See performance notes in 'oneOf'.
618+-- >>> noneOf xs = Parser.satisfy (`Foldable.notElem` xs)
620+{-# INLINE noneOf #-}
621+noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
622+noneOf xs = satisfy (`Foldable.notElem` xs)
464-------------------------------------------------------------------------------
624-------------------------------------------------------------------------------
465-- Taking elements
625-- Taking elements
466-------------------------------------------------------------------------------
626-------------------------------------------------------------------------------
@@ -469,7 +629,43 @@ satisfy predicate = Parser step initial extract
469{-# ANN type Tuple'Fused Fuse #-}
629{-# ANN type Tuple'Fused Fuse #-}
470data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
630data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
632+-- | @takeBetween m n@ takes a minimum of @m@ and a maximum of @n@ input
633+-- elements and folds them using the supplied fold.
635+-- Stops after @n@ elements.
636+-- Fails if the stream ends before @m@ elements could be taken.
642+-- takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)
472−-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
643+-- where prsr = Parser.takeBetween low high Fold.toList
648+-- >>> takeBetween' 2 4 [1, 2, 3, 4, 5]
651+-- >>> takeBetween' 2 4 [1, 2]
654+-- >>> takeBetween' 2 4 [1]
655+-- Left (ParseError "takeBetween: Expecting alteast 2 elements, got 1")
657+-- >>> takeBetween' 0 0 [1, 2]
660+-- >>> takeBetween' 0 1 []
663+-- @takeBetween@ is the most general take operation, other take operations can
664+-- be defined in terms of takeBetween. For example:
666+-- >>> take n = Parser.takeBetween 0 n
667+-- >>> takeEQ n = Parser.takeBetween n n
668+-- >>> takeGE n = Parser.takeBetween n maxBound
474-- /Pre-release/
670-- /Pre-release/
@@ -546,9 +742,14 @@ takeBetween low high (Fold fstep finitial fextract) =
546 | i >= low && i <= high = fmap IDone (fextract s)
742 | i >= low && i <= high = fmap IDone (fextract s)
547 | otherwise = return $ IError (f i)
743 | otherwise = return $ IError (f i)
549−-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
745+-- | Stops after taking exactly @n@ input elements.
551−-- /Pre-release/
747+-- * Stops - after consuming @n@ elements.
748+-- * Fails - if the stream or the collecting fold ends before it can collect
749+-- exactly @n@ elements.
751+-- >>> Stream.parse (Parser.takeEQ 4 Fold.toList) $ Stream.fromList [1,0,1]
752+-- Left (ParseError "takeEQ: Expecting exactly 4 elements, input terminated on 3")
553{-# INLINE takeEQ #-}
754{-# INLINE takeEQ #-}
554takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b
755takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b
@@ -602,7 +803,17 @@ takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
602 $ "takeEQ: Expecting exactly " ++ show cnt
803 $ "takeEQ: Expecting exactly " ++ show cnt
603 ++ " elements, input terminated on " ++ show i
804 ++ " elements, input terminated on " ++ show i
605−-- | See 'Streamly.Internal.Data.Parser.takeGE'.
806+-- | Take at least @n@ input elements, but can collect more.
808+-- * Stops - when the collecting fold stops.
809+-- * Fails - if the stream or the collecting fold ends before producing @n@
812+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1]
813+-- Left (ParseError "takeGE: Expecting at least 4 elements, input terminated on 3")
815+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]
607-- /Pre-release/
818-- /Pre-release/
@@ -659,7 +870,19 @@ takeGE n (Fold fstep finitial fextract) = Parser step initial extract
659-- Conditional splitting
870-- Conditional splitting
660-------------------------------------------------------------------------------
871-------------------------------------------------------------------------------
662−-- | See 'Streamly.Internal.Data.Parser.takeWhileP'.
873+-- XXX We should perhaps use only takeWhileP and rename it to takeWhile.
875+-- | Like 'takeWhile' but uses a 'Parser' instead of a 'Fold' to collect the
876+-- input. The combinator stops when the condition fails or if the collecting
879+-- Other interesting parsers can be implemented in terms of this parser:
881+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
882+-- >>> takeWhileBetween cond m n p = Parser.takeWhileP cond (Parser.takeBetween m n p)
884+-- Stops: when the condition fails or the collecting parser stops.
885+-- Fails: when the collecting parser fails.
664-- /Pre-release/
887-- /Pre-release/
@@ -682,12 +905,26 @@ takeWhileP predicate (Parser pstep pinitial pextract) =
682 Partial _ _ -> error "Bug: takeWhileP: Partial in extract"
905 Partial _ _ -> error "Bug: takeWhileP: Partial in extract"
683 Continue n s1 -> return $ Continue (n + 1) s1
906 Continue n s1 -> return $ Continue (n + 1) s1
685−-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
908+-- | Collect stream elements until an element fails the predicate. The element
909+-- on which the predicate fails is returned back to the input stream.
687−-- /Pre-release/
911+-- * Stops - when the predicate fails or the collecting fold stops.
914+-- >>> Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]
917+-- >>> takeWhile cond f = Parser.takeWhileP cond (Parser.fromFold f)
919+-- We can implement a @breakOn@ using 'takeWhile':
922+-- breakOn p = takeWhile (not p)
689{-# INLINE takeWhile #-}
925{-# INLINE takeWhile #-}
690takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
926takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
927+-- takeWhile cond f = takeWhileP cond (fromFold f)
691takeWhile predicate (Fold fstep finitial fextract) =
928takeWhile predicate (Fold fstep finitial fextract) =
692 Parser step initial extract
929 Parser step initial extract
@@ -711,12 +948,13 @@ takeWhile predicate (Fold fstep finitial fextract) =
712 extract s = fmap (Done 0) (fextract s)
949 extract s = fmap (Done 0) (fextract s)
714−-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
951+-- | Like 'takeWhile' but takes at least one element otherwise fails.
716−-- /Pre-release/
953+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
718{-# INLINE takeWhile1 #-}
955{-# INLINE takeWhile1 #-}
719takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
956takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
957+-- takeWhile1 cond f = takeWhileP cond (takeBetween 1 maxBound f)
720takeWhile1 predicate (Fold fstep finitial fextract) =
958takeWhile1 predicate (Fold fstep finitial fextract) =
721 Parser step initial extract
959 Parser step initial extract
@@ -753,6 +991,17 @@ takeWhile1 predicate (Fold fstep finitial fextract) =
753 extract (Left' _) = return $ Error "takeWhile1: end of input"
991 extract (Left' _) = return $ Error "takeWhile1: end of input"
754 extract (Right' s) = fmap (Done 0) (fextract s)
992 extract (Right' s) = fmap (Done 0) (fextract s)
994+-- | Drain the input as long as the predicate succeeds, running the effects and
995+-- discarding the results.
997+-- This is also called @skipWhile@ in some parsing libraries.
999+-- >>> dropWhile p = Parser.takeWhile p Fold.drain
1001+{-# INLINE dropWhile #-}
1002+dropWhile :: Monad m => (a -> Bool) -> Parser a m ()
1003+dropWhile p = takeWhile p FL.drain
756-------------------------------------------------------------------------------
1005-------------------------------------------------------------------------------
757-- Separators
1006-- Separators
758-------------------------------------------------------------------------------
1007-------------------------------------------------------------------------------
@@ -760,6 +1009,7 @@ takeWhile1 predicate (Fold fstep finitial fextract) =
760data FramedEscState s =
1009data FramedEscState s =
761 FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int
1010 FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int
1012+-- XXX We can remove Maybe from esc
763{-# INLINE takeFramedByGeneric #-}
1013{-# INLINE takeFramedByGeneric #-}
764takeFramedByGeneric :: Monad m =>
1014takeFramedByGeneric :: Monad m =>
765 Maybe (a -> Bool)
1015 Maybe (a -> Bool)
@@ -857,12 +1107,26 @@ takeFramedByGeneric esc begin end (Fold fstep finitial fextract) =
857 Nothing -> err "takeFramedByGeneric: missing closing frame"
1107 Nothing -> err "takeFramedByGeneric: missing closing frame"
858 extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"
1108 extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"
860−-- | See 'Streamly.Internal.Data.Parser.takeEndBy'.
1110+-- | @takeEndBy cond parser@ parses a token that ends by a separator chosen by
1111+-- the supplied predicate. The separator is also taken with the token.
1113+-- This can be combined with other parsers to implement other interesting
1114+-- parsers as follows:
1116+-- >>> takeEndByLE cond n p = Parser.takeEndBy cond (Parser.fromFold $ Fold.take n p)
1117+-- >>> takeEndByBetween cond m n p = Parser.takeEndBy cond (Parser.takeBetween m n p)
1119+-- >>> takeEndBy = Parser.takeEndByEsc (const False)
1121+-- See also "Streamly.Data.Fold.takeEndBy". Unlike the fold, the collecting
1122+-- parser in the takeEndBy parser can decide whether to fail or not if the
1123+-- stream does not end with separator.
862-- /Pre-release/
1125-- /Pre-release/
864{-# INLINE takeEndBy #-}
1127{-# INLINE takeEndBy #-}
865takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
1128takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
1129+-- takeEndBy = takeEndByEsc (const False)
866takeEndBy cond (Parser pstep pinitial pextract) =
1130takeEndBy cond (Parser pstep pinitial pextract) =
868 Parser step initial pextract
1132 Parser step initial pextract
@@ -877,10 +1141,11 @@ takeEndBy cond (Parser pstep pinitial pextract) =
877 then return res
1141 then return res
878 else extractStep pextract res
1142 else extractStep pextract res
880−-- | See 'Streamly.Internal.Data.Parser.takeEndByEsc'.
1144+-- | Like 'takeEndBy' but the separator elements can be escaped using an
881−--
1145+-- escape char determined by the first predicate. The escape characters are
882−-- /Pre-release/
1146+-- removed.
884{-# INLINE takeEndByEsc #-}
1149{-# INLINE takeEndByEsc #-}
885takeEndByEsc :: Monad m =>
1150takeEndByEsc :: Monad m =>
886 (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b
1151 (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b
@@ -909,12 +1174,18 @@ takeEndByEsc isEsc isSep (Parser pstep pinitial pextract) =
909 extract (Right' _) =
1174 extract (Right' _) =
910 return $ Error "takeEndByEsc: trailing escape"
1175 return $ Error "takeEndByEsc: trailing escape"
1177+-- | Like 'takeEndBy' but the separator is dropped.
912−-- | See 'Streamly.Internal.Data.Parser.takeEndBy_'.
1179+-- See also "Streamly.Data.Fold.takeEndBy_".
914-- /Pre-release/
1181-- /Pre-release/
916{-# INLINE takeEndBy_ #-}
1183{-# INLINE takeEndBy_ #-}
917takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b
1184takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b
1186+takeEndBy_ isEnd p =
1187+ takeFramedByGeneric Nothing Nothing (Just isEnd) (toFold p)
918takeEndBy_ cond (Parser pstep pinitial pextract) =
1189takeEndBy_ cond (Parser pstep pinitial pextract) =
920 Parser step pinitial pextract
1191 Parser step pinitial pextract
@@ -926,11 +1197,39 @@ takeEndBy_ cond (Parser pstep pinitial pextract) =
926 then pextract s
1197 then pextract s
927 else pstep s a
1198 else pstep s a
929−-- | See 'Streamly.Internal.Data.Parser.takeStartBy'.
1200+-- | Take either the separator or the token. Separator is a Left value and
1201+-- token is Right value.
1204+{-# INLINE takeEitherSepBy #-}
1205+takeEitherSepBy :: -- Monad m =>
1206+ (a -> Bool) -> Fold m (Either a b) c -> Parser a m c
1207+takeEitherSepBy _cond = undefined -- D.toParserK . D.takeEitherSepBy cond
1209+-- | Parse a token that starts with an element chosen by the predicate. The
1210+-- parser fails if the input does not start with the selected element.
1212+-- * Stops - when the predicate succeeds in non-leading position.
1213+-- * Fails - when the predicate fails in the leading position.
1215+-- >>> splitWithPrefix p f = Stream.parseMany (Parser.takeStartBy p f)
1219+-- >>> p = Parser.takeStartBy (== ',') Fold.toList
1220+-- >>> leadingComma = Stream.parse p . Stream.fromList
1221+-- >>> leadingComma "a,b"
1222+-- Left (ParseError "takeStartBy: missing frame start")
1224+-- >>> leadingComma ",,"
1226+-- >>> leadingComma ",a,b"
1228+-- >>> leadingComma ""
931-- /Pre-release/
1231-- /Pre-release/
934{-# INLINE takeStartBy #-}
1233{-# INLINE takeStartBy #-}
935takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
1234takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
936takeStartBy cond (Fold fstep finitial fextract) =
1235takeStartBy cond (Fold fstep finitial fextract) =
@@ -966,9 +1265,40 @@ takeStartBy cond (Fold fstep finitial fextract) =
966 extract (Left' s) = fmap (Done 0) $ fextract s
1265 extract (Left' s) = fmap (Done 0) $ fextract s
967 extract (Right' s) = fmap (Done 0) $ fextract s
1266 extract (Right' s) = fmap (Done 0) $ fextract s
1268+-- | Like 'takeStartBy' but drops the separator.
1270+-- >>> takeStartBy_ isBegin = Parser.takeFramedByGeneric Nothing (Just isBegin) Nothing
1272+{-# INLINE takeStartBy_ #-}
1273+takeStartBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
1274+takeStartBy_ isBegin = takeFramedByGeneric Nothing (Just isBegin) Nothing
1276+-- | @takeFramedByEsc_ isEsc isBegin isEnd fold@ parses a token framed using a
1277+-- begin and end predicate, and an escape character. The frame begin and end
1278+-- characters lose their special meaning if preceded by the escape character.
1280+-- Nested frames are allowed if begin and end markers are different, nested
1281+-- frames must be balanced unless escaped, nested frame markers are emitted as
1286+-- >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList
1287+-- >>> Stream.parse p $ Stream.fromList "{hello}"
1289+-- >>> Stream.parse p $ Stream.fromList "{hello {world}}"
1290+-- Right "hello {world}"
1291+-- >>> Stream.parse p $ Stream.fromList "{hello \\{world}"
1292+-- Right "hello {world"
1293+-- >>> Stream.parse p $ Stream.fromList "{hello {world}"
1294+-- Left (ParseError "takeFramedByEsc_: missing frame end")
969{-# INLINE takeFramedByEsc_ #-}
1297{-# INLINE takeFramedByEsc_ #-}
970takeFramedByEsc_ :: Monad m =>
1298takeFramedByEsc_ :: Monad m =>
971 (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
1299 (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
1300+-- takeFramedByEsc_ isEsc isEnd p =
1301+-- takeFramedByGeneric (Just isEsc) Nothing (Just isEnd) (toFold p)
972takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
1302takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
974 Parser step initial extract
1304 Parser step initial extract
@@ -1017,9 +1347,16 @@ takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
1018data FramedState s = FrameInit !s | FrameGo !s Int
1348data FramedState s = FrameInit !s | FrameGo !s Int
1350+-- | @takeFramedBy_ isBegin isEnd fold@ parses a token framed by a begin and an
1353+-- >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)
1020{-# INLINE takeFramedBy_ #-}
1355{-# INLINE takeFramedBy_ #-}
1021takeFramedBy_ :: Monad m =>
1356takeFramedBy_ :: Monad m =>
1022 (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
1357 (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
1358+-- takeFramedBy_ isBegin isEnd =
1359+-- takeFramedByGeneric (Just (const False)) (Just isBegin) (Just isEnd)
1023takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
1360takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
1025 Parser step initial extract
1362 Parser step initial extract
@@ -1064,8 +1401,24 @@ takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
1065data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
1402data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
1067−-- | See 'Streamly.Internal.Data.Parser.wordBy'.
1404+-- Note we can also get words using something like:
1405+-- sepBy FL.toList (takeWhile (not . p) Fold.toList) (dropWhile p)
1407+-- But that won't be as efficient and ergonomic.
1409+-- | Like 'splitOn' but strips leading, trailing, and repeated separators.
1410+-- Therefore, @".a..b."@ having '.' as the separator would be parsed as
1411+-- @["a","b"]@. In other words, its like parsing words from whitespace
1414+-- * Stops - when it finds a word separator after a non-word element
1417+-- >>> wordBy = Parser.wordFramedBy (const False) (const False) (const False)
1420+-- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)
1070{-# INLINE wordBy #-}
1423{-# INLINE wordBy #-}
1071wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
1424wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
@@ -1114,7 +1467,24 @@ data WordFramedState s b =
1114 | WordFramedEsc !s !Int
1467 | WordFramedEsc !s !Int
1115 | WordFramedSkipPost !b
1468 | WordFramedSkipPost !b
1470+-- | Like 'wordBy' but treats anything inside a pair of quotes as a single
1471+-- word, the quotes can be escaped by an escape character. Recursive quotes
1472+-- are possible if quote begin and end characters are different, quotes must be
1473+-- balanced. Outermost quotes are stripped.
1475+-- >>> braces = Parser.wordFramedBy (== '\\') (== '{') (== '}') isSpace Fold.toList
1476+-- >>> Stream.parse braces $ Stream.fromList "{ab} cd"
1478+-- >>> Stream.parse braces $ Stream.fromList "{ab}{cd}"
1480+-- >>> Stream.parse braces $ Stream.fromList "a{b} cd"
1482+-- >>> Stream.parse braces $ Stream.fromList "a{{b}} cd"
1117−-- | See 'Streamly.Internal.Data.Parser.wordFramedBy'
1485+-- >>> quotes = Parser.wordFramedBy (== '\\') (== '"') (== '"') isSpace Fold.toList
1486+-- >>> Stream.parse quotes $ Stream.fromList "\"a\"\"b\""
1119{-# INLINE wordFramedBy #-}
1489{-# INLINE wordFramedBy #-}
1120wordFramedBy :: Monad m =>
1490wordFramedBy :: Monad m =>
@@ -1206,6 +1576,26 @@ data WordQuotedState s b a =
1206 | WordQuotedEsc !s !Int a
1576 | WordQuotedEsc !s !Int a
1207 | WordQuotedSkipPost !b
1577 | WordQuotedSkipPost !b
1579+-- | Like 'wordFramedBy' but the closing quote is determined by the opening
1580+-- quote. The first quote begin starts a quote that is closed by its
1581+-- corresponding closing quote.
1583+-- 'wordFramedBy' and 'wordQuotedBy' both allow multiple quote characters based
1584+-- on the predicates but 'wordQuotedBy' always fixes the quote at the first
1585+-- occurrence and then it is closed only by the corresponding closing quote.
1586+-- Therefore, other quoting characters can be embedded inside it as normal
1587+-- characters. On the other hand, 'wordFramedBy' would close the quote as soon
1588+-- as it encounters any of the closing quotes.
1590+-- >>> q = (`elem` ['"', '\''])
1591+-- >>> p kQ = Parser.wordQuotedBy kQ (== '\\') q q id isSpace Fold.toList
1593+-- >>> Stream.parse (p False) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
1594+-- Right "ab'c;d\"ef"
1596+-- >>> Stream.parse (p True) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
1597+-- Right "a\"b'c\";'d\"e'f"
1209{-# INLINE wordQuotedBy #-}
1599{-# INLINE wordQuotedBy #-}
1210wordQuotedBy :: (Monad m, Eq a) =>
1600wordQuotedBy :: (Monad m, Eq a) =>
1211 Bool -- ^ keep the quotes in the output
1601 Bool -- ^ keep the quotes in the output
@@ -1313,7 +1703,31 @@ data GroupByState a s
1313 = GroupByInit !s
1703 = GroupByInit !s
1314 | GroupByGrouping !a !s
1704 | GroupByGrouping !a !s
1316−-- | See 'Streamly.Internal.Data.Parser.groupBy'.
1706+-- | Given an input stream @[a,b,c,...]@ and a comparison function @cmp@, the
1707+-- parser assigns the element @a@ to the first group, then if @a \`cmp` b@ is
1708+-- 'True' @b@ is also assigned to the same group. If @a \`cmp` c@ is 'True'
1709+-- then @c@ is also assigned to the same group and so on. When the comparison
1710+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
1711+-- the result of the fold is the result of the parser.
1713+-- * Stops - when the comparison fails.
1718+-- Stream.fold Fold.toList
1719+-- . Stream.parseMany (Parser.groupBy eq Fold.toList)
1720+-- . Stream.fromList
1723+-- >>> runGroupsBy (<) []
1726+-- >>> runGroupsBy (<) [1]
1729+-- >>> runGroupsBy (<) [3, 5, 4, 1, 2, 0]
1730+-- [Right [3,5,4],Right [1,2],Right [0]]
1318{-# INLINE groupBy #-}
1732{-# INLINE groupBy #-}
1319groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
1733groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
@@ -1345,7 +1759,35 @@ groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
1345 extract (GroupByInit s) = fmap (Done 0) $ fextract s
1759 extract (GroupByInit s) = fmap (Done 0) $ fextract s
1346 extract (GroupByGrouping _ s) = fmap (Done 0) $ fextract s
1760 extract (GroupByGrouping _ s) = fmap (Done 0) $ fextract s
1762+-- | Unlike 'groupBy' this combinator performs a rolling comparison of two
1763+-- successive elements in the input stream. Assuming the input stream
1764+-- is @[a,b,c,...]@ and the comparison function is @cmp@, the parser
1765+-- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is
1766+-- 'True' @b@ is also assigned to the same group. If @b \`cmp` c@ is 'True'
1767+-- then @c@ is also assigned to the same group and so on. When the comparison
1768+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
1769+-- the result of the fold is the result of the parser.
1771+-- * Stops - when the comparison fails.
1775+-- runGroupsByRolling eq =
1776+-- Stream.fold Fold.toList
1348−-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
1777+-- . Stream.parseMany (Parser.groupByRolling eq Fold.toList)
1778+-- . Stream.fromList
1781+-- >>> runGroupsByRolling (<) []
1784+-- >>> runGroupsByRolling (<) [1]
1787+-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]
1788+-- [Right [3,5],Right [4],Right [1,2],Right [0]]
1350{-# INLINE groupByRolling #-}
1792{-# INLINE groupByRolling #-}
1351groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
1793groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
@@ -1384,6 +1826,16 @@ data GroupByStatePair a s1 s2
1384 | GroupByGroupingPairL !a !s1 !s2
1826 | GroupByGroupingPairL !a !s1 !s2
1385 | GroupByGroupingPairR !a !s1 !s2
1827 | GroupByGroupingPairR !a !s1 !s2
1829+-- | Like 'groupByRolling', but if the predicate is 'True' then collects using
1830+-- the first fold as long as the predicate holds 'True', if the predicate is
1831+-- 'False' collects using the second fold as long as it remains 'False'.
1832+-- Returns 'Left' for the first case and 'Right' for the second case.
1834+-- For example, if we want to detect sorted sequences in a stream, both
1835+-- ascending and descending cases we can use 'groupByRollingEither (<=)
1836+-- Fold.toList Fold.toList'.
1387{-# INLINE groupByRollingEither #-}
1839{-# INLINE groupByRollingEither #-}
1388groupByRollingEither :: Monad m =>
1840groupByRollingEither :: Monad m =>
1389 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
1841 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
@@ -1468,13 +1920,26 @@ groupByRollingEither
1468-- XXX use an Unfold instead of a list?
1920-- XXX use an Unfold instead of a list?
1469-- XXX custom combinators for matching list, array and stream?
1921-- XXX custom combinators for matching list, array and stream?
1470-- XXX rename to listBy?
1922-- XXX rename to listBy?
1924+-- | Match the given sequence of elements using the given comparison function.
1925+-- Returns the original sequence if successful.
1472−-- | See 'Streamly.Internal.Data.Parser.streamEqBy'.
1927+-- Definition:
1474−-- /Pre-release/
1929+-- >>> listEqBy cmp xs = Parser.streamEqBy cmp (Stream.fromList xs) *> Parser.fromPure xs
1933+-- >>> Stream.parse (Parser.listEqBy (==) "string") $ Stream.fromList "string"
1936+-- >>> Stream.parse (Parser.listEqBy (==) "mismatch") $ Stream.fromList "match"
1937+-- Left (ParseError "streamEqBy: mismtach occurred")
1476{-# INLINE listEqBy #-}
1939{-# INLINE listEqBy #-}
1477listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]
1940listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]
1941+listEqBy cmp xs = streamEqBy cmp (D.fromList xs) *> fromPure xs
1478listEqBy cmp str = Parser step initial extract
1943listEqBy cmp str = Parser step initial extract
@@ -1501,8 +1966,13 @@ listEqBy cmp str = Parser step initial extract
1502 $ "listEqBy: end of input, yet to match "
1967 $ "listEqBy: end of input, yet to match "
1503 ++ show (length xs) ++ " elements"
1968 ++ show (length xs) ++ " elements"
1505−-- | Like 'listEqBy' but uses a stream instead of a list
1971+-- | Like 'listEqBy' but uses a stream instead of a list and does not return
1974+-- See also: "Streamly.Data.Stream.streamEqBy"
1506{-# INLINE streamEqBy #-}
1976{-# INLINE streamEqBy #-}
1507streamEqBy :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()
1977streamEqBy :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()
1508streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
1978streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
@@ -1540,6 +2010,39 @@ streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
1541 extract _ = return $ Error "streamEqBy: end of input"
2011 extract _ = return $ Error "streamEqBy: end of input"
2013+-- Rename to "list".
2014+-- | Match the input sequence with the supplied list and return it if
2017+-- >>> listEq = Parser.listEqBy (==)
2019+{-# INLINE listEq #-}
2020+listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]
2021+listEq = listEqBy (==)
2023+-- | Match if the input stream is a subsequence of the argument stream i.e. all
2024+-- the elements of the input stream occur, in order, in the argument stream.
2025+-- The elements do not have to occur consecutively. A sequence is considered a
2026+-- subsequence of itself.
2027+{-# INLINE subsequenceBy #-}
2028+subsequenceBy :: -- Monad m =>
2029+ (a -> a -> Bool) -> Stream m a -> Parser a m ()
2030+subsequenceBy = undefined
2033+-- Should go in Data.Parser.Regex in streamly package so that it can depend on
2035+{-# INLINE regexPosix #-}
2036+regexPosix :: -- Monad m =>
2037+ Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
2038+regexPosix = undefined
2040+{-# INLINE regexPCRE #-}
2041+regexPCRE :: -- Monad m =>
2042+ Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
2043+regexPCRE = undefined
1543-------------------------------------------------------------------------------
2046-------------------------------------------------------------------------------
1544-- Transformations on input
2047-- Transformations on input
1545-------------------------------------------------------------------------------
2048-------------------------------------------------------------------------------
@@ -1548,7 +2051,7 @@ streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
1548-- parser can always return a Continue in initial when we feed the fold's
2051-- parser can always return a Continue in initial when we feed the fold's
1549-- initial result to it. We can work this around for postscan by introducing an
2052-- initial result to it. We can work this around for postscan by introducing an
1550-- initial state and calling "initial" only on the first input.
2053-- initial state and calling "initial" only on the first input.
1552-- | Stateful scan on the input of a parser using a Fold.
2055-- | Stateful scan on the input of a parser using a Fold.
1554-- /Unimplemented/
2057-- /Unimplemented/
@@ -1709,7 +2212,16 @@ spanByRolling eq f1 f2 =
1709-- nested parsers
2212-- nested parsers
1710-------------------------------------------------------------------------------
2213-------------------------------------------------------------------------------
1712−-- | See 'Streamly.Internal.Data.Parser.takeP'.
2215+-- | Takes at-most @n@ input elements.
2217+-- * Stops - when the collecting parser stops.
2218+-- * Fails - when the collecting parser fails.
2220+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
2223+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 5 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
2224+-- Left (ParseError "takeEQ: Expecting exactly 5 elements, input terminated on 4")
1714-- /Internal/
2226-- /Internal/
1715{-# INLINE takeP #-}
2227{-# INLINE takeP #-}
@@ -1783,9 +2295,7 @@ takeP lim (Parser pstep pinitial pextract) = Parser step initial extract
1783 Error err -> IError err
2295 Error err -> IError err
1784 _ -> error "Bug: takeP invalid state in initial"
2296 _ -> error "Bug: takeP invalid state in initial"
1786−-- | See 'Streamly.Internal.Data.Parser.lookahead'.
2298+-- | Run a parser without consuming the input.
1790{-# INLINE lookAhead #-}
2300{-# INLINE lookAhead #-}
1791lookAhead :: Monad m => Parser a m b -> Parser a m b
2301lookAhead :: Monad m => Parser a m b -> Parser a m b
@@ -1822,14 +2332,32 @@ lookAhead (Parser step1 initial1 _) = Parser step initial extract
1822-------------------------------------------------------------------------------
2332-------------------------------------------------------------------------------
1823-- Interleaving
2333-- Interleaving
1824-------------------------------------------------------------------------------
2334-------------------------------------------------------------------------------
2336+-- To deinterleave we can chain two parsers one behind the other. The input is
2337+-- given to the first parser and the input definitively rejected by the first
2338+-- parser is given to the second parser.
2340+-- We can either have the parsers themselves buffer the input or use the shared
2341+-- global buffer to hold it until none of the parsers need it. When the first
2342+-- parser returns Skip (i.e. rewind) we let the second parser consume the
2343+-- rejected input and when it is done we move the cursor forward to the first
2344+-- parser again. This will require a "move forward" command as well.
2346+-- To implement grep we can use three parsers, one to find the pattern, one
2347+-- to store the context behind the pattern and one to store the context in
2348+-- front of the pattern. When a match occurs we need to emit the accumulator of
2349+-- all the three parsers. One parser can count the line numbers to provide the
2350+-- line number info.
1826data DeintercalateState fs sp ss =
2352data DeintercalateState fs sp ss =
1827 DeintercalateL !fs !sp
2353 DeintercalateL !fs !sp
1828 | DeintercalateR !fs !ss !Bool
2354 | DeintercalateR !fs !ss !Bool
1830−-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
2356+-- XXX rename this to intercalate
1832−-- /Internal/
2358+-- | Apply two parsers alternately to an input stream. The input stream is
2359+-- considered an interleaving of two patterns. The two parsers represent the
1834{-# INLINE deintercalate #-}
2362{-# INLINE deintercalate #-}
1835deintercalate :: Monad m =>
2363deintercalate :: Monad m =>
@@ -1926,8 +2454,13 @@ data SepByState fs sp ss =
1926 SepByInit !fs !sp
2454 SepByInit !fs !sp
1927 | SepBySeparator !fs !ss !Bool
2455 | SepBySeparator !fs !ss !Bool
1929−-- This is a special case of deintercalate and can be easily implemented in
2457+-- | Run the content parser first, when it is done, the separator parser is
1930−-- terms of deintercalate.
2458+-- run, when it is done content parser is run again and so on. If none of the
2459+-- parsers consumes an input then parser returns a failure.
2461+-- >>> sepBy p1 p2 sink = Parser.deintercalate p1 p2 (Fold.catLefts sink)
2462+-- >>> sepBy content sep sink = Parser.sepBy1 content sep sink <|> return mempty
1931{-# INLINE sepBy #-}
2464{-# INLINE sepBy #-}
1932sepBy :: Monad m =>
2465sepBy :: Monad m =>
1933 Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
2466 Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
@@ -2000,11 +2533,49 @@ sepBy
2001 extract (SepBySeparator fs _ _) = fmap (Done 0) $ fextract fs
2534 extract (SepBySeparator fs _ _) = fmap (Done 0) $ fextract fs
2536+-- XXX This can be implemented using refold, parse one and then continue
2537+-- collecting the rest in that.
2539+-- | Parse items separated by a separator parsed by the supplied parser. At
2540+-- least one item must be present for the parser to succeed.
2542+-- Note that this can go in infinite loop if both the parsers fail on some
2543+-- input. Detection of that would make the implementation more complex.
2545+{-# INLINE sepBy1 #-}
2546+sepBy1 :: Monad m =>
2547+ Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
2548+sepBy1 p sep sink = do
2550+ f <- fromEffect $ FL.reduce sink
2551+ f1 <- fromEffect $ FL.snoc f x
2554+-------------------------------------------------------------------------------
2555+-- Interleaving a collection of parsers
2556+-------------------------------------------------------------------------------
2558+-- | Apply a collection of parsers to an input stream in a round robin fashion.
2559+-- Each parser is applied until it stops and then we repeat starting with the
2560+-- the first parser again.
2564+{-# INLINE roundRobin #-}
2565+roundRobin :: -- (Foldable t, Monad m) =>
2566+ t (Parser a m b) -> Fold m b c -> Parser a m c
2567+roundRobin _ps _f = undefined
2003-------------------------------------------------------------------------------
2569-------------------------------------------------------------------------------
2004-- Sequential Collection
2570-- Sequential Collection
2005-------------------------------------------------------------------------------
2571-------------------------------------------------------------------------------
2573+-- | @sequence f p@ collects sequential parses of parsers in a
2574+-- serial stream @p@ using the fold @f@. Fails if the input ends or any
2575+-- of the parsers fail.
2007−-- | See 'Streamly.Internal.Data.Parser.sequence'.
2008{-# INLINE sequence #-}
2579{-# INLINE sequence #-}
2009sequence :: Monad m =>
2580sequence :: Monad m =>
2010 D.Stream m (Parser a m b) -> Fold m b c -> Parser a m c
2581 D.Stream m (Parser a m b) -> Fold m b c -> Parser a m c
@@ -2089,7 +2660,10 @@ sequence (D.Stream sstep sstate) (Fold fstep finitial fextract) =
2089-------------------------------------------------------------------------------
2660-------------------------------------------------------------------------------
2092−-- | See 'Streamly.Internal.Data.Parser.choice'.
2663+-- | @choice parsers@ applies the @parsers@ in order and returns the first
2664+-- successful parse.
2666+-- This is same as 'asum' but more efficient.
2094-- /Broken/
2668-- /Broken/
@@ -2102,23 +2676,45 @@ choice = foldl1 shortest
2102-- Sequential Repetition
2676-- Sequential Repetition
2103-------------------------------------------------------------------------------
2677-------------------------------------------------------------------------------
2679+-- | Like 'many' but uses a 'Parser' instead of a 'Fold' to collect the
2680+-- results. Parsing stops or fails if the collecting parser stops or fails.
2105{-# INLINE manyP #-}
2684{-# INLINE manyP #-}
2106manyP :: -- MonadCatch m =>
2685manyP :: -- MonadCatch m =>
2107 Parser a m b -> Parser b m c -> Parser a m c
2686 Parser a m b -> Parser b m c -> Parser a m c
2108manyP _p _f = undefined
2687manyP _p _f = undefined
2110−-- | See 'Streamly.Internal.Data.Parser.many'.
2689+-- | Collect zero or more parses. Apply the supplied parser repeatedly on the
2690+-- input stream and push the parse results to a downstream fold.
2112−-- /Pre-release/
2692+-- Stops: when the downstream fold stops or the parser fails.
2693+-- Fails: never, produces zero or more results.
2695+-- >>> many = Parser.countBetween 0 maxBound
2697+-- Compare with 'Control.Applicative.many'.
2114{-# INLINE many #-}
2699{-# INLINE many #-}
2115many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
2700many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
2116many = splitMany
2701many = splitMany
2117-- many = countBetween 0 maxBound
2702-- many = countBetween 0 maxBound
2119−-- | See 'Streamly.Internal.Data.Parser.some'.
2704+-- Note: many1 would perhaps be a better name for this and consistent with
2705+-- other names like takeWhile1. But we retain the name "some" for
2708+-- | Collect one or more parses. Apply the supplied parser repeatedly on the
2709+-- input stream and push the parse results to a downstream fold.
2121−-- /Pre-release/
2711+-- Stops: when the downstream fold stops or the parser fails.
2712+-- Fails: if it stops without producing a single result.
2714+-- >>> some p f = Parser.manyP p (Parser.takeGE 1 f)
2715+-- >>> some = Parser.countBetween 1 maxBound
2717+-- Compare with 'Control.Applicative.some'.
2123{-# INLINE some #-}
2719{-# INLINE some #-}
2124some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
2720some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
@@ -2126,7 +2722,11 @@ some = splitSome
2126-- some p f = manyP p (takeGE 1 f)
2722-- some p f = manyP p (takeGE 1 f)
2127-- some = countBetween 1 maxBound
2723-- some = countBetween 1 maxBound
2129−-- | See 'Streamly.Internal.Data.Parser.countBetween'.
2725+-- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of
2726+-- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if
2727+-- the input ends or the parser fails before @m@ results are collected.
2729+-- >>> countBetween m n p f = Parser.manyP p (Parser.takeBetween m n f)
2131-- /Unimplemented/
2731-- /Unimplemented/
@@ -2136,7 +2736,12 @@ countBetween :: -- MonadCatch m =>
2136countBetween _m _n _p = undefined
2736countBetween _m _n _p = undefined
2137-- countBetween m n p f = manyP p (takeBetween m n f)
2737-- countBetween m n p f = manyP p (takeBetween m n f)
2139−-- | See 'Streamly.Internal.Data.Parser.count'.
2739+-- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using
2740+-- the fold @f@. Fails if the input ends or the parser fails before @n@
2741+-- results are collected.
2743+-- >>> count n = Parser.countBetween n n
2744+-- >>> count n p f = Parser.manyP p (Parser.takeEQ n f)
2141-- /Unimplemented/
2746-- /Unimplemented/
@@ -2146,13 +2751,34 @@ count :: -- MonadCatch m =>
2146count n = countBetween n n
2751count n = countBetween n n
2147-- count n p f = manyP p (takeEQ n f)
2752-- count n p f = manyP p (takeEQ n f)
2754+-- | Like 'manyTill' but uses a 'Parser' to collect the results instead of a
2755+-- 'Fold'. Parsing stops or fails if the collecting parser stops or fails.
2757+-- We can implemnent parsers like the following using 'manyTillP':
2760+-- countBetweenTill m n f p = manyTillP (takeBetween m n f) p
2765+{-# INLINE manyTillP #-}
2766+manyTillP :: -- Monad m =>
2767+ Parser a m b -> Parser a m x -> Parser b m c -> Parser a m c
2768+manyTillP _p1 _p2 _f = undefined
2769+ -- D.toParserK $ D.manyTillP (D.fromParserK p1) (D.fromParserK p2) f
2149data ManyTillState fs sr sl
2771data ManyTillState fs sr sl
2150 = ManyTillR Int fs sr
2772 = ManyTillR Int fs sr
2151 | ManyTillL Int fs sl
2773 | ManyTillL Int fs sl
2153−-- | See 'Streamly.Internal.Data.Parser.manyTill'.
2775+-- | @manyTill chunking test f@ tries the parser @test@ on the input, if @test@
2776+-- fails it backtracks and tries @chunking@, after @chunking@ succeeds @test@ is
2777+-- tried again and so on. The parser stops when @test@ succeeds. The output of
2778+-- @test@ is discarded and the output of @chunking@ is accumulated by the
2779+-- supplied fold. The parser fails if @chunking@ fails.
2155−-- /Pre-release/
2781+-- Stops when the fold @f@ stops.
2157{-# INLINE manyTill #-}
2783{-# INLINE manyTill #-}
2158manyTill :: Monad m
2784manyTill :: Monad m
@@ -2245,3 +2871,53 @@ manyTill (Parser stepL initialL extractL)
2245 return $ Continue n (ManyTillL 0 fs s)
2871 return $ Continue n (ManyTillL 0 fs s)
2246 Partial _ _ -> error "Partial in extract"
2872 Partial _ _ -> error "Partial in extract"
2247 extract (ManyTillR _ fs _) = fmap (Done 0) $ fextract fs
2873 extract (ManyTillR _ fs _) = fmap (Done 0) $ fextract fs
2875+-- | @manyThen f collect recover@ repeats the parser @collect@ on the input and
2876+-- collects the output in the supplied fold. If the the parser @collect@ fails,
2877+-- parser @recover@ is run until it stops and then we start repeating the
2878+-- parser @collect@ again. The parser fails if the recovery parser fails.
2880+-- For example, this can be used to find a key frame in a video stream after an
2885+{-# INLINE manyThen #-}
2886+manyThen :: -- (Foldable t, Monad m) =>
2887+ Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
2888+manyThen _parser _recover _f = undefined
2890+-------------------------------------------------------------------------------
2891+-- Repeated Alternatives
2892+-------------------------------------------------------------------------------
2894+-- | Keep trying a parser up to a maximum of @n@ failures. When the parser
2895+-- fails the input consumed till now is dropped and the new instance is tried
2896+-- on the fresh input.
2900+{-# INLINE retryMaxTotal #-}
2901+retryMaxTotal :: -- (Monad m) =>
2902+ Int -> Parser a m b -> Fold m b c -> Parser a m c
2903+retryMaxTotal _n _p _f = undefined
2905+-- | Like 'retryMaxTotal' but aborts after @n@ successive failures.
2909+{-# INLINE retryMaxSuccessive #-}
2910+retryMaxSuccessive :: -- (Monad m) =>
2911+ Int -> Parser a m b -> Fold m b c -> Parser a m c
2912+retryMaxSuccessive _n _p _f = undefined
2914+-- | Keep trying a parser until it succeeds. When the parser fails the input
2915+-- consumed till now is dropped and the new instance is tried on the fresh
2920+{-# INLINE retry #-}
2921+retry :: -- (Monad m) =>
2922+ Parser a m b -> Parser a m b
2923+retry _p = undefined