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
53 -- Parsers chained in series, if one parser terminates the composition
54 -- terminates. Currently we are using folds to collect the output of the
55 -- parsers but we can use Parsers instead of folds to make the composition
56 -- more powerful. For example, we can do:
58 -- takeEndByOrMax cond n p = takeEndBy cond (take n p)
59 -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)
60 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
62 -- Grab a sequence of input elements without inspecting them
64 -- , take -- take -- takeBetween 0 n
65 , takeEQ -- takeBetween n n
66 , takeGE -- takeBetween n maxBound
67 -- , takeGE1 -- take1 -- takeBetween 1 n
70 -- Grab a sequence of input elements by inspecting them
91 , groupByRollingEither
96 -- , prefixOf -- match any prefix of a given string
97 -- , suffixOf -- match any suffix of a given string
98 -- , infixOf -- match any substring of a given string
105 -- Second order parsers (parsers using parsers)
106 -- * Binary Combinators
108 -- ** Sequential Applicative
112 -- ** Parallel Applicatives
116 -- , teeTill -- like manyTill but parallel
118 -- ** Sequential Interleaving
119 -- Use two folds, run a primary parser, its rejected values go to the
124 -- ** Sequential Alternative
127 -- ** Parallel Alternatives
132 -- * N-ary Combinators
133 -- ** Sequential Collection
137 -- ** Sequential Repetition
140 -- , countBetweenTill
147 -- -- ** Special cases
148 -- XXX traditional implmentations of these may be of limited use. For
149 -- example, consider parsing lines separated by "\r\n". The main parser
150 -- will have to detect and exclude the sequence "\r\n" anyway so that we
151 -- can apply the "sep" parser.
153 -- We can instead implement these as special cases of deintercalate.
164 -- A simple and stupid impl would be to just convert the stream to an array
165 -- and give the array reference to all consumers. The array can be grown on
166 -- demand by any consumer and truncated when nonbody needs it.
168 -- -- ** Distribute to collection
169 -- -- ** Distribute to repetition
171 -- -- ** Interleaved collection
174 -- -- ** Interleaved repetition
175 -- repeat one parser and when it fails run an error recovery parser
176 -- e.g. to find a key frame in the stream after an error
178 -- ** Collection of Alternatives
181 -- , fastestN -- first N successful in time
182 -- , choiceN -- first N successful in position
183 , choice -- first successful in position
185 -- -- ** Repeated Alternatives
186 -- , retryMax -- try N times
187 -- , retryUntil -- try until successful
188 -- , retryUntilN -- try until successful n times
199import Control.Exception (assert, Exception)
200import Control.Monad (when)
201import Control.Monad.Catch (MonadCatch, MonadThrow(..))
202import Data.Bifunctor (first)
203import Fusion.Plugin.Types (Fuse(..))
204import Streamly.Internal.Data.Fold.Type (Fold(..))
205import Streamly.Internal.Data.SVar.Type (defState)
206import Streamly.Internal.Data.Either.Strict (Either'(..))
207import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
208import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
210import qualified Streamly.Internal.Data.Fold.Type as FL
211import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
212import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
215 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span
217import Streamly.Internal.Data.Parser.ParserD.Tee
218import Streamly.Internal.Data.Parser.ParserD.Type
223-- >>> :set -package streamly
224-- >>> import Prelude hiding ()
225-- >>> import qualified Data.Maybe as Maybe
226-- >>> import qualified Streamly.Prelude as Stream
227-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
228-- >>> import qualified Streamly.Data.Fold as Fold
229-- >>> import qualified Streamly.Internal.Data.Parser as Parser
231-------------------------------------------------------------------------------
232-- Downgrade a parser to a Fold
233-------------------------------------------------------------------------------
235data ParserToFoldError =
243instance Exception ParserToFoldError
245-- | See 'Streamly.Internal.Data.Parser.toFold'.
250toFold :: MonadThrow m => Parser m a b -> Fold m a b
251toFold (Parser pstep pinitial pextract) = Fold step initial pextract
258 IPartial s -> return $ FL.Partial s
259 IDone b -> return $ FL.Done b
260 IError err -> throwM $ InitialError err
265 Partial 0 s -> return $ FL.Partial s
266 Continue 0 s -> return $ FL.Partial s
267 Done 0 b -> return $ FL.Done b
268 Partial n _ -> throwM $ PartialError n
269 Continue n _ -> throwM $ ContinueError n
270 Done n _ -> throwM $ DoneError n
271 Error err -> throwM $ ErrorError err
273-------------------------------------------------------------------------------
274-- Upgrade folds to parses
275-------------------------------------------------------------------------------
277-- | See 'Streamly.Internal.Data.Parser.fromFold'.
281{-# INLINE fromFold #-}
282fromFold :: Monad m => Fold m a b -> Parser m a b
283fromFold (Fold fstep finitial fextract) = Parser step initial fextract
291 FL.Partial s1 -> IPartial s1
298 FL.Partial s1 -> Partial 0 s1
299 FL.Done b -> Done 0 b
301-- | Convert Maybe returning folds to error returning parsers.
305{-# INLINE fromFoldMaybe #-}
306fromFoldMaybe :: MonadThrow m => String -> Fold m a (Maybe b) -> Parser m a b
307fromFoldMaybe errMsg (Fold fstep finitial fextract) = Parser step initial extract
315 FL.Partial s1 -> IPartial s1
319 Nothing -> IError errMsg
325 FL.Partial s1 -> Partial 0 s1
329 Nothing -> Error errMsg
335 Nothing -> throwM $ ParseError errMsg
337-------------------------------------------------------------------------------
339-------------------------------------------------------------------------------
341-- | See 'Streamly.Internal.Data.Parser.peek'.
346peek :: MonadThrow m => Parser m a a
347peek = Parser step initial extract
351 initial = return $ IPartial ()
353 step () a = return $ Done 1 a
355 extract () = throwM $ ParseError "peek: end of input"
357-- | See 'Streamly.Internal.Data.Parser.eof'.
362eof :: Monad m => Parser m a ()
363eof = Parser step initial return
367 initial = return $ IPartial ()
369 step () _ = return $ Error "eof: not at end of input"
371-- | See 'Streamly.Internal.Data.Parser.next'.
376next :: Monad m => Parser m a (Maybe a)
377next = Parser step initial extract
379 initial = pure $ IPartial ()
380 step _ a = pure $ Done 0 (Just a)
381 extract _ = pure Nothing
383-- | See 'Streamly.Internal.Data.Parser.either'.
388either :: MonadThrow m => (a -> Either String b) -> Parser m a b
389either f = Parser step initial extract
393 initial = return $ IPartial ()
398 Left err -> Error err
400 extract _ = throwM $ ParseError "end of input"
402-- | See 'Streamly.Internal.Data.Parser.maybe'.
404-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
405-- >>> maybe f = Parser.either (toEither . f)
410maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
411-- maybe f = either (Maybe.maybe (Left "maybe: predicate failed") Right . f)
412maybe parserF = Parser step initial extract
416 initial = return $ IPartial ()
421 Nothing -> Error "maybe: predicate failed"
423 extract _ = throwM $ ParseError "maybe: end of input"
425-- | See 'Streamly.Internal.Data.Parser.satisfy'.
427-- >>> toMaybe f x = if f x then Just x else Nothing
428-- >>> satisfy f = Parser.maybe (toMaybe f)
432{-# INLINE satisfy #-}
433satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
434-- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)
435satisfy predicate = Parser step initial extract
439 initial = return $ IPartial ()
444 else Error "satisfy: predicate failed"
446 extract _ = throwM $ ParseError "satisfy: end of input"
448-------------------------------------------------------------------------------
450-------------------------------------------------------------------------------
452-- Required to fuse "take" with "many" in "chunksOf", for ghc-9.x
453{-# ANN type Tuple'Fused Fuse #-}
454data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
456-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
460{-# INLINE takeBetween #-}
461takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
462takeBetween low high (Fold fstep finitial fextract) =
464 Parser step initial (extract streamErr)
469 "takeBetween: Expecting alteast " ++ show low
470 ++ " elements, got " ++ show i
473 "takeBetween: lower bound - " ++ show low
474 ++ " is greater than higher bound - " ++ show high
476 foldErr :: Int -> String
478 "takeBetween: the collecting fold terminated after"
479 ++ " consuming" ++ show i ++ " elements"
480 ++ " minimum" ++ show low ++ " elements needed"
482 -- Exactly the same as snext except different constructors, we can possibly
483 -- deduplicate the two.
489 let s1 = Tuple'Fused i1 s
491 -- XXX ideally this should be a Continue instead
492 then return $ IPartial s1
493 else IDone <$> extract foldErr s1
498 else IError (foldErr i1)
501 when (low >= 0 && high >= 0 && low > high)
502 $ throwM $ ParseError invalidRange
504 finitial >>= inext (-1)
506 -- Keep the impl same as inext
512 let s1 = Tuple'Fused i1 s
514 then return $ Continue 0 s1
515 else Done 0 <$> extract foldErr s1
520 else Error (foldErr i1)
522 step (Tuple'Fused i s) a = fstep s a >>= snext i
524 extract f (Tuple'Fused i s)
525 | i >= low && i <= high = fextract s
526 | otherwise = throwM $ ParseError (f i)
528-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
533takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
534takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
543 FL.Partial s -> IPartial $ Tuple' 0 s
548 $ "takeEQ: Expecting exactly " ++ show cnt
549 ++ " elements, fold terminated without"
550 ++ " consuming any elements"
557 FL.Partial s -> Continue 0 $ Tuple' i1 s
560 $ "takeEQ: Expecting exactly " ++ show cnt
561 ++ " elements, fold terminated on " ++ show i1
566 FL.Partial s -> fextract s
567 FL.Done b -> return b
568 -- XXX we should not reach here when initial returns Step type
569 -- reachable only when n == 0
570 | otherwise = Done 1 <$> fextract r
577 | i == 0 && cnt == 0 = fextract r
581 $ "takeEQ: Expecting exactly " ++ show cnt
582 ++ " elements, input terminated on " ++ show i
584-- | See 'Streamly.Internal.Data.Parser.takeGE'.
589takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
590takeGE n (Fold fstep finitial fextract) = Parser step initial extract
598 FL.Partial s -> IPartial $ Tuple' 0 s
603 $ "takeGE: Expecting at least " ++ show cnt
604 ++ " elements, fold terminated without"
605 ++ " consuming any elements"
612 FL.Partial s -> Continue 0 $ Tuple' i1 s
615 $ "takeGE: Expecting at least " ++ show cnt
616 ++ " elements, fold terminated on " ++ show i1
621 FL.Partial s -> Partial 0 $ Tuple' i1 s
622 FL.Done b -> Done 0 b
629 | i >= cnt = fextract r
633 $ "takeGE: Expecting at least " ++ show cnt
634 ++ " elements, input terminated on " ++ show i
636-------------------------------------------------------------------------------
637-- Conditional splitting
638-------------------------------------------------------------------------------
640-- | See 'Streamly.Internal.Data.Parser.takeWhileP'.
644{-# INLINE takeWhileP #-}
645takeWhileP :: Monad m => (a -> Bool) -> Parser m a b -> Parser m a b
646takeWhileP predicate (Parser pstep pinitial pextract) =
647 Parser step pinitial pextract
654 else Done 1 <$> pextract s
656-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
660{-# INLINE takeWhile #-}
661takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
662takeWhile predicate (Fold fstep finitial fextract) =
663 Parser step initial fextract
670 FL.Partial s -> IPartial s
679 FL.Partial s1 -> Partial 0 s1
680 FL.Done b -> Done 0 b
681 else Done 1 <$> fextract s
683-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
687{-# INLINE takeWhile1 #-}
688takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
689takeWhile1 predicate (Fold fstep finitial fextract) =
690 Parser step initial extract
697 FL.Partial s -> IPartial (Left' s)
700 $ "takeWhile1: fold terminated without consuming:"
703 {-# INLINE process #-}
708 FL.Partial s1 -> Partial 0 (Right' s1)
709 FL.Done b -> Done 0 b
714 else return $ Error "takeWhile1: predicate failed on first element"
722 extract (Left' _) = throwM $ ParseError "takeWhile1: end of input"
723 extract (Right' s) = fextract s
725-------------------------------------------------------------------------------
727-------------------------------------------------------------------------------
729{-# INLINE takeFramedByGeneric #-}
730takeFramedByGeneric :: MonadThrow m =>
736takeFramedByGeneric esc begin end (Fold fstep finitial fextract) =
738 Parser step initial extract
746 FL.Partial s -> IPartial (FrameEscInit s)
748 error "takeFramedByGeneric: fold done without input"
750 {-# INLINE process #-}
755 FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)
756 FL.Done b -> Done 0 b
758 {-# INLINE processNoEsc #-}
768 then Done 0 <$> fextract s
769 else process s a (n - 1)
771 let n1 = if isBegin a then n + 1 else n
773 Nothing -> -- takeEndBy case
775 then Done 0 <$> fextract s
777 Nothing -> -- takeStartBy case
781 then Done 0 <$> fextract s
784 error $ "takeFramedByGeneric: "
785 ++ "Both begin and end frame predicate missing"
787 {-# INLINE processCheckEsc #-}
788 processCheckEsc s a n =
792 then return $ Partial 0 $ FrameEscEsc s n
793 else processNoEsc s a n
794 Nothing -> processNoEsc s a n
796 step (FrameEscInit s) a =
800 then return $ Partial 0 (FrameEscGo s 0)
801 else return $ Error "takeFramedByGeneric: missing frame start"
806 then Done 0 <$> fextract s
807 else processCheckEsc s a 0
809 error "Both begin and end frame predicate missing"
810 step (FrameEscGo s n) a = processCheckEsc s a n
811 step (FrameEscEsc s n) a = process s a n
813 err = throwM . ParseError
815 extract (FrameEscInit _) =
816 err "takeFramedByGeneric: empty token"
817 extract (FrameEscGo s _) =
821 Nothing -> fextract s
822 Just _ -> err "takeFramedByGeneric: missing frame end"
823 Nothing -> err "takeFramedByGeneric: missing closing frame"
824 extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"
826-- | See 'Streamly.Internal.Data.Parser.takeEndBy'.
830{-# INLINE takeEndBy #-}
831takeEndBy :: MonadCatch m => (a -> Bool) -> Parser m a b -> Parser m a b
832takeEndBy cond (Parser pstep pinitial pextract) =
834 Parser step initial pextract
844 else extractStep pextract res
846-- | See 'Streamly.Internal.Data.Parser.takeEndByEsc'.
850{-# INLINE takeEndByEsc #-}
851takeEndByEsc :: MonadCatch m =>
852 (a -> Bool) -> (a -> Bool) -> Parser m a b -> Parser m a b
853takeEndByEsc isEsc isSep (Parser pstep pinitial pextract) =
855 Parser step initial extract
859 initial = first Left' <$> pinitial
861 step (Left' s) a = do
863 then return $ Partial 0 $ Right' s
867 then return $ mapStateStep Left' res
868 else extractStep pextract res
870 step (Right' s) a = do
872 return $ mapStateStep Left' res
874 extract (Left' s) = pextract s
876 throwM $ ParseError "takeEndByEsc: trailing escape"
878-- | See 'Streamly.Internal.Data.Parser.takeEndBy_'.
882{-# INLINE takeEndBy_ #-}
883takeEndBy_ :: MonadCatch m =>
884 (a -> Bool) -> Parser m a b -> Parser m a b
885takeEndBy_ cond (Parser pstep pinitial pextract) =
887 Parser step initial pextract
900-- | See 'Streamly.Internal.Data.Parser.takeStartBy'.
905{-# INLINE takeStartBy #-}
906takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
907takeStartBy cond (Fold fstep finitial fextract) =
909 Parser step initial extract
917 FL.Partial s -> IPartial (Left' s)
918 FL.Done _ -> IError "takeStartBy: fold done without input"
920 {-# INLINE process #-}
925 FL.Partial s1 -> Partial 0 (Right' s1)
926 FL.Done b -> Done 0 b
931 else return $ Error "takeStartBy: missing frame start"
935 else Done 1 <$> fextract s
937 extract (Left' s) = fextract s
938 extract (Right' s) = fextract s
940data FramedEscState s =
941 FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int
943{-# INLINE takeFramedByEsc_ #-}
944takeFramedByEsc_ :: MonadThrow m =>
945 (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
946takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
948 Parser step initial extract
956 FL.Partial s -> IPartial (FrameEscInit s)
958 error "takeFramedByEsc_: fold done without input"
960 {-# INLINE process #-}
965 FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)
966 FL.Done b -> Done 0 b
968 step (FrameEscInit s) a =
970 then return $ Partial 0 (FrameEscGo s 0)
971 else return $ Error "takeFramedByEsc_: missing frame start"
972 step (FrameEscGo s n) a =
974 then return $ Partial 0 $ FrameEscEsc s n
978 let n1 = if isBegin a then n + 1 else n
982 then Done 0 <$> fextract s
983 else process s a (n - 1)
984 step (FrameEscEsc s n) a = process s a n
986 err = throwM . ParseError
988 extract (FrameEscInit _) = err "takeFramedByEsc_: empty token"
989 extract (FrameEscGo _ _) = err "takeFramedByEsc_: missing frame end"
990 extract (FrameEscEsc _ _) = err "takeFramedByEsc_: trailing escape"
992data FramedState s = FrameInit !s | FrameGo !s Int
994{-# INLINE takeFramedBy_ #-}
995takeFramedBy_ :: MonadThrow m =>
996 (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
997takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
999 Parser step initial extract
1007 FL.Partial s -> IPartial (FrameInit s)
1009 error "takeFramedBy_: fold done without input"
1011 {-# INLINE process #-}
1016 FL.Partial s1 -> Continue 0 (FrameGo s1 n)
1017 FL.Done b -> Done 0 b
1019 step (FrameInit s) a =
1021 then return $ Continue 0 (FrameGo s 0)
1022 else return $ Error "takeFramedBy_: missing frame start"
1023 step (FrameGo s n) a
1025 let n1 = if isBegin a then n + 1 else n
1027 | n == 0 = Done 0 <$> fextract s
1028 | otherwise = process s a (n - 1)
1030 err = throwM . ParseError
1032 extract (FrameInit _) = err "takeFramedBy_: empty token"
1033 extract (FrameGo _ _) = err "takeFramedBy_: missing frame end"
1035-------------------------------------------------------------------------------
1036-- Grouping and words
1037-------------------------------------------------------------------------------
1039data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
1041-- | See 'Streamly.Internal.Data.Parser.wordBy'.
1044{-# INLINE wordBy #-}
1045wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
1046wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
1050 {-# INLINE worder #-}
1055 FL.Partial s1 -> Partial 0 $ WBWord s1
1056 FL.Done b -> Done 0 b
1062 FL.Partial s -> IPartial $ WBLeft s
1063 FL.Done b -> IDone b
1066 if not (predicate a)
1068 else return $ Partial 0 $ WBLeft s
1070 if not (predicate a)
1074 return $ Partial 0 $ WBRight b
1075 step (WBRight b) a =
1077 $ if not (predicate a)
1079 else Partial 0 $ WBRight b
1081 extract (WBLeft s) = fextract s
1082 extract (WBWord s) = fextract s
1083 extract (WBRight b) = return b
1085data WordFramedState s b =
1086 WordFramedSkipPre !s
1087 | WordFramedWord !s !Int
1088 | WordFramedEsc !s !Int
1089 | WordFramedSkipPost !b
1091-- | See 'Streamly.Internal.Data.Parser.wordFramedBy'
1093{-# INLINE wordFramedBy #-}
1094wordFramedBy :: MonadCatch m =>
1095 (a -> Bool) -- ^ Escape
1096 -> (a -> Bool) -- ^ left quote
1097 -> (a -> Bool) -- ^ right quote
1098 -> (a -> Bool) -- ^ word seperator
1101wordFramedBy isEsc isBegin isEnd isSep
1102 (Fold fstep finitial fextract) =
1103 Parser step initial extract
1111 FL.Partial s -> IPartial (WordFramedSkipPre s)
1113 error "wordFramedBy: fold done without input"
1115 {-# INLINE process #-}
1120 FL.Partial s1 -> Continue 0 (WordFramedWord s1 n)
1121 FL.Done b -> Done 0 b
1123 step (WordFramedSkipPre s) a
1124 | isEsc a = return $ Continue 0 $ WordFramedEsc s 0
1125 | isSep a = return $ Partial 0 $ WordFramedSkipPre s
1126 | isBegin a = return $ Continue 0 $ WordFramedWord s 1
1128 return $ Error "wordFramedBy: missing frame start"
1129 | otherwise = process s a 0
1130 step (WordFramedWord s n) a
1131 | isEsc a = return $ Continue 0 $ WordFramedEsc s n
1132 | n == 0 && isSep a = do
1134 return $ Partial 0 $ WordFramedSkipPost b
1136 -- We need to use different order for checking begin and end for
1137 -- the n == 0 and n == 1 case so that when the begin and end
1138 -- character is the same we treat the one after begin as end.
1141 -- Need to check isBegin first
1143 then return $ Continue 0 $ WordFramedWord s 1
1145 then return $ Error "wordFramedBy: missing frame start"
1148 -- Need to check isEnd first
1152 then return $ Continue 0 $ WordFramedWord s 0
1153 else process s a (n - 1)
1155 then process s a (n + 1)
1157 step (WordFramedEsc s n) a = process s a n
1158 step (WordFramedSkipPost b) a =
1162 else Partial 0 $ WordFramedSkipPost b
1164 err = throwM . ParseError
1166 extract (WordFramedSkipPre s) = fextract s
1167 extract (WordFramedWord s n) =
1170 else err "wordFramedBy: missing frame end"
1171 extract (WordFramedEsc _ _) =
1172 err "wordFramedBy: trailing escape"
1173 extract (WordFramedSkipPost b) = return b
1175data WordQuotedState s b a =
1176 WordQuotedSkipPre !s
1177 | WordUnquotedWord !s
1178 | WordQuotedWord !s !Int a
1179 | WordUnquotedEsc !s
1180 | WordQuotedEsc !s !Int a
1181 | WordQuotedSkipPost !b
1183{-# INLINE wordQuotedBy #-}
1184wordQuotedBy :: (MonadCatch m, Eq a) =>
1185 Bool -- ^ keep the quotes in the output
1186 -> (a -> Bool) -- ^ Escape
1187 -> (a -> Bool) -- ^ left quote
1188 -> (a -> Bool) -- ^ right quote
1189 -> (a -> a) -- ^ get right quote from the left quote
1190 -> (a -> Bool) -- ^ word seperator
1193wordQuotedBy keepQuotes isEsc isBegin isEnd toRight isSep
1194 (Fold fstep finitial fextract) =
1195 Parser step initial extract
1203 FL.Partial s -> IPartial (WordQuotedSkipPre s)
1205 error "wordQuotedBy: fold done without input"
1207 {-# INLINE process #-}
1208 process s a n q = do
1212 FL.Partial s1 -> Continue 0 (WordQuotedWord s1 n q)
1213 FL.Done b -> Done 0 b
1215 {-# INLINE processUnquoted #-}
1216 processUnquoted s a = do
1220 FL.Partial s1 -> Continue 0 (WordUnquotedWord s1)
1221 FL.Done b -> Done 0 b
1223 step (WordQuotedSkipPre s) a
1224 | isEsc a = return $ Continue 0 $ WordUnquotedEsc s
1225 | isSep a = return $ Partial 0 $ WordQuotedSkipPre s
1228 then process s a 1 a
1229 else return $ Continue 0 $ WordQuotedWord s 1 a
1231 return $ Error "wordQuotedBy: missing frame start"
1232 | otherwise = processUnquoted s a
1233 step (WordUnquotedWord s) a
1234 | isEsc a = return $ Continue 0 $ WordUnquotedEsc s
1237 return $ Partial 0 $ WordQuotedSkipPost b
1241 then process s a 1 a
1242 else return $ Continue 0 $ WordQuotedWord s 1 a
1244 then return $ Error "wordQuotedBy: missing frame start"
1245 else processUnquoted s a
1246 step (WordQuotedWord s n q) a
1247 | isEsc a = return $ Continue 0 $ WordQuotedEsc s n q
1248 -- XXX Will this ever occur? Will n ever be 0?
1249 | n == 0 && isSep a = do
1251 return $ Partial 0 $ WordQuotedSkipPost b
1257 then processUnquoted s a
1258 else return $ Continue 0 $ WordUnquotedWord s
1259 else process s a (n - 1) q
1261 then process s a (n + 1) q
1262 else process s a n q
1263 step (WordUnquotedEsc s) a = processUnquoted s a
1264 step (WordQuotedEsc s n q) a = process s a n q
1265 step (WordQuotedSkipPost b) a =
1269 else Partial 0 $ WordQuotedSkipPost b
1271 err = throwM . ParseError
1273 extract (WordQuotedSkipPre s) = fextract s
1274 extract (WordUnquotedWord s) = fextract s
1275 extract (WordQuotedWord s n _) =
1278 else err "wordQuotedBy: missing frame end"
1279 extract WordQuotedEsc {} =
1280 err "wordQuotedBy: trailing escape"
1281 extract (WordUnquotedEsc _) =
1282 err "wordQuotedBy: trailing escape"
1283 extract (WordQuotedSkipPost b) = return b
1285{-# ANN type GroupByState Fuse #-}
1286data GroupByState a s
1288 | GroupByGrouping !a !s
1290-- | See 'Streamly.Internal.Data.Parser.groupBy'.
1292{-# INLINE groupBy #-}
1293groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
1294groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
1298 {-# INLINE grouper #-}
1303 FL.Done b -> Done 0 b
1304 FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
1310 FL.Partial s -> IPartial $ GroupByInit s
1311 FL.Done b -> IDone b
1313 step (GroupByInit s) a = grouper s a a
1314 step (GroupByGrouping a0 s) a =
1317 else Done 1 <$> fextract s
1319 extract (GroupByInit s) = fextract s
1320 extract (GroupByGrouping _ s) = fextract s
1322-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
1324{-# INLINE groupByRolling #-}
1325groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
1326groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
1330 {-# INLINE grouper #-}
1335 FL.Done b -> Done 0 b
1336 FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
1342 FL.Partial s -> IPartial $ GroupByInit s
1343 FL.Done b -> IDone b
1345 step (GroupByInit s) a = grouper s a
1346 step (GroupByGrouping a0 s) a =
1349 else Done 1 <$> fextract s
1351 extract (GroupByInit s) = fextract s
1352 extract (GroupByGrouping _ s) = fextract s
1354{-# ANN type GroupByStatePair Fuse #-}
1355data GroupByStatePair a s1 s2
1356 = GroupByInitPair !s1 !s2
1357 | GroupByGroupingPair !a !s1 !s2
1358 | GroupByGroupingPairL !a !s1 !s2
1359 | GroupByGroupingPairR !a !s1 !s2
1361{-# INLINE groupByRollingEither #-}
1362groupByRollingEither :: MonadCatch m =>
1363 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (Either b c)
1366 (Fold fstep1 finitial1 fextract1)
1367 (Fold fstep2 finitial2 fextract2) = Parser step initial extract
1371 {-# INLINE grouper #-}
1372 grouper s1 s2 a = do
1373 return $ Continue 0 (GroupByGroupingPair a s1 s2)
1375 {-# INLINE grouperL2 #-}
1376 grouperL2 s1 s2 a = do
1380 FL.Done b -> Done 0 (Left b)
1381 FL.Partial s11 -> Partial 0 (GroupByGroupingPairL a s11 s2)
1383 {-# INLINE grouperL #-}
1384 grouperL s1 s2 a0 a = do
1387 FL.Done b -> return $ Done 0 (Left b)
1388 FL.Partial s11 -> grouperL2 s11 s2 a
1390 {-# INLINE grouperR2 #-}
1391 grouperR2 s1 s2 a = do
1395 FL.Done b -> Done 0 (Right b)
1396 FL.Partial s21 -> Partial 0 (GroupByGroupingPairR a s1 s21)
1398 {-# INLINE grouperR #-}
1399 grouperR s1 s2 a0 a = do
1402 FL.Done b -> return $ Done 0 (Right b)
1403 FL.Partial s21 -> grouperR2 s1 s21 a
1412 FL.Partial s2 -> IPartial $ GroupByInitPair s1 s2
1413 FL.Done b -> IDone (Right b)
1414 FL.Done b -> IDone (Left b)
1416 step (GroupByInitPair s1 s2) a = grouper s1 s2 a
1418 step (GroupByGroupingPair a0 s1 s2) a =
1420 then grouperL s1 s2 a0 a
1421 else grouperR s1 s2 a0 a
1423 step (GroupByGroupingPairL a0 s1 s2) a =
1425 then grouperL2 s1 s2 a
1426 else Done 1 . Left <$> fextract1 s1
1428 step (GroupByGroupingPairR a0 s1 s2) a =
1430 then grouperR2 s1 s2 a
1431 else Done 1 . Right <$> fextract2 s2
1433 extract (GroupByInitPair s1 _) = Left <$> fextract1 s1
1434 extract (GroupByGroupingPairL _ s1 _) = Left <$> fextract1 s1
1435 extract (GroupByGroupingPairR _ _ s2) = Right <$> fextract2 s2
1436 extract (GroupByGroupingPair a s1 _) = do
1439 FL.Done b -> return $ Left b
1440 FL.Partial s11 -> Left <$> fextract1 s11
1442-- XXX use an Unfold instead of a list?
1443-- XXX custom combinators for matching list, array and stream?
1444-- XXX rename to listBy?
1446-- | See 'Streamly.Internal.Data.Parser.eqBy'.
1451eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
1452eqBy cmp str = Parser step initial extract
1456 -- XXX Should return IDone in initial for [] case
1457 initial = return $ IPartial str
1459 step [] _ = return $ Done 0 ()
1464 else Error "eqBy: failed, yet to match the last element"
1470 $ "eqBy: failed, yet to match "
1471 ++ show (length xs + 1) ++ " elements"
1476 $ "eqBy: end of input, yet to match "
1477 ++ show (length xs) ++ " elements"
1479-- XXX rename to streamBy?
1480-- | Like eqBy but uses a stream instead of a list
1481{-# INLINE matchBy #-}
1482matchBy :: MonadThrow m => (a -> a -> Bool) -> D.Stream m a -> Parser m a ()
1483matchBy cmp (D.Stream sstep state) = Parser step initial extract
1488 r <- sstep defState state
1490 D.Yield x s -> return $ IPartial (Just' x, s)
1491 D.Stop -> return $ IDone ()
1492 -- Need Skip/Continue in initial to loop right here
1493 D.Skip s -> return $ IPartial (Nothing', s)
1495 step (Just' x, st) a =
1498 r <- sstep defState st
1501 D.Yield x1 s -> Continue 0 (Just' x1, s)
1503 D.Skip s -> Continue 1 (Nothing', s)
1504 else return $ Error "match: mismtach occurred"
1505 step (Nothing', st) a = do
1506 r <- sstep defState st
1511 then Continue 0 (Nothing', s)
1512 else Error "match: mismatch occurred"
1514 D.Skip s -> Continue 1 (Nothing', s)
1516 extract _ = throwM $ ParseError "match: end of input"
1518-------------------------------------------------------------------------------
1519-- Transformations on input
1520-------------------------------------------------------------------------------
1522-- Initial needs a "Continue" constructor to implement scans on parsers. As a
1523-- parser can always return a Continue in initial when we feed the fold's
1524-- initial result to it. We can work this around for postscan by introducing an
1525-- initial state and calling "initial" only on the first input.
1527-- | Stateful scan on the input of a parser using a Fold.
1531{-# INLINE postscan #-}
1532postscan :: -- Monad m =>
1533 Fold m a b -> Parser m b c -> Parser m a c
1536{-# INLINE zipWithM #-}
1537zipWithM :: MonadThrow m =>
1538 (a -> b -> m c) -> D.Stream m a -> Fold m c x -> Parser m b x
1539zipWithM zf (D.Stream sstep state) (Fold fstep finitial fextract) =
1540 Parser step initial extract
1548 r <- sstep defState state
1550 D.Yield x s -> return $ IPartial (Just' x, s, fs)
1554 -- Need Skip/Continue in initial to loop right here
1555 D.Skip s -> return $ IPartial (Nothing', s, fs)
1556 FL.Done x -> return $ IDone x
1558 step (Just' a, st, fs) b = do
1562 FL.Partial fs1 -> do
1563 r <- sstep defState st
1565 D.Yield x1 s -> return $ Continue 0 (Just' x1, s, fs1)
1569 D.Skip s -> return $ Continue 1 (Nothing', s, fs1)
1570 FL.Done x -> return $ Done 0 x
1571 step (Nothing', st, fs) b = do
1572 r <- sstep defState st
1579 return $ Continue 0 (Nothing', s, fs1)
1580 FL.Done x -> return $ Done 0 x
1584 D.Skip s -> return $ Continue 1 (Nothing', s, fs)
1586 extract _ = throwM $ ParseError "zipWithM: end of input"
1588-- | Zip the input of a fold with a stream.
1593zip :: MonadThrow m => D.Stream m a -> Fold m (a, b) x -> Parser m b x
1594zip = zipWithM (curry return)
1596-- | Pair each element of a fold input with its index, starting from index 0.
1599{-# INLINE indexed #-}
1600indexed :: forall m a b. MonadThrow m => Fold m (Int, a) b -> Parser m a b
1601indexed = zip (D.enumerateFromIntegral 0 :: D.Stream m Int)
1603-- | @makeIndexFilter indexer filter predicate@ generates a fold filtering
1604-- function using a fold indexing function that attaches an index to each input
1605-- element and a filtering function that filters using @(index, element) ->
1606-- Bool) as predicate.
1611-- filterWithIndex = makeIndexFilter indexed filter
1612-- filterWithAbsTime = makeIndexFilter timestamped filter
1613-- filterWithRelTime = makeIndexFilter timeIndexed filter
1617{-# INLINE makeIndexFilter #-}
1619 (Fold m (s, a) b -> Parser m a b)
1620 -> (((s, a) -> Bool) -> Fold m (s, a) b -> Fold m (s, a) b)
1621 -> (((s, a) -> Bool) -> Fold m a b -> Parser m a b)
1622makeIndexFilter f comb g = f . comb g . FL.lmap snd
1624-- | @sampleFromthen offset stride@ samples the element at @offset@ index and
1625-- then every element at strides of @stride@.
1628{-# INLINE sampleFromthen #-}
1629sampleFromthen :: MonadThrow m => Int -> Int -> Fold m a b -> Parser m a b
1630sampleFromthen offset size =
1631 makeIndexFilter indexed FL.filter (\(i, _) -> (i + offset) `mod` size == 0)
1633--------------------------------------------------------------------------------
1635--------------------------------------------------------------------------------
1637-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the
1638-- input as long as the predicate @p@ is 'True'. @f2@ consumes the rest of the
1642-- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs
1644-- > span_ (< 1) [1,2,3]
1647-- > span_ (< 2) [1,2,3]
1650-- > span_ (< 4) [1,2,3]
1657span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1658span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)
1660-- | Break the input stream into two groups, the first group takes the input as
1661-- long as the predicate applied to the first element of the stream and next
1662-- input element holds 'True', the second group takes the rest of the input.
1666{-# INLINE spanBy #-}
1669 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1670spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)
1672-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
1673-- predicate is applied to the previous and the next input elements.
1676{-# INLINE spanByRolling #-}
1679 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1680spanByRolling eq f1 f2 =
1681 noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)
1683-------------------------------------------------------------------------------
1685-------------------------------------------------------------------------------
1687-- | See 'Streamly.Internal.Data.Parser.takeP'.
1691takeP :: Monad m => Int -> Parser m a b -> Parser m a b
1692takeP lim (Parser pstep pinitial pextract) = Parser step initial extract
1701 then return $ IPartial $ Tuple' 0 s
1702 else IDone <$> pextract s
1703 IDone b -> return $ IDone b
1704 IError e -> return $ IError e
1706 step (Tuple' cnt r) a = do
1707 assert (cnt < lim) (return ())
1712 assert (cnt1 >= 0) (return ())
1714 then return $ Partial 0 $ Tuple' cnt1 s
1715 else Done 0 <$> pextract s
1717 assert (cnt1 >= 0) (return ())
1719 then return $ Continue 0 $ Tuple' cnt1 s
1720 -- XXX This should error out?
1721 -- If designed properly, this will probably error out.
1722 -- "pextract" should error out
1726 -- This is a tricky case, we have the following options:
1727 -- 1. Done 0 with extract as you have written
1728 -- 2. Done n, will require buffering elements
1729 -- 3. Use a backtracking fold and not a parser, once we have
1730 -- backtracking in folds
1731 else Done 0 <$> pextract s
1733 let taken = cnt1 - n
1734 assert (taken >= 0) (return ())
1735 return $ Partial n $ Tuple' taken s
1737 let taken = cnt1 - n
1738 assert (taken >= 0) (return ())
1739 return $ Continue n $ Tuple' taken s
1740 Done n b -> return $ Done n b
1741 Error str -> return $ Error str
1743 extract (Tuple' _ r) = pextract r
1745-- | See 'Streamly.Internal.Data.Parser.lookahead'.
1749{-# INLINE lookAhead #-}
1750lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
1751lookAhead (Parser step1 initial1 _) = Parser step initial extract
1757 return $ case res of
1758 IPartial s -> IPartial (Tuple' 0 s)
1760 IError e -> IError e
1762 step (Tuple' cnt st) a = do
1767 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
1768 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
1769 Done _ b -> Done cnt1 b
1770 Error err -> Error err
1772 -- XXX returning an error let's us backtrack. To implement it in a way so
1773 -- that it terminates on eof without an error then we need a way to
1774 -- backtrack on eof, that will require extract to return 'Step' type.
1775 extract (Tuple' n _) =
1778 $ "lookAhead: end of input after consuming "
1779 ++ show n ++ " elements"
1781-------------------------------------------------------------------------------
1783-------------------------------------------------------------------------------
1785data DeintercalateState fs sp ss =
1786 DeintercalateL !fs !sp
1787 | DeintercalateR !fs !ss !Bool
1789-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
1793{-# INLINE deintercalate #-}
1794deintercalate :: Monad m =>
1795 Fold m (Either x y) z
1800 (Fold fstep finitial fextract)
1801 (Parser stepL initialL extractL)
1802 (Parser stepR initialR extractR) = Parser step initial extract
1807 error $ "deintercalate: " ++ p ++ " parser cannot "
1808 ++ status ++ " without input"
1816 IPartial sL -> return $ IPartial $ DeintercalateL fs sL
1817 IDone _ -> errMsg "left" "succeed"
1818 IError _ -> errMsg "left" "fail"
1819 FL.Done c -> return $ IDone c
1821 step (DeintercalateL fs sL) a = do
1824 Partial n s -> return $ Partial n (DeintercalateL fs s)
1825 Continue n s -> return $ Continue n (DeintercalateL fs s)
1827 fres <- fstep fs (Left b)
1829 FL.Partial fs1 -> do
1834 $ Partial n (DeintercalateR fs1 sR False)
1835 IDone _ -> errMsg "right" "succeed"
1836 IError _ -> errMsg "right" "fail"
1837 FL.Done c -> return $ Done n c
1838 Error err -> return $ Error err
1839 step (DeintercalateR fs sR consumed) a = do
1842 Partial n s -> return $ Partial n (DeintercalateR fs s True)
1843 Continue n s -> return $ Continue n (DeintercalateR fs s True)
1847 fres <- fstep fs (Right b)
1849 FL.Partial fs1 -> do
1853 return $ Partial n $ DeintercalateL fs1 sL
1854 IDone _ -> errMsg "left" "succeed"
1855 IError _ -> errMsg "left" "fail"
1856 FL.Done c -> return $ Done n c
1857 else error "deintercalate: infinite loop"
1858 Error err -> return $ Error err
1860 extract (DeintercalateL fs sL) = do
1862 res <- fstep fs (Left r)
1864 FL.Partial fs1 -> fextract fs1
1865 FL.Done c -> return c
1866 extract (DeintercalateR fs sR _) = do
1868 res <- fstep fs (Right r)
1870 FL.Partial fs1 -> fextract fs1
1871 FL.Done c -> return c
1873data SepByState fs sp ss =
1875 | SepBySeparator !fs !ss !Bool
1877-- This is a special case of deintercalate and can be easily implemented in
1878-- terms of deintercalate.
1880sepBy :: MonadCatch m =>
1881 Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
1883 (Fold fstep finitial fextract)
1884 (Parser pstep pinitial pextract)
1885 (Parser sstep sinitial _) = Parser step initial extract
1890 error $ "sepBy: " ++ p ++ " parser cannot "
1891 ++ status ++ " without input"
1899 IPartial sp -> return $ IPartial $ SepByInit fs sp
1900 IDone _ -> errMsg "content" "succeed"
1901 IError _ -> errMsg "content" "fail"
1902 FL.Done b -> return $ IDone b
1904 step (SepByInit fs sp) a = do
1907 Partial n s -> return $ Partial n (SepByInit fs s)
1908 Continue n s -> return $ Continue n (SepByInit fs s)
1912 FL.Partial fs1 -> do
1916 return $ Partial n (SepBySeparator fs1 ss False)
1917 IDone _ -> errMsg "separator" "succeed"
1918 IError _ -> errMsg "separator" "fail"
1919 FL.Done c -> return $ Done n c
1920 Error err -> return $ Error err
1921 step (SepBySeparator fs ss consumed) a = do
1924 Partial n s -> return $ Partial n (SepBySeparator fs s True)
1925 Continue n s -> return $ Continue n (SepBySeparator fs s True)
1931 IPartial sp -> return $ Partial n $ SepByInit fs sp
1932 IDone _ -> errMsg "content" "succeed"
1933 IError _ -> errMsg "content" "fail"
1934 else error "sepBy: infinite loop"
1935 Error err -> return $ Error err
1937 extract (SepByInit fs sp) = do
1941 FL.Partial fs1 -> fextract fs1
1942 FL.Done c -> return c
1943 extract (SepBySeparator fs _ _) = fextract fs
1945-------------------------------------------------------------------------------
1946-- Sequential Collection
1947-------------------------------------------------------------------------------
1949-- | See 'Streamly.Internal.Data.Parser.sequence'.
1950{-# INLINE sequence #-}
1951sequence :: MonadThrow m =>
1952 Fold m b c -> D.Stream m (Parser m a b) -> Parser m a c
1953sequence (Fold fstep finitial fextract) (D.Stream sstep sstate) =
1954 Parser step initial extract
1961 FL.Partial fs -> return $ IPartial (Nothing', sstate, fs)
1962 FL.Done c -> return $ IDone c
1964 -- state does not contain any parser
1965 -- yield a new parser from the stream
1966 step (Nothing', ss, fs) _ = do
1967 sres <- sstep defState ss
1969 D.Yield p ss1 -> return $ Continue 1 (Just' p, ss1, fs)
1973 D.Skip ss1 -> return $ Continue 1 (Nothing', ss1, fs)
1975 -- state holds a parser that may or may not have been
1976 -- initialized. pinit holds the initial parser state
1977 -- or modified parser state respectively
1978 step (Just' (Parser pstep pinit pextr), ss, fs) a = do
1986 Just' $ Parser pstep (return $ IPartial ps2) pextr
1987 in return $ Partial n (newP, ss, fs)
1990 Just' $ Parser pstep (return $ IPartial ps2) pextr
1991 in return $ Continue n (newP, ss, fs)
1996 return $ Partial n (Nothing', ss, fs1)
1997 FL.Done c -> return $ Done n c
1998 Error msg -> return $ Error msg
2003 return $ Partial 1 (Nothing', ss, fs1)
2004 FL.Done c -> return $ Done 1 c
2005 IError err -> return $ Error err
2007 extract (Nothing', _, fs) = fextract fs
2008 extract (Just' (Parser _ pinit pextr), _, fs) = do
2015 FL.Partial fs1 -> fextract fs1
2016 FL.Done c -> return c
2020 FL.Partial fs1 -> fextract fs1
2021 FL.Done c -> return c
2022 IError err -> throwM $ ParseError err
2024-------------------------------------------------------------------------------
2025-- Alternative Collection
2026-------------------------------------------------------------------------------
2028-- | See 'Streamly.Internal.Data.Parser.choice'.
2032{-# INLINE choice #-}
2033choice :: (MonadCatch m, Foldable t) => t (Parser m a b) -> Parser m a b
2034choice = foldl1 shortest
2036-------------------------------------------------------------------------------
2037-- Sequential Repetition
2038-------------------------------------------------------------------------------
2041manyP :: -- MonadCatch m =>
2042 Parser m a b -> Parser m b c -> Parser m a c
2043manyP _p _f = undefined
2045-- | See 'Streamly.Internal.Data.Parser.many'.
2050many :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
2052-- many = countBetween 0 maxBound
2054-- | See 'Streamly.Internal.Data.Parser.some'.
2059some :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
2061-- some p f = manyP p (takeGE 1 f)
2062-- some = countBetween 1 maxBound
2064-- | See 'Streamly.Internal.Data.Parser.countBetween'.
2068{-# INLINE countBetween #-}
2069countBetween :: -- MonadCatch m =>
2070 Int -> Int -> Parser m a b -> Fold m b c -> Parser m a c
2071countBetween _m _n _p = undefined
2072-- countBetween m n p f = manyP p (takeBetween m n f)
2074-- | See 'Streamly.Internal.Data.Parser.count'.
2079count :: -- MonadCatch m =>
2080 Int -> Parser m a b -> Fold m b c -> Parser m a c
2081count n = countBetween n n
2082-- count n p f = manyP p (takeEQ n f)
2084data ManyTillState fs sr sl
2085 = ManyTillR Int fs sr
2086 | ManyTillL Int fs sl
2088-- | See 'Streamly.Internal.Data.Parser.manyTill'.
2092{-# INLINE manyTill #-}
2093manyTill :: MonadCatch m
2094 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
2095manyTill (Fold fstep finitial fextract)
2096 (Parser stepL initialL extractL)
2097 (Parser stepR initialR _) =
2098 Parser step initial extract
2102 -- Caution: Mutual recursion
2104 scrutL fs p c d e = do
2107 IPartial sl -> return $ c (ManyTillL 0 fs sl)
2111 FL.Partial fs1 -> scrutR fs1 p c d e
2112 FL.Done fb -> return $ d fb
2113 IError err -> return $ e err
2115 scrutR fs p c d e = do
2118 IPartial sr -> return $ p (ManyTillR 0 fs sr)
2119 IDone _ -> d <$> fextract fs
2120 IError _ -> scrutL fs p c d e
2125 FL.Partial fs -> scrutR fs IPartial IPartial IDone IError
2126 FL.Done b -> return $ IDone b
2128 step (ManyTillR cnt fs st) a = do
2131 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
2133 assert (cnt + 1 - n >= 0) (return ())
2134 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
2142 return $ Continue (cnt + 1) (ManyTillL 0 fs sl)
2150 FL.Partial fs1 -> scrutR fs1 p c d Error
2151 FL.Done fb -> return $ Done cnt1 fb
2152 IError err -> return $ Error err
2153 -- XXX the cnt is being used only by the assert
2154 step (ManyTillL cnt fs st) a = do
2157 Partial n s -> return $ Partial n (ManyTillL 0 fs s)
2159 assert (cnt + 1 - n >= 0) (return ())
2160 return $ Continue n (ManyTillL (cnt + 1 - n) fs s)
2165 scrutR s (Partial n) (Continue n) (Done n) Error
2166 FL.Done b1 -> return $ Done n b1
2167 Error err -> return $ Error err
2169 extract (ManyTillL _ fs sR) = do
2170 res <- extractL sR >>= fstep fs
2172 FL.Partial s -> fextract s
2173 FL.Done b -> return b
2174 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