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 -- * Conversion to/from ParserK
25 -- * Downgrade to Fold
28 -- First order parsers
46 -- Parsers chained in series, if one parser terminates the composition
47 -- terminates. Currently we are using folds to collect the output of the
48 -- parsers but we can use Parsers instead of folds to make the composition
49 -- more powerful. For example, we can do:
51 -- sliceSepByMax cond n p = sliceBy cond (take n p)
52 -- sliceSepByBetween cond m n p = sliceBy cond (takeBetween m n p)
53 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
55 -- Grab a sequence of input elements without inspecting them
57 -- , take -- take -- takeBetween 0 n
58 -- , takeLE1 -- take1 -- takeBetween 1 n
59 , takeEQ -- takeBetween n n
60 , takeGE -- takeBetween n maxBound
62 -- Grab a sequence of input elements by inspecting them
68 -- , sliceSepByBetween
72 -- , frameSepBy -- parse frames escaped by an escape char/sequence
78 , groupByRollingEither
80 -- , prefixOf -- match any prefix of a given string
81 -- , suffixOf -- match any suffix of a given string
82 -- , infixOf -- match any substring of a given string
89 -- Second order parsers (parsers using parsers)
90 -- * Binary Combinators
92 -- ** Sequential Applicative
96 -- ** Parallel Applicatives
100 -- , teeTill -- like manyTill but parallel
102 -- ** Sequential Interleaving
103 -- Use two folds, run a primary parser, its rejected values go to the
107 -- ** Sequential Alternative
110 -- ** Parallel Alternatives
115 -- * N-ary Combinators
116 -- ** Sequential Collection
120 -- ** Sequential Repetition
123 -- , countBetweenTill
129 -- -- ** Special cases
130 -- XXX traditional implmentations of these may be of limited use. For
131 -- example, consider parsing lines separated by "\r\n". The main parser
132 -- will have to detect and exclude the sequence "\r\n" anyway so that we
133 -- can apply the "sep" parser.
135 -- We can instead implement these as special cases of deintercalate.
146 -- A simple and stupid impl would be to just convert the stream to an array
147 -- and give the array reference to all consumers. The array can be grown on
148 -- demand by any consumer and truncated when nonbody needs it.
150 -- -- ** Distribute to collection
151 -- -- ** Distribute to repetition
153 -- -- ** Interleaved collection
156 -- -- ** Interleaved repetition
157 -- repeat one parser and when it fails run an error recovery parser
158 -- e.g. to find a key frame in the stream after an error
160 -- ** Collection of Alternatives
163 -- , fastestN -- first N successful in time
164 -- , choiceN -- first N successful in position
165 , choice -- first successful in position
167 -- -- ** Repeated Alternatives
168 -- , retryMax -- try N times
169 -- , retryUntil -- try until successful
170 -- , retryUntilN -- try until successful n times
174import Control.Exception (assert, Exception)
175import Control.Monad (when)
176import Control.Monad.Catch (MonadCatch, MonadThrow(..))
177import Fusion.Plugin.Types (Fuse(..))
178import Streamly.Internal.Data.Fold.Type (Fold(..))
179import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
181import qualified Streamly.Internal.Data.Fold.Type as FL
184 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span)
185import Streamly.Internal.Data.Parser.ParserD.Tee
186import Streamly.Internal.Data.Parser.ParserD.Type
191-- >>> :set -package streamly
192-- >>> import Prelude hiding ()
193-- >>> import qualified Streamly.Prelude as Stream
194-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
195-- >>> import qualified Streamly.Data.Fold as Fold
196-- >>> import qualified Streamly.Internal.Data.Parser as Parser
198-------------------------------------------------------------------------------
199-- Downgrade a parser to a Fold
200-------------------------------------------------------------------------------
202data ParserToFoldError =
210instance Exception ParserToFoldError
212-- | See 'Streamly.Internal.Data.Parser.toFold'.
217toFold :: MonadThrow m => Parser m a b -> Fold m a b
218toFold (Parser pstep pinitial pextract) = Fold step initial pextract
225 IPartial s -> return $ FL.Partial s
226 IDone b -> return $ FL.Done b
227 IError err -> throwM $ InitialError err
232 Partial 0 s -> return $ FL.Partial s
233 Continue 0 s -> return $ FL.Partial s
234 Done 0 b -> return $ FL.Done b
235 Partial n _ -> throwM $ PartialError n
236 Continue n _ -> throwM $ ContinueError n
237 Done n _ -> throwM $ DoneError n
238 Error err -> throwM $ ErrorError err
240-------------------------------------------------------------------------------
241-- Upgrade folds to parses
242-------------------------------------------------------------------------------
244-- | See 'Streamly.Internal.Data.Parser.fromFold'.
248{-# INLINE fromFold #-}
249fromFold :: Monad m => Fold m a b -> Parser m a b
250fromFold (Fold fstep finitial fextract) = Parser step initial fextract
258 FL.Partial s1 -> IPartial s1
265 FL.Partial s1 -> Partial 0 s1
266 FL.Done b -> Done 0 b
268-------------------------------------------------------------------------------
270-------------------------------------------------------------------------------
272-- | See 'Streamly.Internal.Data.Parser.peek'.
277peek :: MonadThrow m => Parser m a a
278peek = Parser step initial extract
282 initial = return $ IPartial ()
284 step () a = return $ Done 1 a
286 extract () = throwM $ ParseError "peek: end of input"
288-- | See 'Streamly.Internal.Data.Parser.eof'.
293eof :: Monad m => Parser m a ()
294eof = Parser step initial return
298 initial = return $ IPartial ()
300 step () _ = return $ Error "eof: not at end of input"
302-- | See 'Streamly.Internal.Data.Parser.satisfy'.
306{-# INLINE satisfy #-}
307satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
308satisfy predicate = Parser step initial extract
312 initial = return $ IPartial ()
317 else Error "satisfy: predicate failed"
319 extract _ = throwM $ ParseError "satisfy: end of input"
321-- | See 'Streamly.Internal.Data.Parser.next'.
326next :: Monad m => Parser m a (Maybe a)
327next = Parser step initial extract
329 initial = pure $ IPartial ()
330 step _ a = pure $ Done 0 (Just a)
331 extract _ = pure Nothing
333-- | See 'Streamly.Internal.Data.Parser.maybe'.
338maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
339maybe parserF = Parser step initial extract
343 initial = return $ IPartial ()
348 Nothing -> Error "maybe: predicate failed"
350 extract _ = throwM $ ParseError "maybe: end of input"
352-- | See 'Streamly.Internal.Data.Parser.either'.
357either :: MonadThrow m => (a -> Either String b) -> Parser m a b
358either parserF = Parser step initial extract
362 initial = return $ IPartial ()
367 Left err -> Error $ "either: " ++ err
369 extract _ = throwM $ ParseError "either: end of input"
371-------------------------------------------------------------------------------
373-------------------------------------------------------------------------------
375-- Required to fuse "take" with "many" in "chunksOf", for ghc-9.x
376{-# ANN type Tuple'Fused Fuse #-}
377data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
379-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
383{-# INLINE takeBetween #-}
384takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
385takeBetween low high (Fold fstep finitial fextract) =
387 Parser step initial (extract streamErr)
392 "takeBetween: Expecting alteast " ++ show low
393 ++ " elements, got " ++ show i
396 "takeBetween: lower bound - " ++ show low
397 ++ " is greater than higher bound - " ++ show high
399 foldErr :: Int -> String
401 "takeBetween: the collecting fold terminated after"
402 ++ " consuming" ++ show i ++ " elements"
403 ++ " minimum" ++ show low ++ " elements needed"
405 -- Exactly the same as snext except different constructors, we can possibly
406 -- deduplicate the two.
412 let s1 = Tuple'Fused i1 s
414 -- XXX ideally this should be a Continue instead
415 then return $ IPartial s1
416 else IDone <$> extract foldErr s1
421 else IError (foldErr i1)
424 when (low >= 0 && high >= 0 && low > high)
425 $ throwM $ ParseError invalidRange
427 finitial >>= inext (-1)
429 -- Keep the impl same as inext
435 let s1 = Tuple'Fused i1 s
437 then return $ Continue 0 s1
438 else Done 0 <$> extract foldErr s1
443 else Error (foldErr i1)
445 step (Tuple'Fused i s) a = fstep s a >>= snext i
447 extract f (Tuple'Fused i s)
448 | i >= low && i <= high = fextract s
449 | otherwise = throwM $ ParseError (f i)
451-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
456takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
457takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
466 FL.Partial s -> IPartial $ Tuple' 0 s
471 $ "takeEQ: Expecting exactly " ++ show cnt
472 ++ " elements, fold terminated without"
473 ++ " consuming any elements"
480 FL.Partial s -> Continue 0 $ Tuple' i1 s
483 $ "takeEQ: Expecting exactly " ++ show cnt
484 ++ " elements, fold terminated on " ++ show i1
489 FL.Partial s -> fextract s
490 FL.Done b -> return b
491 -- XXX we should not reach here when initial returns Step type
492 -- reachable only when n == 0
493 | otherwise = Done 1 <$> fextract r
500 | i == 0 && cnt == 0 = fextract r
504 $ "takeEQ: Expecting exactly " ++ show cnt
505 ++ " elements, input terminated on " ++ show i
507-- | See 'Streamly.Internal.Data.Parser.takeGE'.
512takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
513takeGE n (Fold fstep finitial fextract) = Parser step initial extract
521 FL.Partial s -> IPartial $ Tuple' 0 s
526 $ "takeGE: Expecting at least " ++ show cnt
527 ++ " elements, fold terminated without"
528 ++ " consuming any elements"
535 FL.Partial s -> Continue 0 $ Tuple' i1 s
538 $ "takeGE: Expecting at least " ++ show cnt
539 ++ " elements, fold terminated on " ++ show i1
544 FL.Partial s -> Partial 0 $ Tuple' i1 s
545 FL.Done b -> Done 0 b
552 | i >= cnt = fextract r
556 $ "takeGE: Expecting at least " ++ show cnt
557 ++ " elements, input terminated on " ++ show i
559-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
563{-# INLINE takeWhile #-}
564takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
565takeWhile predicate (Fold fstep finitial fextract) =
566 Parser step initial fextract
573 FL.Partial s -> IPartial s
582 FL.Partial s1 -> Partial 0 s1
583 FL.Done b -> Done 0 b
584 else Done 1 <$> fextract s
586-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
590{-# INLINE takeWhile1 #-}
591takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
592takeWhile1 predicate (Fold fstep finitial fextract) =
593 Parser step initial extract
600 FL.Partial s -> IPartial (Left s)
603 $ "takeWhile1: fold terminated without consuming:"
606 {-# INLINE process #-}
611 FL.Partial s1 -> Partial 0 (Right s1)
612 FL.Done b -> Done 0 b
617 else return $ Error "takeWhile1: predicate failed on first element"
625 extract (Left _) = throwM $ ParseError "takeWhile1: end of input"
626 extract (Right s) = fextract s
628-- | See 'Streamly.Internal.Data.Parser.sliceSepByP'.
632sliceSepByP :: MonadCatch m =>
633 (a -> Bool) -> Parser m a b -> Parser m a b
634sliceSepByP cond (Parser pstep pinitial pextract) =
636 Parser step initial pextract
649-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
653data SliceBeginWithState s = Left' s | Right' s
655{-# INLINE sliceBeginWith #-}
656sliceBeginWith :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
657sliceBeginWith cond (Fold fstep finitial fextract) =
659 Parser step initial extract
667 FL.Partial s -> IPartial (Left' s)
668 FL.Done _ -> IError "sliceBeginWith : bad finitial"
670 {-# INLINE process #-}
675 FL.Partial s1 -> Partial 0 (Right' s1)
676 FL.Done b -> Done 0 b
681 else error $ "sliceBeginWith : slice begins with an element which "
682 ++ "fails the predicate"
686 else Done 1 <$> fextract s
688 extract (Left' s) = fextract s
689 extract (Right' s) = fextract s
691data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
693-- | See 'Streamly.Internal.Data.Parser.wordBy'.
697wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
698wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
702 {-# INLINE worder #-}
707 FL.Partial s1 -> Partial 0 $ WBWord s1
708 FL.Done b -> Done 0 b
714 FL.Partial s -> IPartial $ WBLeft s
720 else return $ Partial 0 $ WBLeft s
726 return $ Partial 0 $ WBRight b
729 $ if not (predicate a)
731 else Partial 0 $ WBRight b
733 extract (WBLeft s) = fextract s
734 extract (WBWord s) = fextract s
735 extract (WBRight b) = return b
737{-# ANN type GroupByState Fuse #-}
740 | GroupByGrouping !a !s
742-- | See 'Streamly.Internal.Data.Parser.groupBy'.
744{-# INLINE groupBy #-}
745groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
746groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
750 {-# INLINE grouper #-}
755 FL.Done b -> Done 0 b
756 FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
762 FL.Partial s -> IPartial $ GroupByInit s
765 step (GroupByInit s) a = grouper s a a
766 step (GroupByGrouping a0 s) a =
769 else Done 1 <$> fextract s
771 extract (GroupByInit s) = fextract s
772 extract (GroupByGrouping _ s) = fextract s
774-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
776{-# INLINE groupByRolling #-}
777groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
778groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
782 {-# INLINE grouper #-}
787 FL.Done b -> Done 0 b
788 FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
794 FL.Partial s -> IPartial $ GroupByInit s
797 step (GroupByInit s) a = grouper s a
798 step (GroupByGrouping a0 s) a =
801 else Done 1 <$> fextract s
803 extract (GroupByInit s) = fextract s
804 extract (GroupByGrouping _ s) = fextract s
806{-# ANN type GroupByStatePair Fuse #-}
807data GroupByStatePair a s1 s2
808 = GroupByInitPair !s1 !s2
809 | GroupByGroupingPair !a !s1 !s2
810 | GroupByGroupingPairL !a !s1 !s2
811 | GroupByGroupingPairR !a !s1 !s2
813{-# INLINE groupByRollingEither #-}
814groupByRollingEither :: MonadCatch m =>
815 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (Either b c)
818 (Fold fstep1 finitial1 fextract1)
819 (Fold fstep2 finitial2 fextract2) = Parser step initial extract
823 {-# INLINE grouper #-}
825 return $ Continue 0 (GroupByGroupingPair a s1 s2)
827 {-# INLINE grouperL2 #-}
828 grouperL2 s1 s2 a = do
832 FL.Done b -> Done 0 (Left b)
833 FL.Partial s11 -> Partial 0 (GroupByGroupingPairL a s11 s2)
835 {-# INLINE grouperL #-}
836 grouperL s1 s2 a0 a = do
839 FL.Done b -> return $ Done 0 (Left b)
840 FL.Partial s11 -> grouperL2 s11 s2 a
842 {-# INLINE grouperR2 #-}
843 grouperR2 s1 s2 a = do
847 FL.Done b -> Done 0 (Right b)
848 FL.Partial s21 -> Partial 0 (GroupByGroupingPairR a s1 s21)
850 {-# INLINE grouperR #-}
851 grouperR s1 s2 a0 a = do
854 FL.Done b -> return $ Done 0 (Right b)
855 FL.Partial s21 -> grouperR2 s1 s21 a
864 FL.Partial s2 -> IPartial $ GroupByInitPair s1 s2
865 FL.Done b -> IDone (Right b)
866 FL.Done b -> IDone (Left b)
868 step (GroupByInitPair s1 s2) a = grouper s1 s2 a
870 step (GroupByGroupingPair a0 s1 s2) a =
872 then grouperL s1 s2 a0 a
873 else grouperR s1 s2 a0 a
875 step (GroupByGroupingPairL a0 s1 s2) a =
877 then grouperL2 s1 s2 a
878 else Done 1 . Left <$> fextract1 s1
880 step (GroupByGroupingPairR a0 s1 s2) a =
882 then grouperR2 s1 s2 a
883 else Done 1 . Right <$> fextract2 s2
885 extract (GroupByInitPair s1 _) = Left <$> fextract1 s1
886 extract (GroupByGroupingPairL _ s1 _) = Left <$> fextract1 s1
887 extract (GroupByGroupingPairR _ _ s2) = Right <$> fextract2 s2
888 extract (GroupByGroupingPair a s1 _) = do
891 FL.Done b -> return $ Left b
892 FL.Partial s11 -> Left <$> fextract1 s11
894-- XXX use an Unfold instead of a list?
895-- XXX custom combinators for matching list, array and stream?
897-- | See 'Streamly.Internal.Data.Parser.eqBy'.
902eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
903eqBy cmp str = Parser step initial extract
907 initial = return $ IPartial str
909 step [] _ = return $ Done 0 ()
914 else Error "eqBy: failed, yet to match the last element"
920 $ "eqBy: failed, yet to match "
921 ++ show (length xs + 1) ++ " elements"
926 $ "eqBy: end of input, yet to match "
927 ++ show (length xs) ++ " elements"
929--------------------------------------------------------------------------------
931--------------------------------------------------------------------------------
933-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the
934-- input as long as the predicate @p@ is 'True'. @f2@ consumes the rest of the
938-- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs
940-- > span_ (< 1) [1,2,3]
943-- > span_ (< 2) [1,2,3]
946-- > span_ (< 4) [1,2,3]
953span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
954span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)
956-- | Break the input stream into two groups, the first group takes the input as
957-- long as the predicate applied to the first element of the stream and next
958-- input element holds 'True', the second group takes the rest of the input.
965 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
966spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)
968-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
969-- predicate is applied to the previous and the next input elements.
972{-# INLINE spanByRolling #-}
975 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
976spanByRolling eq f1 f2 =
977 noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)
979-------------------------------------------------------------------------------
981-------------------------------------------------------------------------------
983-- | See 'Streamly.Internal.Data.Parser.takeP'.
987takeP :: Monad m => Int -> Parser m a b -> Parser m a b
988takeP lim (Parser pstep pinitial pextract) = Parser step initial extract
997 then return $ IPartial $ Tuple' 0 s
998 else IDone <$> pextract s
999 IDone b -> return $ IDone b
1000 IError e -> return $ IError e
1002 step (Tuple' cnt r) a = do
1003 assert (cnt < lim) (return ())
1008 assert (cnt1 >= 0) (return ())
1010 then return $ Partial 0 $ Tuple' cnt1 s
1011 else Done 0 <$> pextract s
1013 assert (cnt1 >= 0) (return ())
1015 then return $ Continue 0 $ Tuple' cnt1 s
1016 -- XXX This should error out?
1017 -- If designed properly, this will probably error out.
1018 -- "pextract" should error out
1022 -- This is a tricky case, we have the following options:
1023 -- 1. Done 0 with extract as you have written
1024 -- 2. Done n, will require buffering elements
1025 -- 3. Use a backtracking fold and not a parser, once we have
1026 -- backtracking in folds
1027 else Done 0 <$> pextract s
1029 let taken = cnt1 - n
1030 assert (taken >= 0) (return ())
1031 return $ Partial n $ Tuple' taken s
1033 let taken = cnt1 - n
1034 assert (taken >= 0) (return ())
1035 return $ Continue n $ Tuple' taken s
1036 Done n b -> return $ Done n b
1037 Error str -> return $ Error str
1039 extract (Tuple' _ r) = pextract r
1041-- | See 'Streamly.Internal.Data.Parser.lookahead'.
1045{-# INLINE lookAhead #-}
1046lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
1047lookAhead (Parser step1 initial1 _) = Parser step initial extract
1053 return $ case res of
1054 IPartial s -> IPartial (Tuple' 0 s)
1056 IError e -> IError e
1058 step (Tuple' cnt st) a = do
1063 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
1064 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
1065 Done _ b -> Done cnt1 b
1066 Error err -> Error err
1068 -- XXX returning an error let's us backtrack. To implement it in a way so
1069 -- that it terminates on eof without an error then we need a way to
1070 -- backtrack on eof, that will require extract to return 'Step' type.
1071 extract (Tuple' n _) =
1074 $ "lookAhead: end of input after consuming "
1075 ++ show n ++ " elements"
1077-------------------------------------------------------------------------------
1079-------------------------------------------------------------------------------
1081-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
1085{-# INLINE deintercalate #-}
1088 Fold m a y -> Parser m x a
1089 -> Fold m b z -> Parser m x b
1090 -> Parser m x (y, z)
1091deintercalate = undefined
1093-------------------------------------------------------------------------------
1094-- Sequential Collection
1095-------------------------------------------------------------------------------
1097-- | See 'Streamly.Internal.Data.Parser.sequence'.
1101{-# INLINE sequence #-}
1104 Fold m b c -> t (Parser m a b) -> Parser m a c
1105sequence _f _p = undefined
1107-------------------------------------------------------------------------------
1108-- Alternative Collection
1109-------------------------------------------------------------------------------
1111-- | See 'Streamly.Internal.Data.Parser.choice'.
1115{-# INLINE choice #-}
1116choice :: (MonadCatch m, Foldable t) => t (Parser m a b) -> Parser m a b
1117choice = foldl1 shortest
1119-------------------------------------------------------------------------------
1120-- Sequential Repetition
1121-------------------------------------------------------------------------------
1123-- | See 'Streamly.Internal.Data.Parser.many'.
1128many :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1130-- many = countBetween 0 maxBound
1132-- | See 'Streamly.Internal.Data.Parser.some'.
1137some :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1139-- some f p = many (takeGE 1 f) p
1140-- many = countBetween 1 maxBound
1142-- | See 'Streamly.Internal.Data.Parser.countBetween'.
1146{-# INLINE countBetween #-}
1149 Int -> Int -> Parser m a b -> Fold m b c -> Parser m a c
1150countBetween _m _n _p = undefined
1151-- countBetween m n p f = many (takeBetween m n f) p
1153-- | See 'Streamly.Internal.Data.Parser.count'.
1160 Int -> Parser m a b -> Fold m b c -> Parser m a c
1161count n = countBetween n n
1162-- count n f p = many (takeEQ n f) p
1164data ManyTillState fs sr sl
1165 = ManyTillR Int fs sr
1166 | ManyTillL Int fs sl
1168-- | See 'Streamly.Internal.Data.Parser.manyTill'.
1172{-# INLINE manyTill #-}
1173manyTill :: MonadCatch m
1174 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
1175manyTill (Fold fstep finitial fextract)
1176 (Parser stepL initialL extractL)
1177 (Parser stepR initialR _) =
1178 Parser step initial extract
1182 -- Caution: Mutual recursion
1184 scrutL fs p c d e = do
1187 IPartial sl -> return $ c (ManyTillL 0 fs sl)
1191 FL.Partial fs1 -> scrutR fs1 p c d e
1192 FL.Done fb -> return $ d fb
1193 IError err -> return $ e err
1195 scrutR fs p c d e = do
1198 IPartial sr -> return $ p (ManyTillR 0 fs sr)
1199 IDone _ -> d <$> fextract fs
1200 IError _ -> scrutL fs p c d e
1205 FL.Partial fs -> scrutR fs IPartial IPartial IDone IError
1206 FL.Done b -> return $ IDone b
1208 step (ManyTillR cnt fs st) a = do
1211 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
1213 assert (cnt + 1 - n >= 0) (return ())
1214 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
1222 return $ Continue (cnt + 1) (ManyTillL 0 fs sl)
1230 FL.Partial fs1 -> scrutR fs1 p c d Error
1231 FL.Done fb -> return $ Done cnt1 fb
1232 IError err -> return $ Error err
1233 -- XXX the cnt is being used only by the assert
1234 step (ManyTillL cnt fs st) a = do
1237 Partial n s -> return $ Partial n (ManyTillL 0 fs s)
1239 assert (cnt + 1 - n >= 0) (return ())
1240 return $ Continue n (ManyTillL (cnt + 1 - n) fs s)
1245 scrutR s (Partial n) (Continue n) (Done n) Error
1246 FL.Done b1 -> return $ Done n b1
1247 Error err -> return $ Error err
1249 extract (ManyTillL _ fs sR) = do
1250 res <- extractL sR >>= fstep fs
1252 FL.Partial s -> fextract s
1253 FL.Done b -> return b
1254 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