4-- Module : Streamly.Internal.Data.Parser.ParserD
5-- Copyright : (c) 2020 Composewell Technologies
6-- License : BSD-3-Clause
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
11-- Direct style parser implementation with stream fusion.
13module Streamly.Internal.Data.Parser.ParserD
21 -- * Downgrade to Fold
24 -- First order parsers
42 -- Parsers chained in series, if one parser terminates the composition
43 -- terminates. Currently we are using folds to collect the output of the
44 -- parsers but we can use Parsers instead of folds to make the composition
45 -- more powerful. For example, we can do:
47 -- sliceSepByMax cond n p = sliceBy cond (take n p)
48 -- sliceSepByBetween cond m n p = sliceBy cond (takeBetween m n p)
49 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
51 -- Grab a sequence of input elements without inspecting them
53 -- , take -- take -- takeBetween 0 n
54 -- , takeLE1 -- take1 -- takeBetween 1 n
55 , takeEQ -- takeBetween n n
56 , takeGE -- takeBetween n maxBound
58 -- Grab a sequence of input elements by inspecting them
64 -- , sliceSepByBetween
68 -- , frameSepBy -- parse frames escaped by an escape char/sequence
74 , groupByRollingEither
76 -- , prefixOf -- match any prefix of a given string
77 -- , suffixOf -- match any suffix of a given string
78 -- , infixOf -- match any substring of a given string
85 -- Second order parsers (parsers using parsers)
86 -- * Binary Combinators
88 -- ** Sequential Applicative
92 -- ** Parallel Applicatives
96 -- , teeTill -- like manyTill but parallel
98 -- ** Sequential Interleaving
99 -- Use two folds, run a primary parser, its rejected values go to the
103 -- ** Sequential Alternative
106 -- ** Parallel Alternatives
111 -- * N-ary Combinators
112 -- ** Sequential Collection
116 -- ** Sequential Repetition
119 -- , countBetweenTill
125 -- -- ** Special cases
126 -- XXX traditional implmentations of these may be of limited use. For
127 -- example, consider parsing lines separated by "\r\n". The main parser
128 -- will have to detect and exclude the sequence "\r\n" anyway so that we
129 -- can apply the "sep" parser.
131 -- We can instead implement these as special cases of deintercalate.
142 -- A simple and stupid impl would be to just convert the stream to an array
143 -- and give the array reference to all consumers. The array can be grown on
144 -- demand by any consumer and truncated when nonbody needs it.
146 -- -- ** Distribute to collection
147 -- -- ** Distribute to repetition
149 -- -- ** Interleaved collection
152 -- -- ** Interleaved repetition
153 -- repeat one parser and when it fails run an error recovery parser
154 -- e.g. to find a key frame in the stream after an error
156 -- ** Collection of Alternatives
159 -- , fastestN -- first N successful in time
160 -- , choiceN -- first N successful in position
161 , choice -- first successful in position
163 -- -- ** Repeated Alternatives
164 -- , retryMax -- try N times
165 -- , retryUntil -- try until successful
166 -- , retryUntilN -- try until successful n times
170import Control.Exception (assert, Exception)
171import Control.Monad (when)
172import Control.Monad.Catch (MonadCatch, MonadThrow(..))
173import Fusion.Plugin.Types (Fuse(..))
174import Streamly.Internal.Data.Fold.Type (Fold(..))
175import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
177import qualified Streamly.Internal.Data.Fold.Type as FL
180 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span)
181import Streamly.Internal.Data.Parser.ParserD.Tee
182import Streamly.Internal.Data.Parser.ParserD.Type
187-- >>> import Prelude hiding ()
188-- >>> import qualified Streamly.Prelude as Stream
189-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
190-- >>> import qualified Streamly.Data.Fold as Fold
191-- >>> import qualified Streamly.Internal.Data.Parser as Parser
193-------------------------------------------------------------------------------
194-- Downgrade a parser to a Fold
195-------------------------------------------------------------------------------
197data ParserToFoldError =
205instance Exception ParserToFoldError
207-- | See 'Streamly.Internal.Data.Parser.toFold'.
212toFold :: MonadThrow m => Parser m a b -> Fold m a b
213toFold (Parser pstep pinitial pextract) = Fold step initial pextract
220 IPartial s -> return $ FL.Partial s
221 IDone b -> return $ FL.Done b
222 IError err -> throwM $ InitialError err
227 Partial 0 s -> return $ FL.Partial s
228 Continue 0 s -> return $ FL.Partial s
229 Done 0 b -> return $ FL.Done b
230 Partial n _ -> throwM $ PartialError n
231 Continue n _ -> throwM $ ContinueError n
232 Done n _ -> throwM $ DoneError n
233 Error err -> throwM $ ErrorError err
235-------------------------------------------------------------------------------
236-- Upgrade folds to parses
237-------------------------------------------------------------------------------
239-- | See 'Streamly.Internal.Data.Parser.fromFold'.
243{-# INLINE fromFold #-}
244fromFold :: Monad m => Fold m a b -> Parser m a b
245fromFold (Fold fstep finitial fextract) = Parser step initial fextract
253 FL.Partial s1 -> IPartial s1
260 FL.Partial s1 -> Partial 0 s1
261 FL.Done b -> Done 0 b
263-------------------------------------------------------------------------------
265-------------------------------------------------------------------------------
267-- | See 'Streamly.Internal.Data.Parser.peek'.
271{-# INLINABLE peek #-}
272peek :: MonadThrow m => Parser m a a
273peek = Parser step initial extract
277 initial = return $ IPartial ()
279 step () a = return $ Done 1 a
281 extract () = throwM $ ParseError "peek: end of input"
283-- | See 'Streamly.Internal.Data.Parser.eof'.
288eof :: Monad m => Parser m a ()
289eof = Parser step initial return
293 initial = return $ IPartial ()
295 step () _ = return $ Error "eof: not at end of input"
297-- | See 'Streamly.Internal.Data.Parser.satisfy'.
301{-# INLINE satisfy #-}
302satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
303satisfy predicate = Parser step initial extract
307 initial = return $ IPartial ()
312 else Error "satisfy: predicate failed"
314 extract _ = throwM $ ParseError "satisfy: end of input"
316-- | See 'Streamly.Internal.Data.Parser.next'.
321next :: Monad m => Parser m a (Maybe a)
322next = Parser step initial extract
324 initial = pure $ IPartial ()
325 step _ a = pure $ Done 0 (Just a)
326 extract _ = pure Nothing
328-- | See 'Streamly.Internal.Data.Parser.maybe'.
333maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
334maybe parser = Parser step initial extract
338 initial = return $ IPartial ()
343 Nothing -> Error "maybe: predicate failed"
345 extract _ = throwM $ ParseError "maybe: end of input"
347-- | See 'Streamly.Internal.Data.Parser.either'.
352either :: MonadThrow m => (a -> Either String b) -> Parser m a b
353either parser = Parser step initial extract
357 initial = return $ IPartial ()
362 Left err -> Error $ "either: " ++ err
364 extract _ = throwM $ ParseError "either: end of input"
366-------------------------------------------------------------------------------
368-------------------------------------------------------------------------------
370-- Required to fuse "take" with "many" in "chunksOf", for ghc-9.x
371{-# ANN type Tuple'Fused Fuse #-}
372data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
374-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
378{-# INLINE takeBetween #-}
379takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
380takeBetween low high (Fold fstep finitial fextract) =
382 Parser step initial (extract streamErr)
387 "takeBetween: Expecting alteast " ++ show low
388 ++ " elements, got " ++ show i
391 "takeBetween: lower bound - " ++ show low
392 ++ " is greater than higher bound - " ++ show high
394 foldErr :: Int -> String
396 "takeBetween: the collecting fold terminated after"
397 ++ " consuming" ++ show i ++ " elements"
398 ++ " minimum" ++ show low ++ " elements needed"
400 -- Exactly the same as snext except different constructors, we can possibly
401 -- deduplicate the two.
407 let s1 = Tuple'Fused i1 s
409 -- XXX ideally this should be a Continue instead
410 then return $ IPartial s1
411 else IDone <$> extract foldErr s1
416 else IError (foldErr i1)
419 when (low >= 0 && high >= 0 && low > high)
420 $ throwM $ ParseError invalidRange
422 finitial >>= inext (-1)
424 -- Keep the impl same as inext
430 let s1 = Tuple'Fused i1 s
432 then return $ Continue 0 s1
433 else Done 0 <$> extract foldErr s1
438 else Error (foldErr i1)
440 step (Tuple'Fused i s) a = fstep s a >>= snext i
442 extract f (Tuple'Fused i s)
443 | i >= low && i <= high = fextract s
444 | otherwise = throwM $ ParseError (f i)
446-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
451takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
452takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
461 FL.Partial s -> IPartial $ Tuple' 0 s
466 $ "takeEQ: Expecting exactly " ++ show cnt
467 ++ " elements, fold terminated without"
468 ++ " consuming any elements"
475 FL.Partial s -> Continue 0 $ Tuple' i1 s
478 $ "takeEQ: Expecting exactly " ++ show cnt
479 ++ " elements, fold terminated on " ++ show i1
484 FL.Partial s -> fextract s
485 FL.Done b -> return b
486 -- XXX we should not reach here when initial returns Step type
487 -- reachable only when n == 0
488 | otherwise = Done 1 <$> fextract r
495 | i == 0 && cnt == 0 = fextract r
499 $ "takeEQ: Expecting exactly " ++ show cnt
500 ++ " elements, input terminated on " ++ show i
502-- | See 'Streamly.Internal.Data.Parser.takeGE'.
507takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
508takeGE n (Fold fstep finitial fextract) = Parser step initial extract
516 FL.Partial s -> IPartial $ Tuple' 0 s
521 $ "takeGE: Expecting at least " ++ show cnt
522 ++ " elements, fold terminated without"
523 ++ " consuming any elements"
530 FL.Partial s -> Continue 0 $ Tuple' i1 s
533 $ "takeGE: Expecting at least " ++ show cnt
534 ++ " elements, fold terminated on " ++ show i1
539 FL.Partial s -> Partial 0 $ Tuple' i1 s
540 FL.Done b -> Done 0 b
547 | i >= cnt = fextract r
551 $ "takeGE: Expecting at least " ++ show cnt
552 ++ " elements, input terminated on " ++ show i
554-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
558{-# INLINE takeWhile #-}
559takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
560takeWhile predicate (Fold fstep finitial fextract) =
561 Parser step initial fextract
568 FL.Partial s -> IPartial s
577 FL.Partial s1 -> Partial 0 s1
578 FL.Done b -> Done 0 b
579 else Done 1 <$> fextract s
581-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
585{-# INLINE takeWhile1 #-}
586takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
587takeWhile1 predicate (Fold fstep finitial fextract) =
588 Parser step initial extract
595 FL.Partial s -> IPartial (Left s)
598 $ "takeWhile1: fold terminated without consuming:"
601 {-# INLINE process #-}
606 FL.Partial s1 -> Partial 0 (Right s1)
607 FL.Done b -> Done 0 b
612 else return $ Error "takeWhile1: predicate failed on first element"
620 extract (Left _) = throwM $ ParseError "takeWhile1: end of input"
621 extract (Right s) = fextract s
623-- | See 'Streamly.Internal.Data.Parser.sliceSepByP'.
627sliceSepByP :: MonadCatch m =>
628 (a -> Bool) -> Parser m a b -> Parser m a b
629sliceSepByP cond (Parser pstep pinitial pextract) =
631 Parser step initial pextract
644-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
648data SliceBeginWithState s = Left' s | Right' s
650{-# INLINE sliceBeginWith #-}
651sliceBeginWith :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
652sliceBeginWith cond (Fold fstep finitial fextract) =
654 Parser step initial extract
662 FL.Partial s -> IPartial (Left' s)
663 FL.Done _ -> IError "sliceBeginWith : bad finitial"
665 {-# INLINE process #-}
670 FL.Partial s1 -> Partial 0 (Right' s1)
671 FL.Done b -> Done 0 b
676 else error $ "sliceBeginWith : slice begins with an element which "
677 ++ "fails the predicate"
681 else Done 1 <$> fextract s
683 extract (Left' s) = fextract s
684 extract (Right' s) = fextract s
686data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
688-- | See 'Streamly.Internal.Data.Parser.wordBy'.
692wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
693wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
697 {-# INLINE worder #-}
702 FL.Partial s1 -> Partial 0 $ WBWord s1
703 FL.Done b -> Done 0 b
709 FL.Partial s -> IPartial $ WBLeft s
715 else return $ Partial 0 $ WBLeft s
721 return $ Partial 0 $ WBRight b
724 $ if not (predicate a)
726 else Partial 0 $ WBRight b
728 extract (WBLeft s) = fextract s
729 extract (WBWord s) = fextract s
730 extract (WBRight b) = return b
732{-# ANN type GroupByState Fuse #-}
735 | GroupByGrouping !a !s
737-- | See 'Streamly.Internal.Data.Parser.groupBy'.
739{-# INLINE groupBy #-}
740groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
741groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
745 {-# INLINE grouper #-}
750 FL.Done b -> Done 0 b
751 FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
757 FL.Partial s -> IPartial $ GroupByInit s
760 step (GroupByInit s) a = grouper s a a
761 step (GroupByGrouping a0 s) a =
764 else Done 1 <$> fextract s
766 extract (GroupByInit s) = fextract s
767 extract (GroupByGrouping _ s) = fextract s
769-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
771{-# INLINE groupByRolling #-}
772groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
773groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
777 {-# INLINE grouper #-}
782 FL.Done b -> Done 0 b
783 FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
789 FL.Partial s -> IPartial $ GroupByInit s
792 step (GroupByInit s) a = grouper s a
793 step (GroupByGrouping a0 s) a =
796 else Done 1 <$> fextract s
798 extract (GroupByInit s) = fextract s
799 extract (GroupByGrouping _ s) = fextract s
801{-# ANN type GroupByStatePair Fuse #-}
802data GroupByStatePair a s1 s2
803 = GroupByInitPair !s1 !s2
804 | GroupByGroupingPair !a !s1 !s2
805 | GroupByGroupingPairL !a !s1 !s2
806 | GroupByGroupingPairR !a !s1 !s2
808{-# INLINABLE groupByRollingEither #-}
809groupByRollingEither :: MonadCatch m =>
810 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (Either b c)
813 (Fold fstep1 finitial1 fextract1)
814 (Fold fstep2 finitial2 fextract2) = Parser step initial extract
818 {-# INLINE grouper #-}
820 return $ Continue 0 (GroupByGroupingPair a s1 s2)
822 {-# INLINE grouperL2 #-}
823 grouperL2 s1 s2 a = do
827 FL.Done b -> Done 0 (Left b)
828 FL.Partial s11 -> Partial 0 (GroupByGroupingPairL a s11 s2)
830 {-# INLINE grouperL #-}
831 grouperL s1 s2 a0 a = do
834 FL.Done b -> return $ Done 0 (Left b)
835 FL.Partial s11 -> grouperL2 s11 s2 a
837 {-# INLINE grouperR2 #-}
838 grouperR2 s1 s2 a = do
842 FL.Done b -> Done 0 (Right b)
843 FL.Partial s21 -> Partial 0 (GroupByGroupingPairR a s1 s21)
845 {-# INLINE grouperR #-}
846 grouperR s1 s2 a0 a = do
849 FL.Done b -> return $ Done 0 (Right b)
850 FL.Partial s21 -> grouperR2 s1 s21 a
859 FL.Partial s2 -> IPartial $ GroupByInitPair s1 s2
860 FL.Done b -> IDone (Right b)
861 FL.Done b -> IDone (Left b)
863 step (GroupByInitPair s1 s2) a = grouper s1 s2 a
865 step (GroupByGroupingPair a0 s1 s2) a =
867 then grouperL s1 s2 a0 a
868 else grouperR s1 s2 a0 a
870 step (GroupByGroupingPairL a0 s1 s2) a =
872 then grouperL2 s1 s2 a
873 else Done 1 . Left <$> fextract1 s1
875 step (GroupByGroupingPairR a0 s1 s2) a =
877 then grouperR2 s1 s2 a
878 else Done 1 . Right <$> fextract2 s2
880 extract (GroupByInitPair s1 _) = Left <$> fextract1 s1
881 extract (GroupByGroupingPairL _ s1 _) = Left <$> fextract1 s1
882 extract (GroupByGroupingPairR _ _ s2) = Right <$> fextract2 s2
883 extract (GroupByGroupingPair a s1 _) = do
886 FL.Done b -> return $ Left b
887 FL.Partial s11 -> Left <$> fextract1 s11
889-- XXX use an Unfold instead of a list?
890-- XXX custom combinators for matching list, array and stream?
892-- | See 'Streamly.Internal.Data.Parser.eqBy'.
897eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
898eqBy cmp str = Parser step initial extract
902 initial = return $ IPartial str
904 step [] _ = return $ Done 0 ()
909 else Error "eqBy: failed, yet to match the last element"
915 $ "eqBy: failed, yet to match "
916 ++ show (length xs + 1) ++ " elements"
921 $ "eqBy: end of input, yet to match "
922 ++ show (length xs) ++ " elements"
924--------------------------------------------------------------------------------
926--------------------------------------------------------------------------------
928-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the
929-- input as long as the predicate @p@ is 'True'. @f2@ consumes the rest of the
933-- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs
935-- > span_ (< 1) [1,2,3]
938-- > span_ (< 2) [1,2,3]
941-- > span_ (< 4) [1,2,3]
948span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
949span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)
951-- | Break the input stream into two groups, the first group takes the input as
952-- long as the predicate applied to the first element of the stream and next
953-- input element holds 'True', the second group takes the rest of the input.
960 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
961spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)
963-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
964-- predicate is applied to the previous and the next input elements.
967{-# INLINE spanByRolling #-}
970 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
971spanByRolling eq f1 f2 =
972 noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)
974-------------------------------------------------------------------------------
976-------------------------------------------------------------------------------
978-- | See 'Streamly.Internal.Data.Parser.takeP'.
982takeP :: Monad m => Int -> Parser m a b -> Parser m a b
983takeP lim (Parser pstep pinitial pextract) = Parser step initial extract
992 then return $ IPartial $ Tuple' 0 s
993 else IDone <$> pextract s
994 IDone b -> return $ IDone b
995 IError e -> return $ IError e
997 step (Tuple' cnt r) a = do
998 assert (cnt < lim) (return ())
1003 assert (cnt1 >= 0) (return ())
1005 then return $ Partial 0 $ Tuple' cnt1 s
1006 else Done 0 <$> pextract s
1008 assert (cnt1 >= 0) (return ())
1010 then return $ Continue 0 $ Tuple' cnt1 s
1011 -- XXX This should error out?
1012 -- If designed properly, this will probably error out.
1013 -- "pextract" should error out
1017 -- This is a tricky case, we have the following options:
1018 -- 1. Done 0 with extract as you have written
1019 -- 2. Done n, will require buffering elements
1020 -- 3. Use a backtracking fold and not a parser, once we have
1021 -- backtracking in folds
1022 else Done 0 <$> pextract s
1024 let taken = cnt1 - n
1025 assert (taken >= 0) (return ())
1026 return $ Partial n $ Tuple' taken s
1028 let taken = cnt1 - n
1029 assert (taken >= 0) (return ())
1030 return $ Continue n $ Tuple' taken s
1031 Done n b -> return $ Done n b
1032 Error str -> return $ Error str
1034 extract (Tuple' _ r) = pextract r
1036-- | See 'Streamly.Internal.Data.Parser.lookahead'.
1040{-# INLINE lookAhead #-}
1041lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
1042lookAhead (Parser step1 initial1 _) = Parser step initial extract
1048 return $ case res of
1049 IPartial s -> IPartial (Tuple' 0 s)
1051 IError e -> IError e
1053 step (Tuple' cnt st) a = do
1058 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
1059 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
1060 Done _ b -> Done cnt1 b
1061 Error err -> Error err
1063 -- XXX returning an error let's us backtrack. To implement it in a way so
1064 -- that it terminates on eof without an error then we need a way to
1065 -- backtrack on eof, that will require extract to return 'Step' type.
1066 extract (Tuple' n _) =
1069 $ "lookAhead: end of input after consuming "
1070 ++ show n ++ " elements"
1072-------------------------------------------------------------------------------
1074-------------------------------------------------------------------------------
1076-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
1080{-# INLINE deintercalate #-}
1083 Fold m a y -> Parser m x a
1084 -> Fold m b z -> Parser m x b
1085 -> Parser m x (y, z)
1086deintercalate = undefined
1088-------------------------------------------------------------------------------
1089-- Sequential Collection
1090-------------------------------------------------------------------------------
1092-- | See 'Streamly.Internal.Data.Parser.sequence'.
1096{-# INLINE sequence #-}
1099 Fold m b c -> t (Parser m a b) -> Parser m a c
1100sequence _f _p = undefined
1102-------------------------------------------------------------------------------
1103-- Alternative Collection
1104-------------------------------------------------------------------------------
1106-- | See 'Streamly.Internal.Data.Parser.choice'.
1110{-# INLINE choice #-}
1111choice :: (MonadCatch m, Foldable t) => t (Parser m a b) -> Parser m a b
1112choice = foldl1 shortest
1114-------------------------------------------------------------------------------
1115-- Sequential Repetition
1116-------------------------------------------------------------------------------
1118-- | See 'Streamly.Internal.Data.Parser.many'.
1123many :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1125-- many = countBetween 0 maxBound
1127-- | See 'Streamly.Internal.Data.Parser.some'.
1132some :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1134-- some f p = many (takeGE 1 f) p
1135-- many = countBetween 1 maxBound
1137-- | See 'Streamly.Internal.Data.Parser.countBetween'.
1141{-# INLINE countBetween #-}
1144 Int -> Int -> Parser m a b -> Fold m b c -> Parser m a c
1145countBetween _m _n _p = undefined
1146-- countBetween m n p f = many (takeBetween m n f) p
1148-- | See 'Streamly.Internal.Data.Parser.count'.
1155 Int -> Parser m a b -> Fold m b c -> Parser m a c
1156count n = countBetween n n
1157-- count n f p = many (takeEQ n f) p
1159data ManyTillState fs sr sl
1160 = ManyTillR Int fs sr
1161 | ManyTillL Int fs sl
1163-- | See 'Streamly.Internal.Data.Parser.manyTill'.
1167{-# INLINE manyTill #-}
1168manyTill :: MonadCatch m
1169 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
1170manyTill (Fold fstep finitial fextract)
1171 (Parser stepL initialL extractL)
1172 (Parser stepR initialR _) =
1173 Parser step initial extract
1177 -- Caution: Mutual recursion
1179 -- Don't inline this
1180 scrutL fs p c d e = do
1183 IPartial sl -> return $ c (ManyTillL 0 fs sl)
1187 FL.Partial fs1 -> scrutR fs1 p c d e
1188 FL.Done fb -> return $ d fb
1189 IError err -> return $ e err
1191 {-# INLINE scrutR #-}
1192 scrutR fs p c d e = do
1195 IPartial sr -> return $ p (ManyTillR 0 fs sr)
1196 IDone _ -> d <$> fextract fs
1197 IError _ -> scrutL fs p c d e
1202 FL.Partial fs -> scrutR fs IPartial IPartial IDone IError
1203 FL.Done b -> return $ IDone b
1205 step (ManyTillR cnt fs st) a = do
1208 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
1210 assert (cnt + 1 - n >= 0) (return ())
1211 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
1219 return $ Continue (cnt + 1) (ManyTillL 0 fs sl)
1227 FL.Partial fs1 -> scrutR fs1 p c d Error
1228 FL.Done fb -> return $ Done cnt1 fb
1229 IError err -> return $ Error err
1230 -- XXX the cnt is being used only by the assert
1231 step (ManyTillL cnt fs st) a = do
1234 Partial n s -> return $ Partial n (ManyTillL 0 fs s)
1236 assert (cnt + 1 - n >= 0) (return ())
1237 return $ Continue n (ManyTillL (cnt + 1 - n) fs s)
1242 scrutR s (Partial n) (Continue n) (Done n) Error
1243 FL.Done b1 -> return $ Done n b1
1244 Error err -> return $ Error err
1246 extract (ManyTillL _ fs sR) = do
1247 res <- extractL sR >>= fstep fs
1249 FL.Partial s -> fextract s
1250 FL.Done b -> return b
1251 extract (ManyTillR _ fs _) = fextract fs
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list