@@ -1,5 +1,3 @@
1−#include "inline.hs"
2−
31-- |
42-- Module : Streamly.Internal.Data.Parser.ParserD
53-- Copyright : (c) 2020 Composewell Technologies
@@ -8,7 +6,42 @@
86-- Stability : experimental
97-- Portability : GHC
108--
11−-- Direct style parser implementation with stream fusion.
9+-- Fast backtracking parsers with stream fusion and native streaming
10+-- capability.
11+--
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:
18+--
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.
25+--
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.
30+--
31+-- = Errors
32+--
33+-- Failing parsers in this module throw the 'D.ParseError' exception.
34+--
35+-- = Naming
36+--
37+-- As far as possible, try that the names of the combinators in this module are
38+-- consistent with:
39+--
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>
1245
1346module Streamly.Internal.Data.Parser.ParserD
1447 (
@@ -16,7 +49,6 @@ module Streamly.Internal.Data.Parser.ParserD
1649 , ParseError (..)
1750 , Step (..)
1851 , Initial (..)
19− , rmapM
2052
2153 -- * Conversion to/from ParserK
2254 , fromParserK
@@ -40,17 +72,26 @@ module Streamly.Internal.Data.Parser.ParserD
4072 , postscan
4173 , filter
4274
75+ -- * Map on output
76+ , rmapM
77+
4378 -- * Element parsers
4479 , peek
80+
81+ -- All of these can be expressed in terms of either
82+ , one
83+ , oneEq
84+ , oneNotEq
85+ , oneOf
86+ , noneOf
4587 , eof
4688 , satisfy
47− , next
4889 , maybe
4990 , either
5091
51− -- * Sequence parsers
92+ -- * Sequence parsers (tokenizers)
5293 --
53− -- Parsers chained in series, if one parser terminates the composition
94+ -- | Parsers chained in series, if one parser terminates the composition
5495 -- terminates. Currently we are using folds to collect the output of the
5596 -- parsers but we can use Parsers instead of folds to make the composition
5697 -- more powerful. For example, we can do:
@@ -58,41 +99,54 @@ module Streamly.Internal.Data.Parser.ParserD
5899 -- takeEndByOrMax cond n p = takeEndBy cond (take n p)
59100 -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)
60101 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
102+ , lookAhead
61103
62− -- Grab a sequence of input elements without inspecting them
104+ -- ** By length
105+ -- | Grab a sequence of input elements without inspecting them
63106 , takeBetween
64− -- , take -- take -- takeBetween 0 n
107+ -- , take -- takeBetween 0 n
65108 , takeEQ -- takeBetween n n
66109 , takeGE -- takeBetween n maxBound
67110 -- , takeGE1 -- take1 -- takeBetween 1 n
68111 , takeP
69112
70113 -- Grab a sequence of input elements by inspecting them
71− , lookAhead
114+ -- ** Exact match
115+ , listEq
116+ , listEqBy
117+ , streamEqBy
118+ , subsequenceBy
119+
120+ -- ** By predicate
72121 , takeWhile
73122 , takeWhileP
74123 , takeWhile1
124+ , dropWhile
75125
76− -- Separators
126+ -- ** Separators
77127 , takeEndBy
78128 , takeEndBy_
79129 , takeEndByEsc
130+ -- , takeEndByEsc_
80131 , takeStartBy
132+ , takeStartBy_
133+ , takeEitherSepBy
134+ , wordBy
135+
136+ -- ** By comparing
137+ , groupBy
138+ , groupByRolling
139+ , groupByRollingEither
140+
141+ -- ** Framing
142+ -- , takeFramedBy
81143 , takeFramedBy_
82144 , takeFramedByEsc_
83145 , takeFramedByGeneric
84−
85− -- Words and grouping
86− , wordBy
87146 , wordFramedBy
88147 , wordQuotedBy
89− , groupBy
90− , groupByRolling
91− , groupByRollingEither
92148
93149 -- Matching strings
94− , listEqBy
95− , streamEqBy
96150 -- , prefixOf -- match any prefix of a given string
97151 -- , suffixOf -- match any suffix of a given string
98152 -- , infixOf -- match any substring of a given string
@@ -121,6 +175,25 @@ module Streamly.Internal.Data.Parser.ParserD
121175 -- Use two folds, run a primary parser, its rejected values go to the
122176 -- secondary parser.
123177 , deintercalate
178+ -- , deintercalatePrefix
179+ -- , deintercalateSuffix
180+
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.
186+ --
187+ -- We can instead implement these as special cases of deintercalate.
188+ --
189+ -- @
190+ -- , endBy
191+ -- , sepEndBy
192+ -- , beginBy
193+ -- , sepBeginBy
194+ -- , sepAroundBy
195+ -- @
196+ , sepBy1
124197 , sepBy
125198
126199 -- ** Sequential Alternative
@@ -132,6 +205,7 @@ module Streamly.Internal.Data.Parser.ParserD
132205 , longest
133206 -- , fastest
134207 -}
208+
135209 -- * N-ary Combinators
136210 -- ** Sequential Collection
137211 , sequence
@@ -142,25 +216,12 @@ module Streamly.Internal.Data.Parser.ParserD
142216 , countBetween
143217 -- , countBetweenTill
144218
145− , many
146219 , manyP
220+ , many
147221 , some
222+ , manyTillP
148223 , manyTill
149−
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.
155− --
156− -- We can instead implement these as special cases of deintercalate.
157− --
158− -- , endBy
159− -- , sepBy
160− -- , sepEndBy
161− -- , beginBy
162− -- , sepBeginBy
163− -- , sepAroundBy
224+ , manyThen
164225
165226 -- -- * Distribution
166227 --
@@ -171,24 +232,32 @@ module Streamly.Internal.Data.Parser.ParserD
171232 -- -- ** Distribute to collection
172233 -- -- ** Distribute to repetition
173234
174− -- -- ** Interleaved collection
175− -- Round robin
176− -- Priority based
235+ -- ** Interleaved collection
236+ -- |
237+ --
238+ -- 1. Round robin
239+ -- 2. Priority based
240+ , roundRobin
241+
177242 -- -- ** Interleaved repetition
178243 -- repeat one parser and when it fails run an error recovery parser
179244 -- e.g. to find a key frame in the stream after an error
180245
181246 -- ** Collection of Alternatives
247+ -- | Unimplemented
248+ --
249+ -- @
182250 -- , shortestN
183251 -- , longestN
184252 -- , fastestN -- first N successful in time
185253 -- , choiceN -- first N successful in position
254+ -- @
186255 -- , choice -- first successful in position
187256
188− -- -- ** Repeated Alternatives
189− -- , retryMax -- try N times
190− -- , retryUntil -- try until successful
191− -- , retryUntilN -- try until successful n times
257+ -- ** Repeated Alternatives
258+ , retryMaxTotal
259+ , retryMaxSuccessive
260+ , retry
192261
193262 -- ** Zipping Input
194263 , zipWithM
@@ -196,9 +265,13 @@ module Streamly.Internal.Data.Parser.ParserD
196265 , indexed
197266 , makeIndexFilter
198267 , sampleFromthen
268+
269+ -- * Deprecated
270+ , next
199271 )
200272where
201273
274+#include "inline.hs"
202275#include "assert.hs"
203276
204277import Control.Monad (when)
@@ -209,14 +282,16 @@ import Streamly.Internal.Data.SVar.Type (defState)
209282import Streamly.Internal.Data.Either.Strict (Either'(..))
210283import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
211284import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
285+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
212286
287+import qualified Data.Foldable as Foldable
213288import qualified Streamly.Internal.Data.Fold.Type as FL
214289import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
215290import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
216291
217292import Prelude hiding
218293 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span
219− , zip, filter)
294+ , zip, filter, dropWhile)
220295-- import Streamly.Internal.Data.Parser.ParserD.Tee
221296import Streamly.Internal.Data.Parser.ParserD.Type
222297
@@ -224,19 +299,27 @@ import Streamly.Internal.Data.Parser.ParserD.Type
224299-- $setup
225300-- >>> :m
226301-- >>> import Prelude hiding ()
302+-- >>> import Control.Applicative
303+-- >>> import Data.Char (isSpace)
304+-- >>> import qualified Data.Foldable as Foldable
227305-- >>> import qualified Data.Maybe as Maybe
228306-- >>> import qualified Streamly.Data.Stream as Stream
229307-- >>> 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
231309-- >>> import qualified Streamly.Internal.Data.Parser as Parser
232310
233311-------------------------------------------------------------------------------
234312-- Downgrade a parser to a Fold
235313-------------------------------------------------------------------------------
236314
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.
238317--
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
320+-- recover.
321+--
322+-- /Pre-release/
240323--
241324{-# INLINE toFold #-}
242325toFold :: Monad m => Parser a m b -> Fold m a b
@@ -280,10 +363,8 @@ toFold (Parser pstep pinitial pextract) = Fold step initial extract
280363-------------------------------------------------------------------------------
281364-- Upgrade folds to parses
282365-------------------------------------------------------------------------------
283−--
284−-- | See 'Streamly.Internal.Data.Parser.fromFold'.
285−--
286−-- /Pre-release/
366+
367+-- | Make a 'Parser' from a 'Fold'.
287368--
288369{-# INLINE fromFold #-}
289370fromFold :: Monad m => Fold m a b -> Parser a m b
@@ -307,7 +388,9 @@ fromFold (Fold fstep finitial fextract) = Parser step initial extract
307388
308389 extract = fmap (Done 0) . fextract
309390
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
393+-- returns Nothing.
311394--
312395-- /Pre-release/
313396--
@@ -348,9 +431,15 @@ fromFoldMaybe errMsg (Fold fstep finitial fextract) =
348431-- Failing Parsers
349432-------------------------------------------------------------------------------
350433
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.
352436--
353−-- /Pre-release/
437+-- >>> Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]
438+-- Right (1,1)
439+--
440+-- @
441+-- peek = lookAhead (satisfy True)
442+-- @
354443--
355444{-# INLINE peek #-}
356445peek :: Monad m => Parser a m a
@@ -364,9 +453,10 @@ peek = Parser step initial extract
364453
365454 extract () = return $ Error "peek: end of input"
366455
367−-- | See 'Streamly.Internal.Data.Parser.eof'.
456+-- | Succeeds if we are at the end of input, fails otherwise.
368457--
369−-- /Pre-release/
458+-- >>> Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]
459+-- Right (1,())
370460--
371461{-# INLINE eof #-}
372462eof :: Monad m => Parser a m ()
@@ -380,10 +470,12 @@ eof = Parser step initial extract
380470
381471 extract () = return $ Done 0 ()
382472
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'.
384475--
385476-- /Pre-release/
386477--
478+{-# DEPRECATED next "Please use \"fromFold Fold.one\" instead" #-}
387479{-# INLINE next #-}
388480next :: Monad m => Parser a m (Maybe a)
389481next = Parser step initial extract
@@ -396,7 +488,9 @@ next = Parser step initial extract
396488
397489 extract () = pure $ Done 0 Nothing
398490
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.
400494--
401495-- /Pre-release/
402496--
@@ -415,11 +509,15 @@ either f = Parser step initial extract
415509
416510 extract () = return $ Error "end of input"
417511
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'
514+-- value.
419515--
420516-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
421517-- >>> maybe f = Parser.either (toEither . f)
422518--
519+-- >>> maybe f = Parser.fromFoldMaybe "maybe: predicate failed" (Fold.maybe f)
520+--
423521-- /Pre-release/
424522--
425523{-# INLINE maybe #-}
@@ -438,13 +536,14 @@ maybe parserF = Parser step initial extract
438536
439537 extract () = return $ Error "maybe: end of input"
440538
441−-- | See 'Streamly.Internal.Data.Parser.satisfy'.
539+-- | Returns the next element if it passes the predicate, fails otherwise.
540+--
541+-- >>> Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]
542+-- Right 1
442543--
443544-- >>> toMaybe f x = if f x then Just x else Nothing
444545-- >>> satisfy f = Parser.maybe (toMaybe f)
445546--
446−-- /Pre-release/
447−--
448547{-# INLINE satisfy #-}
449548satisfy :: Monad m => (a -> Bool) -> Parser a m a
450549-- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)
@@ -461,6 +560,67 @@ satisfy predicate = Parser step initial extract
461560
462561 extract () = return $ Error "satisfy: end of input"
463562
563+-- | Consume one element from the head of the stream. Fails if it encounters
564+-- end of input.
565+--
566+-- >>> one = Parser.satisfy $ const True
567+--
568+{-# INLINE one #-}
569+one :: Monad m => Parser a m a
570+one = satisfy $ const True
571+
572+-- Alternate names: "only", "onlyThis".
573+
574+-- | Match a specific element.
575+--
576+-- >>> oneEq x = Parser.satisfy (== x)
577+--
578+{-# INLINE oneEq #-}
579+oneEq :: (Monad m, Eq a) => a -> Parser a m a
580+oneEq x = satisfy (== x)
581+
582+-- Alternate names: "exclude", "notThis".
583+
584+-- | Match anything other than the supplied element.
585+--
586+-- >>> oneNotEq x = Parser.satisfy (/= x)
587+--
588+{-# INLINE oneNotEq #-}
589+oneNotEq :: (Monad m, Eq a) => a -> Parser a m a
590+oneNotEq x = satisfy (/= x)
591+
592+-- | Match any one of the elements in the supplied list.
593+--
594+-- >>> oneOf xs = Parser.satisfy (`Foldable.elem` xs)
595+--
596+-- When performance matters a pattern matching predicate could be more
597+-- efficient than a 'Foldable' datatype:
598+--
599+-- @
600+-- let p x =
601+-- case x of
602+-- 'a' -> True
603+-- 'e' -> True
604+-- _ -> False
605+-- in satisfy p
606+-- @
607+--
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
610+-- search.
611+--
612+{-# INLINE oneOf #-}
613+oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
614+oneOf xs = satisfy (`Foldable.elem` xs)
615+
616+-- | See performance notes in 'oneOf'.
617+--
618+-- >>> noneOf xs = Parser.satisfy (`Foldable.notElem` xs)
619+--
620+{-# INLINE noneOf #-}
621+noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
622+noneOf xs = satisfy (`Foldable.notElem` xs)
623+
464624-------------------------------------------------------------------------------
465625-- Taking elements
466626-------------------------------------------------------------------------------
@@ -469,7 +629,43 @@ satisfy predicate = Parser step initial extract
469629{-# ANN type Tuple'Fused Fuse #-}
470630data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
471631
472−-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
632+-- | @takeBetween m n@ takes a minimum of @m@ and a maximum of @n@ input
633+-- elements and folds them using the supplied fold.
634+--
635+-- Stops after @n@ elements.
636+-- Fails if the stream ends before @m@ elements could be taken.
637+--
638+-- Examples: -
639+--
640+-- @
641+-- >>> :{
642+-- takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)
643+-- where prsr = Parser.takeBetween low high Fold.toList
644+-- :}
645+--
646+-- @
647+--
648+-- >>> takeBetween' 2 4 [1, 2, 3, 4, 5]
649+-- Right [1,2,3,4]
650+--
651+-- >>> takeBetween' 2 4 [1, 2]
652+-- Right [1,2]
653+--
654+-- >>> takeBetween' 2 4 [1]
655+-- Left (ParseError "takeBetween: Expecting alteast 2 elements, got 1")
656+--
657+-- >>> takeBetween' 0 0 [1, 2]
658+-- Right []
659+--
660+-- >>> takeBetween' 0 1 []
661+-- Right []
662+--
663+-- @takeBetween@ is the most general take operation, other take operations can
664+-- be defined in terms of takeBetween. For example:
665+--
666+-- >>> take n = Parser.takeBetween 0 n
667+-- >>> takeEQ n = Parser.takeBetween n n
668+-- >>> takeGE n = Parser.takeBetween n maxBound
473669--
474670-- /Pre-release/
475671--
@@ -546,9 +742,14 @@ takeBetween low high (Fold fstep finitial fextract) =
546742 | i >= low && i <= high = fmap IDone (fextract s)
547743 | otherwise = return $ IError (f i)
548744
549−-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
745+-- | Stops after taking exactly @n@ input elements.
550746--
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.
750+--
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")
552753--
553754{-# INLINE takeEQ #-}
554755takeEQ :: 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
602803 $ "takeEQ: Expecting exactly " ++ show cnt
603804 ++ " elements, input terminated on " ++ show i
604805
605−-- | See 'Streamly.Internal.Data.Parser.takeGE'.
806+-- | Take at least @n@ input elements, but can collect more.
807+--
808+-- * Stops - when the collecting fold stops.
809+-- * Fails - if the stream or the collecting fold ends before producing @n@
810+-- elements.
811+--
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")
814+--
815+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]
816+-- Right [1,0,1,0,1]
606817--
607818-- /Pre-release/
608819--
@@ -659,7 +870,19 @@ takeGE n (Fold fstep finitial fextract) = Parser step initial extract
659870-- Conditional splitting
660871-------------------------------------------------------------------------------
661872
662−-- | See 'Streamly.Internal.Data.Parser.takeWhileP'.
873+-- XXX We should perhaps use only takeWhileP and rename it to takeWhile.
874+
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
877+-- parser stops.
878+--
879+-- Other interesting parsers can be implemented in terms of this parser:
880+--
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)
883+--
884+-- Stops: when the condition fails or the collecting parser stops.
885+-- Fails: when the collecting parser fails.
663886--
664887-- /Pre-release/
665888--
@@ -682,12 +905,26 @@ takeWhileP predicate (Parser pstep pinitial pextract) =
682905 Partial _ _ -> error "Bug: takeWhileP: Partial in extract"
683906 Continue n s1 -> return $ Continue (n + 1) s1
684907
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.
686910--
687−-- /Pre-release/
911+-- * Stops - when the predicate fails or the collecting fold stops.
912+-- * Fails - never.
913+--
914+-- >>> Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]
915+-- Right [0,0]
916+--
917+-- >>> takeWhile cond f = Parser.takeWhileP cond (Parser.fromFold f)
918+--
919+-- We can implement a @breakOn@ using 'takeWhile':
920+--
921+-- @
922+-- breakOn p = takeWhile (not p)
923+-- @
688924--
689925{-# INLINE takeWhile #-}
690926takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
927+-- takeWhile cond f = takeWhileP cond (fromFold f)
691928takeWhile predicate (Fold fstep finitial fextract) =
692929 Parser step initial extract
693930
@@ -711,12 +948,13 @@ takeWhile predicate (Fold fstep finitial fextract) =
711948
712949 extract s = fmap (Done 0) (fextract s)
713950
714−-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
951+-- | Like 'takeWhile' but takes at least one element otherwise fails.
715952--
716−-- /Pre-release/
953+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
717954--
718955{-# INLINE takeWhile1 #-}
719956takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
957+-- takeWhile1 cond f = takeWhileP cond (takeBetween 1 maxBound f)
720958takeWhile1 predicate (Fold fstep finitial fextract) =
721959 Parser step initial extract
722960
@@ -753,6 +991,17 @@ takeWhile1 predicate (Fold fstep finitial fextract) =
753991 extract (Left' _) = return $ Error "takeWhile1: end of input"
754992 extract (Right' s) = fmap (Done 0) (fextract s)
755993
994+-- | Drain the input as long as the predicate succeeds, running the effects and
995+-- discarding the results.
996+--
997+-- This is also called @skipWhile@ in some parsing libraries.
998+--
999+-- >>> dropWhile p = Parser.takeWhile p Fold.drain
1000+--
1001+{-# INLINE dropWhile #-}
1002+dropWhile :: Monad m => (a -> Bool) -> Parser a m ()
1003+dropWhile p = takeWhile p FL.drain
1004+
7561005-------------------------------------------------------------------------------
7571006-- Separators
7581007-------------------------------------------------------------------------------
@@ -760,6 +1009,7 @@ takeWhile1 predicate (Fold fstep finitial fextract) =
7601009data FramedEscState s =
7611010 FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int
7621011
1012+-- XXX We can remove Maybe from esc
7631013{-# INLINE takeFramedByGeneric #-}
7641014takeFramedByGeneric :: Monad m =>
7651015 Maybe (a -> Bool)
@@ -857,12 +1107,26 @@ takeFramedByGeneric esc begin end (Fold fstep finitial fextract) =
8571107 Nothing -> err "takeFramedByGeneric: missing closing frame"
8581108 extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"
8591109
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.
1112+--
1113+-- This can be combined with other parsers to implement other interesting
1114+-- parsers as follows:
1115+--
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)
1118+--
1119+-- >>> takeEndBy = Parser.takeEndByEsc (const False)
1120+--
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.
8611124--
8621125-- /Pre-release/
8631126--
8641127{-# INLINE takeEndBy #-}
8651128takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
1129+-- takeEndBy = takeEndByEsc (const False)
8661130takeEndBy cond (Parser pstep pinitial pextract) =
8671131
8681132 Parser step initial pextract
@@ -877,10 +1141,11 @@ takeEndBy cond (Parser pstep pinitial pextract) =
8771141 then return res
8781142 else extractStep pextract res
8791143
880−-- | See 'Streamly.Internal.Data.Parser.takeEndByEsc'.
881−--
882−-- /Pre-release/
1144+-- | Like 'takeEndBy' but the separator elements can be escaped using an
1145+-- escape char determined by the first predicate. The escape characters are
1146+-- removed.
8831147--
1148+-- /pre-release/
8841149{-# INLINE takeEndByEsc #-}
8851150takeEndByEsc :: Monad m =>
8861151 (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b
@@ -909,12 +1174,18 @@ takeEndByEsc isEsc isSep (Parser pstep pinitial pextract) =
9091174 extract (Right' _) =
9101175 return $ Error "takeEndByEsc: trailing escape"
9111176
912−-- | See 'Streamly.Internal.Data.Parser.takeEndBy_'.
1177+-- | Like 'takeEndBy' but the separator is dropped.
1178+--
1179+-- See also "Streamly.Data.Fold.takeEndBy_".
9131180--
9141181-- /Pre-release/
9151182--
9161183{-# INLINE takeEndBy_ #-}
9171184takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b
1185+{-
1186+takeEndBy_ isEnd p =
1187+ takeFramedByGeneric Nothing Nothing (Just isEnd) (toFold p)
1188+-}
9181189takeEndBy_ cond (Parser pstep pinitial pextract) =
9191190
9201191 Parser step pinitial pextract
@@ -926,11 +1197,39 @@ takeEndBy_ cond (Parser pstep pinitial pextract) =
9261197 then pextract s
9271198 else pstep s a
9281199
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.
1202+--
1203+-- /Unimplemented/
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
1208+
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.
1211+--
1212+-- * Stops - when the predicate succeeds in non-leading position.
1213+-- * Fails - when the predicate fails in the leading position.
1214+--
1215+-- >>> splitWithPrefix p f = Stream.parseMany (Parser.takeStartBy p f)
1216+--
1217+-- Examples: -
1218+--
1219+-- >>> p = Parser.takeStartBy (== ',') Fold.toList
1220+-- >>> leadingComma = Stream.parse p . Stream.fromList
1221+-- >>> leadingComma "a,b"
1222+-- Left (ParseError "takeStartBy: missing frame start")
1223+-- ...
1224+-- >>> leadingComma ",,"
1225+-- Right ","
1226+-- >>> leadingComma ",a,b"
1227+-- Right ",a"
1228+-- >>> leadingComma ""
1229+-- Right ""
9301230--
9311231-- /Pre-release/
9321232--
933−
9341233{-# INLINE takeStartBy #-}
9351234takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
9361235takeStartBy cond (Fold fstep finitial fextract) =
@@ -966,9 +1265,40 @@ takeStartBy cond (Fold fstep finitial fextract) =
9661265 extract (Left' s) = fmap (Done 0) $ fextract s
9671266 extract (Right' s) = fmap (Done 0) $ fextract s
9681267
1268+-- | Like 'takeStartBy' but drops the separator.
1269+--
1270+-- >>> takeStartBy_ isBegin = Parser.takeFramedByGeneric Nothing (Just isBegin) Nothing
1271+--
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
1275+
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.
1279+--
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
1282+-- it is.
1283+--
1284+-- For example,
1285+--
1286+-- >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList
1287+-- >>> Stream.parse p $ Stream.fromList "{hello}"
1288+-- Right "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")
1295+--
1296+-- /Pre-release/
9691297{-# INLINE takeFramedByEsc_ #-}
9701298takeFramedByEsc_ :: Monad m =>
9711299 (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)
9721302takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
9731303
9741304 Parser step initial extract
@@ -1017,9 +1347,16 @@ takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
10171347
10181348data FramedState s = FrameInit !s | FrameGo !s Int
10191349
1350+-- | @takeFramedBy_ isBegin isEnd fold@ parses a token framed by a begin and an
1351+-- end predicate.
1352+--
1353+-- >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)
1354+--
10201355{-# INLINE takeFramedBy_ #-}
10211356takeFramedBy_ :: Monad m =>
10221357 (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)
10231360takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
10241361
10251362 Parser step initial extract
@@ -1064,8 +1401,24 @@ takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
10641401
10651402data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
10661403
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)
1406+--
1407+-- But that won't be as efficient and ergonomic.
1408+
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
1412+-- separated text.
10681413--
1414+-- * Stops - when it finds a word separator after a non-word element
1415+-- * Fails - never.
1416+--
1417+-- >>> wordBy = Parser.wordFramedBy (const False) (const False) (const False)
1418+--
1419+-- @
1420+-- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)
1421+-- @
10691422--
10701423{-# INLINE wordBy #-}
10711424wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
@@ -1114,7 +1467,24 @@ data WordFramedState s b =
11141467 | WordFramedEsc !s !Int
11151468 | WordFramedSkipPost !b
11161469
1117−-- | See 'Streamly.Internal.Data.Parser.wordFramedBy'
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.
1474+--
1475+-- >>> braces = Parser.wordFramedBy (== '\\') (== '{') (== '}') isSpace Fold.toList
1476+-- >>> Stream.parse braces $ Stream.fromList "{ab} cd"
1477+-- Right "ab"
1478+-- >>> Stream.parse braces $ Stream.fromList "{ab}{cd}"
1479+-- Right "abcd"
1480+-- >>> Stream.parse braces $ Stream.fromList "a{b} cd"
1481+-- Right "ab"
1482+-- >>> Stream.parse braces $ Stream.fromList "a{{b}} cd"
1483+-- Right "a{b}"
1484+--
1485+-- >>> quotes = Parser.wordFramedBy (== '\\') (== '"') (== '"') isSpace Fold.toList
1486+-- >>> Stream.parse quotes $ Stream.fromList "\"a\"\"b\""
1487+-- Right "ab"
11181488--
11191489{-# INLINE wordFramedBy #-}
11201490wordFramedBy :: Monad m =>
@@ -1206,6 +1576,26 @@ data WordQuotedState s b a =
12061576 | WordQuotedEsc !s !Int a
12071577 | WordQuotedSkipPost !b
12081578
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.
1582+--
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.
1589+--
1590+-- >>> q = (`elem` ['"', '\''])
1591+-- >>> p kQ = Parser.wordQuotedBy kQ (== '\\') q q id isSpace Fold.toList
1592+--
1593+-- >>> Stream.parse (p False) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
1594+-- Right "ab'c;d\"ef"
1595+--
1596+-- >>> Stream.parse (p True) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
1597+-- Right "a\"b'c\";'d\"e'f"
1598+--
12091599{-# INLINE wordQuotedBy #-}
12101600wordQuotedBy :: (Monad m, Eq a) =>
12111601 Bool -- ^ keep the quotes in the output
@@ -1313,7 +1703,31 @@ data GroupByState a s
13131703 = GroupByInit !s
13141704 | GroupByGrouping !a !s
13151705
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.
1712+--
1713+-- * Stops - when the comparison fails.
1714+-- * Fails - never.
1715+--
1716+-- >>> :{
1717+-- runGroupsBy eq =
1718+-- Stream.fold Fold.toList
1719+-- . Stream.parseMany (Parser.groupBy eq Fold.toList)
1720+-- . Stream.fromList
1721+-- :}
1722+--
1723+-- >>> runGroupsBy (<) []
1724+-- []
1725+--
1726+-- >>> runGroupsBy (<) [1]
1727+-- [Right [1]]
1728+--
1729+-- >>> runGroupsBy (<) [3, 5, 4, 1, 2, 0]
1730+-- [Right [3,5,4],Right [1,2],Right [0]]
13171731--
13181732{-# INLINE groupBy #-}
13191733groupBy :: 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
13451759 extract (GroupByInit s) = fmap (Done 0) $ fextract s
13461760 extract (GroupByGrouping _ s) = fmap (Done 0) $ fextract s
13471761
1348−-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
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.
1770+--
1771+-- * Stops - when the comparison fails.
1772+-- * Fails - never.
1773+--
1774+-- >>> :{
1775+-- runGroupsByRolling eq =
1776+-- Stream.fold Fold.toList
1777+-- . Stream.parseMany (Parser.groupByRolling eq Fold.toList)
1778+-- . Stream.fromList
1779+-- :}
1780+--
1781+-- >>> runGroupsByRolling (<) []
1782+-- []
1783+--
1784+-- >>> runGroupsByRolling (<) [1]
1785+-- [Right [1]]
1786+--
1787+-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]
1788+-- [Right [3,5],Right [4],Right [1,2],Right [0]]
1789+--
1790+-- /Pre-release/
13491791--
13501792{-# INLINE groupByRolling #-}
13511793groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
@@ -1384,6 +1826,16 @@ data GroupByStatePair a s1 s2
13841826 | GroupByGroupingPairL !a !s1 !s2
13851827 | GroupByGroupingPairR !a !s1 !s2
13861828
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.
1833+--
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'.
1837+--
1838+-- /Pre-release/
13871839{-# INLINE groupByRollingEither #-}
13881840groupByRollingEither :: Monad m =>
13891841 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
@@ -1468,13 +1920,26 @@ groupByRollingEither
14681920-- XXX use an Unfold instead of a list?
14691921-- XXX custom combinators for matching list, array and stream?
14701922-- XXX rename to listBy?
1923+
1924+-- | Match the given sequence of elements using the given comparison function.
1925+-- Returns the original sequence if successful.
14711926--
1472−-- | See 'Streamly.Internal.Data.Parser.streamEqBy'.
1927+-- Definition:
14731928--
1474−-- /Pre-release/
1929+-- >>> listEqBy cmp xs = Parser.streamEqBy cmp (Stream.fromList xs) *> Parser.fromPure xs
1930+--
1931+-- Examples:
1932+--
1933+-- >>> Stream.parse (Parser.listEqBy (==) "string") $ Stream.fromList "string"
1934+-- Right "string"
1935+--
1936+-- >>> Stream.parse (Parser.listEqBy (==) "mismatch") $ Stream.fromList "match"
1937+-- Left (ParseError "streamEqBy: mismtach occurred")
14751938--
14761939{-# INLINE listEqBy #-}
14771940listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]
1941+listEqBy cmp xs = streamEqBy cmp (D.fromList xs) *> fromPure xs
1942+{-
14781943listEqBy cmp str = Parser step initial extract
14791944
14801945 where
@@ -1501,8 +1966,13 @@ listEqBy cmp str = Parser step initial extract
15011966 $ Error
15021967 $ "listEqBy: end of input, yet to match "
15031968 ++ show (length xs) ++ " elements"
1969+-}
15041970
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
1972+-- the stream.
1973+--
1974+-- See also: "Streamly.Data.Stream.streamEqBy"
1975+--
15061976{-# INLINE streamEqBy #-}
15071977streamEqBy :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()
15081978streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
@@ -1540,6 +2010,39 @@ streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
15402010
15412011 extract _ = return $ Error "streamEqBy: end of input"
15422012
2013+-- Rename to "list".
2014+-- | Match the input sequence with the supplied list and return it if
2015+-- successful.
2016+--
2017+-- >>> listEq = Parser.listEqBy (==)
2018+--
2019+{-# INLINE listEq #-}
2020+listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]
2021+listEq = listEqBy (==)
2022+
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
2031+
2032+{-
2033+-- Should go in Data.Parser.Regex in streamly package so that it can depend on
2034+-- regex backends.
2035+{-# INLINE regexPosix #-}
2036+regexPosix :: -- Monad m =>
2037+ Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
2038+regexPosix = undefined
2039+
2040+{-# INLINE regexPCRE #-}
2041+regexPCRE :: -- Monad m =>
2042+ Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
2043+regexPCRE = undefined
2044+-}
2045+
15432046-------------------------------------------------------------------------------
15442047-- Transformations on input
15452048-------------------------------------------------------------------------------
@@ -1548,7 +2051,7 @@ streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
15482051-- parser can always return a Continue in initial when we feed the fold's
15492052-- initial result to it. We can work this around for postscan by introducing an
15502053-- initial state and calling "initial" only on the first input.
1551−--
2054+
15522055-- | Stateful scan on the input of a parser using a Fold.
15532056--
15542057-- /Unimplemented/
@@ -1709,7 +2212,16 @@ spanByRolling eq f1 f2 =
17092212-- nested parsers
17102213-------------------------------------------------------------------------------
17112214
1712−-- | See 'Streamly.Internal.Data.Parser.takeP'.
2215+-- | Takes at-most @n@ input elements.
2216+--
2217+-- * Stops - when the collecting parser stops.
2218+-- * Fails - when the collecting parser fails.
2219+--
2220+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
2221+-- Right [1,2]
2222+--
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")
17132225--
17142226-- /Internal/
17152227{-# INLINE takeP #-}
@@ -1783,9 +2295,7 @@ takeP lim (Parser pstep pinitial pextract) = Parser step initial extract
17832295 Error err -> IError err
17842296 _ -> error "Bug: takeP invalid state in initial"
17852297
1786−-- | See 'Streamly.Internal.Data.Parser.lookahead'.
1787−--
1788−-- /Pre-release/
2298+-- | Run a parser without consuming the input.
17892299--
17902300{-# INLINE lookAhead #-}
17912301lookAhead :: Monad m => Parser a m b -> Parser a m b
@@ -1822,14 +2332,32 @@ lookAhead (Parser step1 initial1 _) = Parser step initial extract
18222332-------------------------------------------------------------------------------
18232333-- Interleaving
18242334-------------------------------------------------------------------------------
2335+--
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.
2339+--
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.
2345+--
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.
18252351
18262352data DeintercalateState fs sp ss =
18272353 DeintercalateL !fs !sp
18282354 | DeintercalateR !fs !ss !Bool
18292355
1830−-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
1831−--
1832−-- /Internal/
2356+-- XXX rename this to intercalate
2357+
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
2360+-- two patterns.
18332361--
18342362{-# INLINE deintercalate #-}
18352363deintercalate :: Monad m =>
@@ -1926,8 +2454,13 @@ data SepByState fs sp ss =
19262454 SepByInit !fs !sp
19272455 | SepBySeparator !fs !ss !Bool
19282456
1929−-- This is a special case of deintercalate and can be easily implemented in
1930−-- terms of deintercalate.
2457+-- | Run the content parser first, when it is done, the separator parser is
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.
2460+--
2461+-- >>> sepBy p1 p2 sink = Parser.deintercalate p1 p2 (Fold.catLefts sink)
2462+-- >>> sepBy content sep sink = Parser.sepBy1 content sep sink <|> return mempty
2463+--
19312464{-# INLINE sepBy #-}
19322465sepBy :: Monad m =>
19332466 Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
@@ -2000,11 +2533,49 @@ sepBy
20002533
20012534 extract (SepBySeparator fs _ _) = fmap (Done 0) $ fextract fs
20022535
2536+-- XXX This can be implemented using refold, parse one and then continue
2537+-- collecting the rest in that.
2538+
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.
2541+--
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.
2544+--
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
2549+ x <- p
2550+ f <- fromEffect $ FL.reduce sink
2551+ f1 <- fromEffect $ FL.snoc f x
2552+ many (sep >> p) f1
2553+
2554+-------------------------------------------------------------------------------
2555+-- Interleaving a collection of parsers
2556+-------------------------------------------------------------------------------
2557+--
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.
2561+--
2562+-- /Unimplemented/
2563+--
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
2568+
20032569-------------------------------------------------------------------------------
20042570-- Sequential Collection
20052571-------------------------------------------------------------------------------
2572+
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.
2576+--
2577+-- /Pre-release/
20062578--
2007−-- | See 'Streamly.Internal.Data.Parser.sequence'.
20082579{-# INLINE sequence #-}
20092580sequence :: Monad m =>
20102581 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) =
20892660-------------------------------------------------------------------------------
20902661
20912662{-
2092−-- | See 'Streamly.Internal.Data.Parser.choice'.
2663+-- | @choice parsers@ applies the @parsers@ in order and returns the first
2664+-- successful parse.
2665+--
2666+-- This is same as 'asum' but more efficient.
20932667--
20942668-- /Broken/
20952669--
@@ -2102,23 +2676,45 @@ choice = foldl1 shortest
21022676-- Sequential Repetition
21032677-------------------------------------------------------------------------------
21042678
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.
2681+--
2682+-- /Unimplemented/
2683+--
21052684{-# INLINE manyP #-}
21062685manyP :: -- MonadCatch m =>
21072686 Parser a m b -> Parser b m c -> Parser a m c
21082687manyP _p _f = undefined
21092688
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.
21112691--
2112−-- /Pre-release/
2692+-- Stops: when the downstream fold stops or the parser fails.
2693+-- Fails: never, produces zero or more results.
2694+--
2695+-- >>> many = Parser.countBetween 0 maxBound
2696+--
2697+-- Compare with 'Control.Applicative.many'.
21132698--
21142699{-# INLINE many #-}
21152700many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
21162701many = splitMany
21172702-- many = countBetween 0 maxBound
21182703
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
2706+-- compatibility.
2707+
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.
21202710--
2121−-- /Pre-release/
2711+-- Stops: when the downstream fold stops or the parser fails.
2712+-- Fails: if it stops without producing a single result.
2713+--
2714+-- >>> some p f = Parser.manyP p (Parser.takeGE 1 f)
2715+-- >>> some = Parser.countBetween 1 maxBound
2716+--
2717+-- Compare with 'Control.Applicative.some'.
21222718--
21232719{-# INLINE some #-}
21242720some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
@@ -2126,7 +2722,11 @@ some = splitSome
21262722-- some p f = manyP p (takeGE 1 f)
21272723-- some = countBetween 1 maxBound
21282724
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.
2728+--
2729+-- >>> countBetween m n p f = Parser.manyP p (Parser.takeBetween m n f)
21302730--
21312731-- /Unimplemented/
21322732--
@@ -2136,7 +2736,12 @@ countBetween :: -- MonadCatch m =>
21362736countBetween _m _n _p = undefined
21372737-- countBetween m n p f = manyP p (takeBetween m n f)
21382738
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.
2742+--
2743+-- >>> count n = Parser.countBetween n n
2744+-- >>> count n p f = Parser.manyP p (Parser.takeEQ n f)
21402745--
21412746-- /Unimplemented/
21422747--
@@ -2146,13 +2751,34 @@ count :: -- MonadCatch m =>
21462751count n = countBetween n n
21472752-- count n p f = manyP p (takeEQ n f)
21482753
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.
2756+--
2757+-- We can implemnent parsers like the following using 'manyTillP':
2758+--
2759+-- @
2760+-- countBetweenTill m n f p = manyTillP (takeBetween m n f) p
2761+-- @
2762+--
2763+-- /Unimplemented/
2764+--
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
2770+
21492771data ManyTillState fs sr sl
21502772 = ManyTillR Int fs sr
21512773 | ManyTillL Int fs sl
21522774
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.
21542780--
2155−-- /Pre-release/
2781+-- Stops when the fold @f@ stops.
21562782--
21572783{-# INLINE manyTill #-}
21582784manyTill :: Monad m
@@ -2245,3 +2871,53 @@ manyTill (Parser stepL initialL extractL)
22452871 return $ Continue n (ManyTillL 0 fs s)
22462872 Partial _ _ -> error "Partial in extract"
22472873 extract (ManyTillR _ fs _) = fmap (Done 0) $ fextract fs
2874+
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.
2879+--
2880+-- For example, this can be used to find a key frame in a video stream after an
2881+-- error.
2882+--
2883+-- /Unimplemented/
2884+--
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
2889+
2890+-------------------------------------------------------------------------------
2891+-- Repeated Alternatives
2892+-------------------------------------------------------------------------------
2893+
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.
2897+--
2898+-- /Unimplemented/
2899+--
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
2904+
2905+-- | Like 'retryMaxTotal' but aborts after @n@ successive failures.
2906+--
2907+-- /Unimplemented/
2908+--
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
2913+
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
2916+-- input.
2917+--
2918+-- /Unimplemented/
2919+--
2920+{-# INLINE retry #-}
2921+retry :: -- (Monad m) =>
2922+ Parser a m b -> Parser a m b
2923+retry _p = undefined