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
51 -- Parsers chained in series, if one parser terminates the composition
52 -- terminates. Currently we are using folds to collect the output of the
53 -- parsers but we can use Parsers instead of folds to make the composition
54 -- more powerful. For example, we can do:
56 -- sliceSepByMax cond n p = sliceBy cond (take n p)
57 -- sliceSepByBetween cond m n p = sliceBy cond (takeBetween m n p)
58 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
60 -- Grab a sequence of input elements without inspecting them
62 -- , take -- take -- takeBetween 0 n
63 -- , takeLE1 -- take1 -- takeBetween 1 n
64 , takeEQ -- takeBetween n n
65 , takeGE -- takeBetween n maxBound
68 -- Grab a sequence of input elements by inspecting them
75 -- , sliceSepByBetween
83 , groupByRollingEither
88 -- , prefixOf -- match any prefix of a given string
89 -- , suffixOf -- match any suffix of a given string
90 -- , infixOf -- match any substring of a given string
97 -- Second order parsers (parsers using parsers)
98 -- * Binary Combinators
100 -- ** Sequential Applicative
104 -- ** Parallel Applicatives
108 -- , teeTill -- like manyTill but parallel
110 -- ** Sequential Interleaving
111 -- Use two folds, run a primary parser, its rejected values go to the
115 -- ** Sequential Alternative
118 -- ** Parallel Alternatives
123 -- * N-ary Combinators
124 -- ** Sequential Collection
128 -- ** Sequential Repetition
131 -- , countBetweenTill
137 -- -- ** Special cases
138 -- XXX traditional implmentations of these may be of limited use. For
139 -- example, consider parsing lines separated by "\r\n". The main parser
140 -- will have to detect and exclude the sequence "\r\n" anyway so that we
141 -- can apply the "sep" parser.
143 -- We can instead implement these as special cases of deintercalate.
154 -- A simple and stupid impl would be to just convert the stream to an array
155 -- and give the array reference to all consumers. The array can be grown on
156 -- demand by any consumer and truncated when nonbody needs it.
158 -- -- ** Distribute to collection
159 -- -- ** Distribute to repetition
161 -- -- ** Interleaved collection
164 -- -- ** Interleaved repetition
165 -- repeat one parser and when it fails run an error recovery parser
166 -- e.g. to find a key frame in the stream after an error
168 -- ** Collection of Alternatives
171 -- , fastestN -- first N successful in time
172 -- , choiceN -- first N successful in position
173 , choice -- first successful in position
175 -- -- ** Repeated Alternatives
176 -- , retryMax -- try N times
177 -- , retryUntil -- try until successful
178 -- , retryUntilN -- try until successful n times
189import Control.Exception (assert, Exception)
190import Control.Monad (when)
191import Control.Monad.Catch (MonadCatch, MonadThrow(..))
192import Fusion.Plugin.Types (Fuse(..))
193import Streamly.Internal.Data.Fold.Type (Fold(..))
194import Streamly.Internal.Data.SVar.Type (defState)
195import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
197import qualified Streamly.Internal.Data.Fold.Type as FL
198import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
199import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
202 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span
204import Streamly.Internal.Data.Parser.ParserD.Tee
205import Streamly.Internal.Data.Parser.ParserD.Type
210-- >>> :set -package streamly
211-- >>> import Prelude hiding ()
212-- >>> import qualified Streamly.Prelude as Stream
213-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
214-- >>> import qualified Streamly.Data.Fold as Fold
215-- >>> import qualified Streamly.Internal.Data.Parser as Parser
217-------------------------------------------------------------------------------
218-- Downgrade a parser to a Fold
219-------------------------------------------------------------------------------
221data ParserToFoldError =
229instance Exception ParserToFoldError
231-- | See 'Streamly.Internal.Data.Parser.toFold'.
236toFold :: MonadThrow m => Parser m a b -> Fold m a b
237toFold (Parser pstep pinitial pextract) = Fold step initial pextract
244 IPartial s -> return $ FL.Partial s
245 IDone b -> return $ FL.Done b
246 IError err -> throwM $ InitialError err
251 Partial 0 s -> return $ FL.Partial s
252 Continue 0 s -> return $ FL.Partial s
253 Done 0 b -> return $ FL.Done b
254 Partial n _ -> throwM $ PartialError n
255 Continue n _ -> throwM $ ContinueError n
256 Done n _ -> throwM $ DoneError n
257 Error err -> throwM $ ErrorError err
259-------------------------------------------------------------------------------
260-- Upgrade folds to parses
261-------------------------------------------------------------------------------
263-- | See 'Streamly.Internal.Data.Parser.fromFold'.
267{-# INLINE fromFold #-}
268fromFold :: Monad m => Fold m a b -> Parser m a b
269fromFold (Fold fstep finitial fextract) = Parser step initial fextract
277 FL.Partial s1 -> IPartial s1
284 FL.Partial s1 -> Partial 0 s1
285 FL.Done b -> Done 0 b
287-------------------------------------------------------------------------------
289-------------------------------------------------------------------------------
291-- | See 'Streamly.Internal.Data.Parser.peek'.
296peek :: MonadThrow m => Parser m a a
297peek = Parser step initial extract
301 initial = return $ IPartial ()
303 step () a = return $ Done 1 a
305 extract () = throwM $ ParseError "peek: end of input"
307-- | See 'Streamly.Internal.Data.Parser.eof'.
312eof :: Monad m => Parser m a ()
313eof = Parser step initial return
317 initial = return $ IPartial ()
319 step () _ = return $ Error "eof: not at end of input"
321-- | See 'Streamly.Internal.Data.Parser.satisfy'.
325{-# INLINE satisfy #-}
326satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
327satisfy predicate = Parser step initial extract
331 initial = return $ IPartial ()
336 else Error "satisfy: predicate failed"
338 extract _ = throwM $ ParseError "satisfy: end of input"
340-- | See 'Streamly.Internal.Data.Parser.next'.
345next :: Monad m => Parser m a (Maybe a)
346next = Parser step initial extract
348 initial = pure $ IPartial ()
349 step _ a = pure $ Done 0 (Just a)
350 extract _ = pure Nothing
352-- | See 'Streamly.Internal.Data.Parser.maybe'.
357maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
358maybe parserF = Parser step initial extract
362 initial = return $ IPartial ()
367 Nothing -> Error "maybe: predicate failed"
369 extract _ = throwM $ ParseError "maybe: end of input"
371-- | See 'Streamly.Internal.Data.Parser.either'.
376either :: MonadThrow m => (a -> Either String b) -> Parser m a b
377either parserF = Parser step initial extract
381 initial = return $ IPartial ()
386 Left err -> Error $ "either: " ++ err
388 extract _ = throwM $ ParseError "either: end of input"
390-------------------------------------------------------------------------------
392-------------------------------------------------------------------------------
394-- Required to fuse "take" with "many" in "chunksOf", for ghc-9.x
395{-# ANN type Tuple'Fused Fuse #-}
396data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
398-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
402{-# INLINE takeBetween #-}
403takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
404takeBetween low high (Fold fstep finitial fextract) =
406 Parser step initial (extract streamErr)
411 "takeBetween: Expecting alteast " ++ show low
412 ++ " elements, got " ++ show i
415 "takeBetween: lower bound - " ++ show low
416 ++ " is greater than higher bound - " ++ show high
418 foldErr :: Int -> String
420 "takeBetween: the collecting fold terminated after"
421 ++ " consuming" ++ show i ++ " elements"
422 ++ " minimum" ++ show low ++ " elements needed"
424 -- Exactly the same as snext except different constructors, we can possibly
425 -- deduplicate the two.
431 let s1 = Tuple'Fused i1 s
433 -- XXX ideally this should be a Continue instead
434 then return $ IPartial s1
435 else IDone <$> extract foldErr s1
440 else IError (foldErr i1)
443 when (low >= 0 && high >= 0 && low > high)
444 $ throwM $ ParseError invalidRange
446 finitial >>= inext (-1)
448 -- Keep the impl same as inext
454 let s1 = Tuple'Fused i1 s
456 then return $ Continue 0 s1
457 else Done 0 <$> extract foldErr s1
462 else Error (foldErr i1)
464 step (Tuple'Fused i s) a = fstep s a >>= snext i
466 extract f (Tuple'Fused i s)
467 | i >= low && i <= high = fextract s
468 | otherwise = throwM $ ParseError (f i)
470-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
475takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
476takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
485 FL.Partial s -> IPartial $ Tuple' 0 s
490 $ "takeEQ: Expecting exactly " ++ show cnt
491 ++ " elements, fold terminated without"
492 ++ " consuming any elements"
499 FL.Partial s -> Continue 0 $ Tuple' i1 s
502 $ "takeEQ: Expecting exactly " ++ show cnt
503 ++ " elements, fold terminated on " ++ show i1
508 FL.Partial s -> fextract s
509 FL.Done b -> return b
510 -- XXX we should not reach here when initial returns Step type
511 -- reachable only when n == 0
512 | otherwise = Done 1 <$> fextract r
519 | i == 0 && cnt == 0 = fextract r
523 $ "takeEQ: Expecting exactly " ++ show cnt
524 ++ " elements, input terminated on " ++ show i
526-- | See 'Streamly.Internal.Data.Parser.takeGE'.
531takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
532takeGE n (Fold fstep finitial fextract) = Parser step initial extract
540 FL.Partial s -> IPartial $ Tuple' 0 s
545 $ "takeGE: Expecting at least " ++ show cnt
546 ++ " elements, fold terminated without"
547 ++ " consuming any elements"
554 FL.Partial s -> Continue 0 $ Tuple' i1 s
557 $ "takeGE: Expecting at least " ++ show cnt
558 ++ " elements, fold terminated on " ++ show i1
563 FL.Partial s -> Partial 0 $ Tuple' i1 s
564 FL.Done b -> Done 0 b
571 | i >= cnt = fextract r
575 $ "takeGE: Expecting at least " ++ show cnt
576 ++ " elements, input terminated on " ++ show i
578-------------------------------------------------------------------------------
579-- Conditional splitting
580-------------------------------------------------------------------------------
582-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
586{-# INLINE takeWhile #-}
587takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
588takeWhile predicate (Fold fstep finitial fextract) =
589 Parser step initial fextract
596 FL.Partial s -> IPartial s
605 FL.Partial s1 -> Partial 0 s1
606 FL.Done b -> Done 0 b
607 else Done 1 <$> fextract s
609-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
613{-# INLINE takeWhile1 #-}
614takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
615takeWhile1 predicate (Fold fstep finitial fextract) =
616 Parser step initial extract
623 FL.Partial s -> IPartial (Left s)
626 $ "takeWhile1: fold terminated without consuming:"
629 {-# INLINE process #-}
634 FL.Partial s1 -> Partial 0 (Right s1)
635 FL.Done b -> Done 0 b
640 else return $ Error "takeWhile1: predicate failed on first element"
648 extract (Left _) = throwM $ ParseError "takeWhile1: end of input"
649 extract (Right s) = fextract s
651-------------------------------------------------------------------------------
653-------------------------------------------------------------------------------
655-- | See 'Streamly.Internal.Data.Parser.sliceSepByP'.
659sliceSepByP :: MonadCatch m =>
660 (a -> Bool) -> Parser m a b -> Parser m a b
661sliceSepByP cond (Parser pstep pinitial pextract) =
663 Parser step initial pextract
676-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
680data SliceBeginWithState s = Left' s | Right' s
682{-# INLINE sliceBeginWith #-}
683sliceBeginWith :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
684sliceBeginWith cond (Fold fstep finitial fextract) =
686 Parser step initial extract
694 FL.Partial s -> IPartial (Left' s)
695 FL.Done _ -> IError "sliceBeginWith : bad finitial"
697 {-# INLINE process #-}
702 FL.Partial s1 -> Partial 0 (Right' s1)
703 FL.Done b -> Done 0 b
708 else error $ "sliceBeginWith : slice begins with an element which "
709 ++ "fails the predicate"
713 else Done 1 <$> fextract s
715 extract (Left' s) = fextract s
716 extract (Right' s) = fextract s
718-------------------------------------------------------------------------------
720-------------------------------------------------------------------------------
722data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
724-- | See 'Streamly.Internal.Data.Parser.wordBy'.
728wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
729wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
733 {-# INLINE worder #-}
738 FL.Partial s1 -> Partial 0 $ WBWord s1
739 FL.Done b -> Done 0 b
745 FL.Partial s -> IPartial $ WBLeft s
751 else return $ Partial 0 $ WBLeft s
757 return $ Partial 0 $ WBRight b
760 $ if not (predicate a)
762 else Partial 0 $ WBRight b
764 extract (WBLeft s) = fextract s
765 extract (WBWord s) = fextract s
766 extract (WBRight b) = return b
768{-# ANN type GroupByState Fuse #-}
771 | GroupByGrouping !a !s
773-- | See 'Streamly.Internal.Data.Parser.groupBy'.
775{-# INLINE groupBy #-}
776groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
777groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
781 {-# INLINE grouper #-}
786 FL.Done b -> Done 0 b
787 FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
793 FL.Partial s -> IPartial $ GroupByInit s
796 step (GroupByInit s) a = grouper s a a
797 step (GroupByGrouping a0 s) a =
800 else Done 1 <$> fextract s
802 extract (GroupByInit s) = fextract s
803 extract (GroupByGrouping _ s) = fextract s
805-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
807{-# INLINE groupByRolling #-}
808groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
809groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
813 {-# INLINE grouper #-}
818 FL.Done b -> Done 0 b
819 FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
825 FL.Partial s -> IPartial $ GroupByInit s
828 step (GroupByInit s) a = grouper s a
829 step (GroupByGrouping a0 s) a =
832 else Done 1 <$> fextract s
834 extract (GroupByInit s) = fextract s
835 extract (GroupByGrouping _ s) = fextract s
837{-# ANN type GroupByStatePair Fuse #-}
838data GroupByStatePair a s1 s2
839 = GroupByInitPair !s1 !s2
840 | GroupByGroupingPair !a !s1 !s2
841 | GroupByGroupingPairL !a !s1 !s2
842 | GroupByGroupingPairR !a !s1 !s2
844{-# INLINE groupByRollingEither #-}
845groupByRollingEither :: MonadCatch m =>
846 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (Either b c)
849 (Fold fstep1 finitial1 fextract1)
850 (Fold fstep2 finitial2 fextract2) = Parser step initial extract
854 {-# INLINE grouper #-}
856 return $ Continue 0 (GroupByGroupingPair a s1 s2)
858 {-# INLINE grouperL2 #-}
859 grouperL2 s1 s2 a = do
863 FL.Done b -> Done 0 (Left b)
864 FL.Partial s11 -> Partial 0 (GroupByGroupingPairL a s11 s2)
866 {-# INLINE grouperL #-}
867 grouperL s1 s2 a0 a = do
870 FL.Done b -> return $ Done 0 (Left b)
871 FL.Partial s11 -> grouperL2 s11 s2 a
873 {-# INLINE grouperR2 #-}
874 grouperR2 s1 s2 a = do
878 FL.Done b -> Done 0 (Right b)
879 FL.Partial s21 -> Partial 0 (GroupByGroupingPairR a s1 s21)
881 {-# INLINE grouperR #-}
882 grouperR s1 s2 a0 a = do
885 FL.Done b -> return $ Done 0 (Right b)
886 FL.Partial s21 -> grouperR2 s1 s21 a
895 FL.Partial s2 -> IPartial $ GroupByInitPair s1 s2
896 FL.Done b -> IDone (Right b)
897 FL.Done b -> IDone (Left b)
899 step (GroupByInitPair s1 s2) a = grouper s1 s2 a
901 step (GroupByGroupingPair a0 s1 s2) a =
903 then grouperL s1 s2 a0 a
904 else grouperR s1 s2 a0 a
906 step (GroupByGroupingPairL a0 s1 s2) a =
908 then grouperL2 s1 s2 a
909 else Done 1 . Left <$> fextract1 s1
911 step (GroupByGroupingPairR a0 s1 s2) a =
913 then grouperR2 s1 s2 a
914 else Done 1 . Right <$> fextract2 s2
916 extract (GroupByInitPair s1 _) = Left <$> fextract1 s1
917 extract (GroupByGroupingPairL _ s1 _) = Left <$> fextract1 s1
918 extract (GroupByGroupingPairR _ _ s2) = Right <$> fextract2 s2
919 extract (GroupByGroupingPair a s1 _) = do
922 FL.Done b -> return $ Left b
923 FL.Partial s11 -> Left <$> fextract1 s11
925-- XXX use an Unfold instead of a list?
926-- XXX custom combinators for matching list, array and stream?
927-- XXX rename to listBy?
929-- | See 'Streamly.Internal.Data.Parser.eqBy'.
934eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
935eqBy cmp str = Parser step initial extract
939 -- XXX Should return IDone in initial for [] case
940 initial = return $ IPartial str
942 step [] _ = return $ Done 0 ()
947 else Error "eqBy: failed, yet to match the last element"
953 $ "eqBy: failed, yet to match "
954 ++ show (length xs + 1) ++ " elements"
959 $ "eqBy: end of input, yet to match "
960 ++ show (length xs) ++ " elements"
962-- XXX rename to streamBy?
963-- | Like eqBy but uses a stream instead of a list
964{-# INLINE matchBy #-}
965matchBy :: MonadThrow m => (a -> a -> Bool) -> D.Stream m a -> Parser m a ()
966matchBy cmp (D.Stream sstep state) = Parser step initial extract
971 r <- sstep defState state
973 D.Yield x s -> return $ IPartial (Just x, s)
974 D.Stop -> return $ IDone ()
975 -- Need Skip/Continue in initial to loop right here
976 D.Skip s -> return $ IPartial (Nothing, s)
978 step (Just x, st) a =
981 r <- sstep defState st
984 D.Yield x1 s -> Continue 0 (Just x1, s)
986 D.Skip s -> Continue 1 (Nothing, s)
987 else return $ Error "match: mismtach occurred"
988 step (Nothing, st) a = do
989 r <- sstep defState st
994 then Continue 0 (Nothing, s)
995 else Error "match: mismatch occurred"
997 D.Skip s -> Continue 1 (Nothing, s)
999 extract _ = throwM $ ParseError "match: end of input"
1001{-# INLINE zipWithM #-}
1002zipWithM :: MonadThrow m =>
1003 (a -> b -> m c) -> D.Stream m a -> Fold m c x -> Parser m b x
1004zipWithM zf (D.Stream sstep state) (Fold fstep finitial fextract) =
1005 Parser step initial extract
1013 r <- sstep defState state
1015 D.Yield x s -> return $ IPartial (Just x, s, fs)
1019 -- Need Skip/Continue in initial to loop right here
1020 D.Skip s -> return $ IPartial (Nothing, s, fs)
1021 FL.Done x -> return $ IDone x
1023 step (Just a, st, fs) b = do
1027 FL.Partial fs1 -> do
1028 r <- sstep defState st
1030 D.Yield x1 s -> return $ Continue 0 (Just x1, s, fs1)
1034 D.Skip s -> return $ Continue 1 (Nothing, s, fs1)
1035 FL.Done x -> return $ Done 0 x
1036 step (Nothing, st, fs) b = do
1037 r <- sstep defState st
1043 FL.Partial fs1 -> return $ Continue 0 (Nothing, s, fs1)
1044 FL.Done x -> return $ Done 0 x
1048 D.Skip s -> return $ Continue 1 (Nothing, s, fs)
1050 extract _ = throwM $ ParseError "zipWithM: end of input"
1052-- | Zip the input of a fold with a stream.
1057zip :: MonadThrow m => D.Stream m a -> Fold m (a, b) x -> Parser m b x
1058zip = zipWithM (curry return)
1060-- | Pair each element of a fold input with its index, starting from index 0.
1063{-# INLINE indexed #-}
1064indexed :: forall m a b. MonadThrow m => Fold m (Int, a) b -> Parser m a b
1065indexed = zip (D.enumerateFromIntegral 0 :: D.Stream m Int)
1067-- | @makeIndexFilter indexer filter predicate@ generates a fold filtering
1068-- function using a fold indexing function that attaches an index to each input
1069-- element and a filtering function that filters using @(index, element) ->
1070-- Bool) as predicate.
1075-- filterWithIndex = makeIndexFilter indexed filter
1076-- filterWithAbsTime = makeIndexFilter timestamped filter
1077-- filterWithRelTime = makeIndexFilter timeIndexed filter
1081{-# INLINE makeIndexFilter #-}
1083 (Fold m (s, a) b -> Parser m a b)
1084 -> (((s, a) -> Bool) -> Fold m (s, a) b -> Fold m (s, a) b)
1085 -> (((s, a) -> Bool) -> Fold m a b -> Parser m a b)
1086makeIndexFilter f comb g = f . comb g . FL.lmap snd
1088-- | @sampleFromthen offset stride@ samples the element at @offset@ index and
1089-- then every element at strides of @stride@.
1092{-# INLINE sampleFromthen #-}
1093sampleFromthen :: MonadThrow m => Int -> Int -> Fold m a b -> Parser m a b
1094sampleFromthen offset size =
1095 makeIndexFilter indexed FL.filter (\(i, _) -> (i + offset) `mod` size == 0)
1097--------------------------------------------------------------------------------
1099--------------------------------------------------------------------------------
1101-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the
1102-- input as long as the predicate @p@ is 'True'. @f2@ consumes the rest of the
1106-- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs
1108-- > span_ (< 1) [1,2,3]
1111-- > span_ (< 2) [1,2,3]
1114-- > span_ (< 4) [1,2,3]
1121span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1122span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)
1124-- | Break the input stream into two groups, the first group takes the input as
1125-- long as the predicate applied to the first element of the stream and next
1126-- input element holds 'True', the second group takes the rest of the input.
1130{-# INLINE spanBy #-}
1133 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1134spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)
1136-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
1137-- predicate is applied to the previous and the next input elements.
1140{-# INLINE spanByRolling #-}
1143 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1144spanByRolling eq f1 f2 =
1145 noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)
1147-------------------------------------------------------------------------------
1149-------------------------------------------------------------------------------
1151-- | See 'Streamly.Internal.Data.Parser.takeP'.
1155takeP :: Monad m => Int -> Parser m a b -> Parser m a b
1156takeP lim (Parser pstep pinitial pextract) = Parser step initial extract
1165 then return $ IPartial $ Tuple' 0 s
1166 else IDone <$> pextract s
1167 IDone b -> return $ IDone b
1168 IError e -> return $ IError e
1170 step (Tuple' cnt r) a = do
1171 assert (cnt < lim) (return ())
1176 assert (cnt1 >= 0) (return ())
1178 then return $ Partial 0 $ Tuple' cnt1 s
1179 else Done 0 <$> pextract s
1181 assert (cnt1 >= 0) (return ())
1183 then return $ Continue 0 $ Tuple' cnt1 s
1184 -- XXX This should error out?
1185 -- If designed properly, this will probably error out.
1186 -- "pextract" should error out
1190 -- This is a tricky case, we have the following options:
1191 -- 1. Done 0 with extract as you have written
1192 -- 2. Done n, will require buffering elements
1193 -- 3. Use a backtracking fold and not a parser, once we have
1194 -- backtracking in folds
1195 else Done 0 <$> pextract s
1197 let taken = cnt1 - n
1198 assert (taken >= 0) (return ())
1199 return $ Partial n $ Tuple' taken s
1201 let taken = cnt1 - n
1202 assert (taken >= 0) (return ())
1203 return $ Continue n $ Tuple' taken s
1204 Done n b -> return $ Done n b
1205 Error str -> return $ Error str
1207 extract (Tuple' _ r) = pextract r
1209-- | See 'Streamly.Internal.Data.Parser.lookahead'.
1213{-# INLINE lookAhead #-}
1214lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
1215lookAhead (Parser step1 initial1 _) = Parser step initial extract
1221 return $ case res of
1222 IPartial s -> IPartial (Tuple' 0 s)
1224 IError e -> IError e
1226 step (Tuple' cnt st) a = do
1231 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
1232 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
1233 Done _ b -> Done cnt1 b
1234 Error err -> Error err
1236 -- XXX returning an error let's us backtrack. To implement it in a way so
1237 -- that it terminates on eof without an error then we need a way to
1238 -- backtrack on eof, that will require extract to return 'Step' type.
1239 extract (Tuple' n _) =
1242 $ "lookAhead: end of input after consuming "
1243 ++ show n ++ " elements"
1245-------------------------------------------------------------------------------
1247-------------------------------------------------------------------------------
1249-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
1253{-# INLINE deintercalate #-}
1256 Fold m a y -> Parser m x a
1257 -> Fold m b z -> Parser m x b
1258 -> Parser m x (y, z)
1259deintercalate = undefined
1261-------------------------------------------------------------------------------
1262-- Sequential Collection
1263-------------------------------------------------------------------------------
1265-- | See 'Streamly.Internal.Data.Parser.sequence'.
1269{-# INLINE sequence #-}
1272 Fold m b c -> t (Parser m a b) -> Parser m a c
1273sequence _f _p = undefined
1275-------------------------------------------------------------------------------
1276-- Alternative Collection
1277-------------------------------------------------------------------------------
1279-- | See 'Streamly.Internal.Data.Parser.choice'.
1283{-# INLINE choice #-}
1284choice :: (MonadCatch m, Foldable t) => t (Parser m a b) -> Parser m a b
1285choice = foldl1 shortest
1287-------------------------------------------------------------------------------
1288-- Sequential Repetition
1289-------------------------------------------------------------------------------
1291-- | See 'Streamly.Internal.Data.Parser.many'.
1296many :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1298-- many = countBetween 0 maxBound
1300-- | See 'Streamly.Internal.Data.Parser.some'.
1305some :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1307-- some f p = many (takeGE 1 f) p
1308-- many = countBetween 1 maxBound
1310-- | See 'Streamly.Internal.Data.Parser.countBetween'.
1314{-# INLINE countBetween #-}
1317 Int -> Int -> Parser m a b -> Fold m b c -> Parser m a c
1318countBetween _m _n _p = undefined
1319-- countBetween m n p f = many (takeBetween m n f) p
1321-- | See 'Streamly.Internal.Data.Parser.count'.
1328 Int -> Parser m a b -> Fold m b c -> Parser m a c
1329count n = countBetween n n
1330-- count n f p = many (takeEQ n f) p
1332data ManyTillState fs sr sl
1333 = ManyTillR Int fs sr
1334 | ManyTillL Int fs sl
1336-- | See 'Streamly.Internal.Data.Parser.manyTill'.
1340{-# INLINE manyTill #-}
1341manyTill :: MonadCatch m
1342 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
1343manyTill (Fold fstep finitial fextract)
1344 (Parser stepL initialL extractL)
1345 (Parser stepR initialR _) =
1346 Parser step initial extract
1350 -- Caution: Mutual recursion
1352 scrutL fs p c d e = do
1355 IPartial sl -> return $ c (ManyTillL 0 fs sl)
1359 FL.Partial fs1 -> scrutR fs1 p c d e
1360 FL.Done fb -> return $ d fb
1361 IError err -> return $ e err
1363 scrutR fs p c d e = do
1366 IPartial sr -> return $ p (ManyTillR 0 fs sr)
1367 IDone _ -> d <$> fextract fs
1368 IError _ -> scrutL fs p c d e
1373 FL.Partial fs -> scrutR fs IPartial IPartial IDone IError
1374 FL.Done b -> return $ IDone b
1376 step (ManyTillR cnt fs st) a = do
1379 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
1381 assert (cnt + 1 - n >= 0) (return ())
1382 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
1390 return $ Continue (cnt + 1) (ManyTillL 0 fs sl)
1398 FL.Partial fs1 -> scrutR fs1 p c d Error
1399 FL.Done fb -> return $ Done cnt1 fb
1400 IError err -> return $ Error err
1401 -- XXX the cnt is being used only by the assert
1402 step (ManyTillL cnt fs st) a = do
1405 Partial n s -> return $ Partial n (ManyTillL 0 fs s)
1407 assert (cnt + 1 - n >= 0) (return ())
1408 return $ Continue n (ManyTillL (cnt + 1 - n) fs s)
1413 scrutR s (Partial n) (Continue n) (Done n) Error
1414 FL.Done b1 -> return $ Done n b1
1415 Error err -> return $ Error err
1417 extract (ManyTillL _ fs sR) = do
1418 res <- extractL sR >>= fstep fs
1420 FL.Partial s -> fextract s
1421 FL.Done b -> return b
1422 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