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
52 -- Parsers chained in series, if one parser terminates the composition
53 -- terminates. Currently we are using folds to collect the output of the
54 -- parsers but we can use Parsers instead of folds to make the composition
55 -- more powerful. For example, we can do:
57 -- takeEndByOrMax cond n p = takeEndBy cond (take n p)
58 -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)
59 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
61 -- Grab a sequence of input elements without inspecting them
63 -- , take -- take -- takeBetween 0 n
64 , takeEQ -- takeBetween n n
65 , takeGE -- takeBetween n maxBound
66 -- , takeGE1 -- take1 -- takeBetween 1 n
69 -- Grab a sequence of input elements by inspecting them
90 , groupByRollingEither
95 -- , prefixOf -- match any prefix of a given string
96 -- , suffixOf -- match any suffix of a given string
97 -- , infixOf -- match any substring of a given string
104 -- Second order parsers (parsers using parsers)
105 -- * Binary Combinators
107 -- ** Sequential Applicative
111 -- ** Parallel Applicatives
115 -- , teeTill -- like manyTill but parallel
117 -- ** Sequential Interleaving
118 -- Use two folds, run a primary parser, its rejected values go to the
123 -- ** Sequential Alternative
126 -- ** Parallel Alternatives
131 -- * N-ary Combinators
132 -- ** Sequential Collection
136 -- ** Sequential Repetition
139 -- , countBetweenTill
146 -- -- ** Special cases
147 -- XXX traditional implmentations of these may be of limited use. For
148 -- example, consider parsing lines separated by "\r\n". The main parser
149 -- will have to detect and exclude the sequence "\r\n" anyway so that we
150 -- can apply the "sep" parser.
152 -- We can instead implement these as special cases of deintercalate.
163 -- A simple and stupid impl would be to just convert the stream to an array
164 -- and give the array reference to all consumers. The array can be grown on
165 -- demand by any consumer and truncated when nonbody needs it.
167 -- -- ** Distribute to collection
168 -- -- ** Distribute to repetition
170 -- -- ** Interleaved collection
173 -- -- ** Interleaved repetition
174 -- repeat one parser and when it fails run an error recovery parser
175 -- e.g. to find a key frame in the stream after an error
177 -- ** Collection of Alternatives
180 -- , fastestN -- first N successful in time
181 -- , choiceN -- first N successful in position
182 , choice -- first successful in position
184 -- -- ** Repeated Alternatives
185 -- , retryMax -- try N times
186 -- , retryUntil -- try until successful
187 -- , retryUntilN -- try until successful n times
198import Control.Exception (assert, Exception)
199import Control.Monad (when)
200import Control.Monad.Catch (MonadCatch, MonadThrow(..))
201import Data.Bifunctor (first)
202import Fusion.Plugin.Types (Fuse(..))
203import Streamly.Internal.Data.Fold.Type (Fold(..))
204import Streamly.Internal.Data.SVar.Type (defState)
205import Streamly.Internal.Data.Either.Strict (Either'(..))
206import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
207import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
209import qualified Streamly.Internal.Data.Fold.Type as FL
210import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
211import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
214 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span
216import Streamly.Internal.Data.Parser.ParserD.Tee
217import Streamly.Internal.Data.Parser.ParserD.Type
222-- >>> :set -package streamly
223-- >>> import Prelude hiding ()
224-- >>> import qualified Data.Maybe as Maybe
225-- >>> import qualified Streamly.Prelude as Stream
226-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
227-- >>> import qualified Streamly.Data.Fold as Fold
228-- >>> import qualified Streamly.Internal.Data.Parser as Parser
230-------------------------------------------------------------------------------
231-- Downgrade a parser to a Fold
232-------------------------------------------------------------------------------
234data ParserToFoldError =
242instance Exception ParserToFoldError
244-- | See 'Streamly.Internal.Data.Parser.toFold'.
249toFold :: MonadThrow m => Parser m a b -> Fold m a b
250toFold (Parser pstep pinitial pextract) = Fold step initial pextract
257 IPartial s -> return $ FL.Partial s
258 IDone b -> return $ FL.Done b
259 IError err -> throwM $ InitialError err
264 Partial 0 s -> return $ FL.Partial s
265 Continue 0 s -> return $ FL.Partial s
266 Done 0 b -> return $ FL.Done b
267 Partial n _ -> throwM $ PartialError n
268 Continue n _ -> throwM $ ContinueError n
269 Done n _ -> throwM $ DoneError n
270 Error err -> throwM $ ErrorError err
272-------------------------------------------------------------------------------
273-- Upgrade folds to parses
274-------------------------------------------------------------------------------
276-- | See 'Streamly.Internal.Data.Parser.fromFold'.
280{-# INLINE fromFold #-}
281fromFold :: Monad m => Fold m a b -> Parser m a b
282fromFold (Fold fstep finitial fextract) = Parser step initial fextract
290 FL.Partial s1 -> IPartial s1
297 FL.Partial s1 -> Partial 0 s1
298 FL.Done b -> Done 0 b
300-------------------------------------------------------------------------------
302-------------------------------------------------------------------------------
304-- | See 'Streamly.Internal.Data.Parser.peek'.
309peek :: MonadThrow m => Parser m a a
310peek = Parser step initial extract
314 initial = return $ IPartial ()
316 step () a = return $ Done 1 a
318 extract () = throwM $ ParseError "peek: end of input"
320-- | See 'Streamly.Internal.Data.Parser.eof'.
325eof :: Monad m => Parser m a ()
326eof = Parser step initial return
330 initial = return $ IPartial ()
332 step () _ = return $ Error "eof: not at end of input"
334-- | See 'Streamly.Internal.Data.Parser.next'.
339next :: Monad m => Parser m a (Maybe a)
340next = Parser step initial extract
342 initial = pure $ IPartial ()
343 step _ a = pure $ Done 0 (Just a)
344 extract _ = pure Nothing
346-- | See 'Streamly.Internal.Data.Parser.either'.
351either :: MonadThrow m => (a -> Either String b) -> Parser m a b
352either f = Parser step initial extract
356 initial = return $ IPartial ()
361 Left err -> Error err
363 extract _ = throwM $ ParseError "end of input"
365-- | See 'Streamly.Internal.Data.Parser.maybe'.
367-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
368-- >>> maybe f = Parser.either (toEither . f)
373maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
374-- maybe f = either (Maybe.maybe (Left "maybe: predicate failed") Right . f)
375maybe parserF = Parser step initial extract
379 initial = return $ IPartial ()
384 Nothing -> Error "maybe: predicate failed"
386 extract _ = throwM $ ParseError "maybe: end of input"
388-- | See 'Streamly.Internal.Data.Parser.satisfy'.
390-- >>> toMaybe f x = if f x then Just x else Nothing
391-- >>> satisfy f = Parser.maybe (toMaybe f)
395{-# INLINE satisfy #-}
396satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
397-- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)
398satisfy predicate = Parser step initial extract
402 initial = return $ IPartial ()
407 else Error "satisfy: predicate failed"
409 extract _ = throwM $ ParseError "satisfy: end of input"
411-------------------------------------------------------------------------------
413-------------------------------------------------------------------------------
415-- Required to fuse "take" with "many" in "chunksOf", for ghc-9.x
416{-# ANN type Tuple'Fused Fuse #-}
417data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
419-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
423{-# INLINE takeBetween #-}
424takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
425takeBetween low high (Fold fstep finitial fextract) =
427 Parser step initial (extract streamErr)
432 "takeBetween: Expecting alteast " ++ show low
433 ++ " elements, got " ++ show i
436 "takeBetween: lower bound - " ++ show low
437 ++ " is greater than higher bound - " ++ show high
439 foldErr :: Int -> String
441 "takeBetween: the collecting fold terminated after"
442 ++ " consuming" ++ show i ++ " elements"
443 ++ " minimum" ++ show low ++ " elements needed"
445 -- Exactly the same as snext except different constructors, we can possibly
446 -- deduplicate the two.
452 let s1 = Tuple'Fused i1 s
454 -- XXX ideally this should be a Continue instead
455 then return $ IPartial s1
456 else IDone <$> extract foldErr s1
461 else IError (foldErr i1)
464 when (low >= 0 && high >= 0 && low > high)
465 $ throwM $ ParseError invalidRange
467 finitial >>= inext (-1)
469 -- Keep the impl same as inext
475 let s1 = Tuple'Fused i1 s
477 then return $ Continue 0 s1
478 else Done 0 <$> extract foldErr s1
483 else Error (foldErr i1)
485 step (Tuple'Fused i s) a = fstep s a >>= snext i
487 extract f (Tuple'Fused i s)
488 | i >= low && i <= high = fextract s
489 | otherwise = throwM $ ParseError (f i)
491-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
496takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
497takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
506 FL.Partial s -> IPartial $ Tuple' 0 s
511 $ "takeEQ: Expecting exactly " ++ show cnt
512 ++ " elements, fold terminated without"
513 ++ " consuming any elements"
520 FL.Partial s -> Continue 0 $ Tuple' i1 s
523 $ "takeEQ: Expecting exactly " ++ show cnt
524 ++ " elements, fold terminated on " ++ show i1
529 FL.Partial s -> fextract s
530 FL.Done b -> return b
531 -- XXX we should not reach here when initial returns Step type
532 -- reachable only when n == 0
533 | otherwise = Done 1 <$> fextract r
540 | i == 0 && cnt == 0 = fextract r
544 $ "takeEQ: Expecting exactly " ++ show cnt
545 ++ " elements, input terminated on " ++ show i
547-- | See 'Streamly.Internal.Data.Parser.takeGE'.
552takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
553takeGE n (Fold fstep finitial fextract) = Parser step initial extract
561 FL.Partial s -> IPartial $ Tuple' 0 s
566 $ "takeGE: Expecting at least " ++ show cnt
567 ++ " elements, fold terminated without"
568 ++ " consuming any elements"
575 FL.Partial s -> Continue 0 $ Tuple' i1 s
578 $ "takeGE: Expecting at least " ++ show cnt
579 ++ " elements, fold terminated on " ++ show i1
584 FL.Partial s -> Partial 0 $ Tuple' i1 s
585 FL.Done b -> Done 0 b
592 | i >= cnt = fextract r
596 $ "takeGE: Expecting at least " ++ show cnt
597 ++ " elements, input terminated on " ++ show i
599-------------------------------------------------------------------------------
600-- Conditional splitting
601-------------------------------------------------------------------------------
603-- | See 'Streamly.Internal.Data.Parser.takeWhileP'.
607{-# INLINE takeWhileP #-}
608takeWhileP :: Monad m => (a -> Bool) -> Parser m a b -> Parser m a b
609takeWhileP predicate (Parser pstep pinitial pextract) =
610 Parser step pinitial pextract
617 else Done 1 <$> pextract s
619-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
623{-# INLINE takeWhile #-}
624takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
625takeWhile predicate (Fold fstep finitial fextract) =
626 Parser step initial fextract
633 FL.Partial s -> IPartial s
642 FL.Partial s1 -> Partial 0 s1
643 FL.Done b -> Done 0 b
644 else Done 1 <$> fextract s
646-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
650{-# INLINE takeWhile1 #-}
651takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
652takeWhile1 predicate (Fold fstep finitial fextract) =
653 Parser step initial extract
660 FL.Partial s -> IPartial (Left' s)
663 $ "takeWhile1: fold terminated without consuming:"
666 {-# INLINE process #-}
671 FL.Partial s1 -> Partial 0 (Right' s1)
672 FL.Done b -> Done 0 b
677 else return $ Error "takeWhile1: predicate failed on first element"
685 extract (Left' _) = throwM $ ParseError "takeWhile1: end of input"
686 extract (Right' s) = fextract s
688-------------------------------------------------------------------------------
690-------------------------------------------------------------------------------
692{-# INLINE takeFramedByGeneric #-}
693takeFramedByGeneric :: MonadThrow m =>
699takeFramedByGeneric esc begin end (Fold fstep finitial fextract) =
701 Parser step initial extract
709 FL.Partial s -> IPartial (FrameEscInit s)
711 error "takeFramedByGeneric: fold done without input"
713 {-# INLINE process #-}
718 FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)
719 FL.Done b -> Done 0 b
721 {-# INLINE processNoEsc #-}
731 then Done 0 <$> fextract s
732 else process s a (n - 1)
734 let n1 = if isBegin a then n + 1 else n
736 Nothing -> -- takeEndBy case
738 then Done 0 <$> fextract s
740 Nothing -> -- takeStartBy case
744 then Done 0 <$> fextract s
747 error $ "takeFramedByGeneric: "
748 ++ "Both begin and end frame predicate missing"
750 {-# INLINE processCheckEsc #-}
751 processCheckEsc s a n =
755 then return $ Partial 0 $ FrameEscEsc s n
756 else processNoEsc s a n
757 Nothing -> processNoEsc s a n
759 step (FrameEscInit s) a =
763 then return $ Partial 0 (FrameEscGo s 0)
764 else return $ Error "takeFramedByGeneric: missing frame start"
769 then Done 0 <$> fextract s
770 else processCheckEsc s a 0
772 error "Both begin and end frame predicate missing"
773 step (FrameEscGo s n) a = processCheckEsc s a n
774 step (FrameEscEsc s n) a = process s a n
776 err = throwM . ParseError
778 extract (FrameEscInit _) =
779 err "takeFramedByGeneric: empty token"
780 extract (FrameEscGo s _) =
784 Nothing -> fextract s
785 Just _ -> err "takeFramedByGeneric: missing frame end"
786 Nothing -> err "takeFramedByGeneric: missing closing frame"
787 extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"
789-- | See 'Streamly.Internal.Data.Parser.takeEndBy'.
793{-# INLINE takeEndBy #-}
794takeEndBy :: MonadCatch m => (a -> Bool) -> Parser m a b -> Parser m a b
795takeEndBy cond (Parser pstep pinitial pextract) =
797 Parser step initial pextract
807 else extractStep pextract res
809-- | See 'Streamly.Internal.Data.Parser.takeEndByEsc'.
813{-# INLINE takeEndByEsc #-}
814takeEndByEsc :: MonadCatch m =>
815 (a -> Bool) -> (a -> Bool) -> Parser m a b -> Parser m a b
816takeEndByEsc isEsc isSep (Parser pstep pinitial pextract) =
818 Parser step initial extract
822 initial = first Left' <$> pinitial
824 step (Left' s) a = do
826 then return $ Partial 0 $ Right' s
830 then return $ mapStateStep Left' res
831 else extractStep pextract res
833 step (Right' s) a = do
835 return $ mapStateStep Left' res
837 extract (Left' s) = pextract s
839 throwM $ ParseError "takeEndByEsc: trailing escape"
841-- | See 'Streamly.Internal.Data.Parser.takeEndBy_'.
845{-# INLINE takeEndBy_ #-}
846takeEndBy_ :: MonadCatch m =>
847 (a -> Bool) -> Parser m a b -> Parser m a b
848takeEndBy_ cond (Parser pstep pinitial pextract) =
850 Parser step initial pextract
863-- | See 'Streamly.Internal.Data.Parser.takeStartBy'.
868{-# INLINE takeStartBy #-}
869takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
870takeStartBy cond (Fold fstep finitial fextract) =
872 Parser step initial extract
880 FL.Partial s -> IPartial (Left' s)
881 FL.Done _ -> IError "takeStartBy: fold done without input"
883 {-# INLINE process #-}
888 FL.Partial s1 -> Partial 0 (Right' s1)
889 FL.Done b -> Done 0 b
894 else return $ Error "takeStartBy: missing frame start"
898 else Done 1 <$> fextract s
900 extract (Left' s) = fextract s
901 extract (Right' s) = fextract s
903data FramedEscState s =
904 FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int
906{-# INLINE takeFramedByEsc_ #-}
907takeFramedByEsc_ :: MonadThrow m =>
908 (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
909takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
911 Parser step initial extract
919 FL.Partial s -> IPartial (FrameEscInit s)
921 error "takeFramedByEsc_: fold done without input"
923 {-# INLINE process #-}
928 FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)
929 FL.Done b -> Done 0 b
931 step (FrameEscInit s) a =
933 then return $ Partial 0 (FrameEscGo s 0)
934 else return $ Error "takeFramedByEsc_: missing frame start"
935 step (FrameEscGo s n) a =
937 then return $ Partial 0 $ FrameEscEsc s n
941 let n1 = if isBegin a then n + 1 else n
945 then Done 0 <$> fextract s
946 else process s a (n - 1)
947 step (FrameEscEsc s n) a = process s a n
949 err = throwM . ParseError
951 extract (FrameEscInit _) = err "takeFramedByEsc_: empty token"
952 extract (FrameEscGo _ _) = err "takeFramedByEsc_: missing frame end"
953 extract (FrameEscEsc _ _) = err "takeFramedByEsc_: trailing escape"
955data FramedState s = FrameInit !s | FrameGo !s Int
957{-# INLINE takeFramedBy_ #-}
958takeFramedBy_ :: MonadThrow m =>
959 (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
960takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
962 Parser step initial extract
970 FL.Partial s -> IPartial (FrameInit s)
972 error "takeFramedBy_: fold done without input"
974 {-# INLINE process #-}
979 FL.Partial s1 -> Continue 0 (FrameGo s1 n)
980 FL.Done b -> Done 0 b
982 step (FrameInit s) a =
984 then return $ Continue 0 (FrameGo s 0)
985 else return $ Error "takeFramedBy_: missing frame start"
988 let n1 = if isBegin a then n + 1 else n
990 | n == 0 = Done 0 <$> fextract s
991 | otherwise = process s a (n - 1)
993 err = throwM . ParseError
995 extract (FrameInit _) = err "takeFramedBy_: empty token"
996 extract (FrameGo _ _) = err "takeFramedBy_: missing frame end"
998-------------------------------------------------------------------------------
1000-------------------------------------------------------------------------------
1002data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
1004-- | See 'Streamly.Internal.Data.Parser.wordBy'.
1007{-# INLINE wordBy #-}
1008wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
1009wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
1013 {-# INLINE worder #-}
1018 FL.Partial s1 -> Partial 0 $ WBWord s1
1019 FL.Done b -> Done 0 b
1025 FL.Partial s -> IPartial $ WBLeft s
1026 FL.Done b -> IDone b
1029 if not (predicate a)
1031 else return $ Partial 0 $ WBLeft s
1033 if not (predicate a)
1037 return $ Partial 0 $ WBRight b
1038 step (WBRight b) a =
1040 $ if not (predicate a)
1042 else Partial 0 $ WBRight b
1044 extract (WBLeft s) = fextract s
1045 extract (WBWord s) = fextract s
1046 extract (WBRight b) = return b
1048data WordFramedState s b =
1049 WordFramedSkipPre !s
1050 | WordFramedWord !s !Int
1051 | WordFramedEsc !s !Int
1052 | WordFramedSkipPost !b
1054-- | See 'Streamly.Internal.Data.Parser.wordFramedBy'
1056{-# INLINE wordFramedBy #-}
1057wordFramedBy :: MonadCatch m =>
1058 (a -> Bool) -- ^ Escape
1059 -> (a -> Bool) -- ^ left quote
1060 -> (a -> Bool) -- ^ right quote
1061 -> (a -> Bool) -- ^ word seperator
1064wordFramedBy isEsc isBegin isEnd isSep
1065 (Fold fstep finitial fextract) =
1066 Parser step initial extract
1074 FL.Partial s -> IPartial (WordFramedSkipPre s)
1076 error "wordFramedBy: fold done without input"
1078 {-# INLINE process #-}
1083 FL.Partial s1 -> Continue 0 (WordFramedWord s1 n)
1084 FL.Done b -> Done 0 b
1086 step (WordFramedSkipPre s) a
1087 | isEsc a = return $ Continue 0 $ WordFramedEsc s 0
1088 | isSep a = return $ Partial 0 $ WordFramedSkipPre s
1089 | isBegin a = return $ Continue 0 $ WordFramedWord s 1
1091 return $ Error "wordFramedBy: missing frame start"
1092 | otherwise = process s a 0
1093 step (WordFramedWord s n) a
1094 | isEsc a = return $ Continue 0 $ WordFramedEsc s n
1095 | n == 0 && isSep a = do
1097 return $ Partial 0 $ WordFramedSkipPost b
1099 -- We need to use different order for checking begin and end for
1100 -- the n == 0 and n == 1 case so that when the begin and end
1101 -- character is the same we treat the one after begin as end.
1104 -- Need to check isBegin first
1106 then return $ Continue 0 $ WordFramedWord s 1
1108 then return $ Error "wordFramedBy: missing frame start"
1111 -- Need to check isEnd first
1115 then return $ Continue 0 $ WordFramedWord s 0
1116 else process s a (n - 1)
1118 then process s a (n + 1)
1120 step (WordFramedEsc s n) a = process s a n
1121 step (WordFramedSkipPost b) a =
1125 else Partial 0 $ WordFramedSkipPost b
1127 err = throwM . ParseError
1129 extract (WordFramedSkipPre s) = fextract s
1130 extract (WordFramedWord s n) =
1133 else err "wordFramedBy: missing frame end"
1134 extract (WordFramedEsc _ _) =
1135 err "wordFramedBy: trailing escape"
1136 extract (WordFramedSkipPost b) = return b
1138data WordQuotedState s b a =
1139 WordQuotedSkipPre !s
1140 | WordUnquotedWord !s
1141 | WordQuotedWord !s !Int a
1142 | WordUnquotedEsc !s
1143 | WordQuotedEsc !s !Int a
1144 | WordQuotedSkipPost !b
1146{-# INLINE wordQuotedBy #-}
1147wordQuotedBy :: (MonadCatch m, Eq a) =>
1148 (a -> Bool) -- ^ Escape
1149 -> (a -> Bool) -- ^ left quote
1150 -> (a -> Bool) -- ^ right quote
1151 -> (a -> a) -- ^ get right quote from the left quote
1152 -> (a -> Bool) -- ^ word seperator
1155wordQuotedBy isEsc isBegin isEnd toRight isSep
1156 (Fold fstep finitial fextract) =
1157 Parser step initial extract
1165 FL.Partial s -> IPartial (WordQuotedSkipPre s)
1167 error "wordQuotedBy: fold done without input"
1169 {-# INLINE process #-}
1170 process s a n q = do
1174 FL.Partial s1 -> Continue 0 (WordQuotedWord s1 n q)
1175 FL.Done b -> Done 0 b
1177 {-# INLINE processUnquoted #-}
1178 processUnquoted s a = do
1182 FL.Partial s1 -> Continue 0 (WordUnquotedWord s1)
1183 FL.Done b -> Done 0 b
1185 step (WordQuotedSkipPre s) a
1186 | isEsc a = return $ Continue 0 $ WordUnquotedEsc s
1187 | isSep a = return $ Partial 0 $ WordQuotedSkipPre s
1188 | isBegin a = return $ Continue 0 $ WordQuotedWord s 1 a
1190 return $ Error "wordQuotedBy: missing frame start"
1191 | otherwise = processUnquoted s a
1192 step (WordUnquotedWord s) a
1193 | isEsc a = return $ Continue 0 $ WordUnquotedEsc s
1196 return $ Partial 0 $ WordQuotedSkipPost b
1199 then return $ Continue 0 $ WordQuotedWord s 1 a
1201 then return $ Error "wordQuotedBy: missing frame start"
1202 else processUnquoted s a
1203 step (WordQuotedWord s n q) a
1204 | isEsc a = return $ Continue 0 $ WordQuotedEsc s n q
1205 | n == 0 && isSep a = do
1207 return $ Partial 0 $ WordQuotedSkipPost b
1212 then return $ Continue 0 $ WordUnquotedWord s
1213 else process s a (n - 1) q
1215 then process s a (n + 1) q
1216 else process s a n q
1217 step (WordUnquotedEsc s) a = processUnquoted s a
1218 step (WordQuotedEsc s n q) a = process s a n q
1219 step (WordQuotedSkipPost b) a =
1223 else Partial 0 $ WordQuotedSkipPost b
1225 err = throwM . ParseError
1227 extract (WordQuotedSkipPre s) = fextract s
1228 extract (WordUnquotedWord s) = fextract s
1229 extract (WordQuotedWord s n _) =
1232 else err "wordQuotedBy: missing frame end"
1233 extract WordQuotedEsc {} =
1234 err "wordQuotedBy: trailing escape"
1235 extract (WordUnquotedEsc _) =
1236 err "wordQuotedBy: trailing escape"
1237 extract (WordQuotedSkipPost b) = return b
1239{-# ANN type GroupByState Fuse #-}
1240data GroupByState a s
1242 | GroupByGrouping !a !s
1244-- | See 'Streamly.Internal.Data.Parser.groupBy'.
1246{-# INLINE groupBy #-}
1247groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
1248groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
1252 {-# INLINE grouper #-}
1257 FL.Done b -> Done 0 b
1258 FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
1264 FL.Partial s -> IPartial $ GroupByInit s
1265 FL.Done b -> IDone b
1267 step (GroupByInit s) a = grouper s a a
1268 step (GroupByGrouping a0 s) a =
1271 else Done 1 <$> fextract s
1273 extract (GroupByInit s) = fextract s
1274 extract (GroupByGrouping _ s) = fextract s
1276-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
1278{-# INLINE groupByRolling #-}
1279groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
1280groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
1284 {-# INLINE grouper #-}
1289 FL.Done b -> Done 0 b
1290 FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
1296 FL.Partial s -> IPartial $ GroupByInit s
1297 FL.Done b -> IDone b
1299 step (GroupByInit s) a = grouper s a
1300 step (GroupByGrouping a0 s) a =
1303 else Done 1 <$> fextract s
1305 extract (GroupByInit s) = fextract s
1306 extract (GroupByGrouping _ s) = fextract s
1308{-# ANN type GroupByStatePair Fuse #-}
1309data GroupByStatePair a s1 s2
1310 = GroupByInitPair !s1 !s2
1311 | GroupByGroupingPair !a !s1 !s2
1312 | GroupByGroupingPairL !a !s1 !s2
1313 | GroupByGroupingPairR !a !s1 !s2
1315{-# INLINE groupByRollingEither #-}
1316groupByRollingEither :: MonadCatch m =>
1317 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (Either b c)
1320 (Fold fstep1 finitial1 fextract1)
1321 (Fold fstep2 finitial2 fextract2) = Parser step initial extract
1325 {-# INLINE grouper #-}
1326 grouper s1 s2 a = do
1327 return $ Continue 0 (GroupByGroupingPair a s1 s2)
1329 {-# INLINE grouperL2 #-}
1330 grouperL2 s1 s2 a = do
1334 FL.Done b -> Done 0 (Left b)
1335 FL.Partial s11 -> Partial 0 (GroupByGroupingPairL a s11 s2)
1337 {-# INLINE grouperL #-}
1338 grouperL s1 s2 a0 a = do
1341 FL.Done b -> return $ Done 0 (Left b)
1342 FL.Partial s11 -> grouperL2 s11 s2 a
1344 {-# INLINE grouperR2 #-}
1345 grouperR2 s1 s2 a = do
1349 FL.Done b -> Done 0 (Right b)
1350 FL.Partial s21 -> Partial 0 (GroupByGroupingPairR a s1 s21)
1352 {-# INLINE grouperR #-}
1353 grouperR s1 s2 a0 a = do
1356 FL.Done b -> return $ Done 0 (Right b)
1357 FL.Partial s21 -> grouperR2 s1 s21 a
1366 FL.Partial s2 -> IPartial $ GroupByInitPair s1 s2
1367 FL.Done b -> IDone (Right b)
1368 FL.Done b -> IDone (Left b)
1370 step (GroupByInitPair s1 s2) a = grouper s1 s2 a
1372 step (GroupByGroupingPair a0 s1 s2) a =
1374 then grouperL s1 s2 a0 a
1375 else grouperR s1 s2 a0 a
1377 step (GroupByGroupingPairL a0 s1 s2) a =
1379 then grouperL2 s1 s2 a
1380 else Done 1 . Left <$> fextract1 s1
1382 step (GroupByGroupingPairR a0 s1 s2) a =
1384 then grouperR2 s1 s2 a
1385 else Done 1 . Right <$> fextract2 s2
1387 extract (GroupByInitPair s1 _) = Left <$> fextract1 s1
1388 extract (GroupByGroupingPairL _ s1 _) = Left <$> fextract1 s1
1389 extract (GroupByGroupingPairR _ _ s2) = Right <$> fextract2 s2
1390 extract (GroupByGroupingPair a s1 _) = do
1393 FL.Done b -> return $ Left b
1394 FL.Partial s11 -> Left <$> fextract1 s11
1396-- XXX use an Unfold instead of a list?
1397-- XXX custom combinators for matching list, array and stream?
1398-- XXX rename to listBy?
1400-- | See 'Streamly.Internal.Data.Parser.eqBy'.
1405eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
1406eqBy cmp str = Parser step initial extract
1410 -- XXX Should return IDone in initial for [] case
1411 initial = return $ IPartial str
1413 step [] _ = return $ Done 0 ()
1418 else Error "eqBy: failed, yet to match the last element"
1424 $ "eqBy: failed, yet to match "
1425 ++ show (length xs + 1) ++ " elements"
1430 $ "eqBy: end of input, yet to match "
1431 ++ show (length xs) ++ " elements"
1433-- XXX rename to streamBy?
1434-- | Like eqBy but uses a stream instead of a list
1435{-# INLINE matchBy #-}
1436matchBy :: MonadThrow m => (a -> a -> Bool) -> D.Stream m a -> Parser m a ()
1437matchBy cmp (D.Stream sstep state) = Parser step initial extract
1442 r <- sstep defState state
1444 D.Yield x s -> return $ IPartial (Just' x, s)
1445 D.Stop -> return $ IDone ()
1446 -- Need Skip/Continue in initial to loop right here
1447 D.Skip s -> return $ IPartial (Nothing', s)
1449 step (Just' x, st) a =
1452 r <- sstep defState st
1455 D.Yield x1 s -> Continue 0 (Just' x1, s)
1457 D.Skip s -> Continue 1 (Nothing', s)
1458 else return $ Error "match: mismtach occurred"
1459 step (Nothing', st) a = do
1460 r <- sstep defState st
1465 then Continue 0 (Nothing', s)
1466 else Error "match: mismatch occurred"
1468 D.Skip s -> Continue 1 (Nothing', s)
1470 extract _ = throwM $ ParseError "match: end of input"
1472-------------------------------------------------------------------------------
1473-- Transformations on input
1474-------------------------------------------------------------------------------
1476-- Initial needs a "Continue" constructor to implement scans on parsers. As a
1477-- parser can always return a Continue in initial when we feed the fold's
1478-- initial result to it. We can work this around for postscan by introducing an
1479-- initial state and calling "initial" only on the first input.
1481-- | Stateful scan on the input of a parser using a Fold.
1485{-# INLINE postscan #-}
1486postscan :: -- Monad m =>
1487 Fold m a b -> Parser m b c -> Parser m a c
1490{-# INLINE zipWithM #-}
1491zipWithM :: MonadThrow m =>
1492 (a -> b -> m c) -> D.Stream m a -> Fold m c x -> Parser m b x
1493zipWithM zf (D.Stream sstep state) (Fold fstep finitial fextract) =
1494 Parser step initial extract
1502 r <- sstep defState state
1504 D.Yield x s -> return $ IPartial (Just' x, s, fs)
1508 -- Need Skip/Continue in initial to loop right here
1509 D.Skip s -> return $ IPartial (Nothing', s, fs)
1510 FL.Done x -> return $ IDone x
1512 step (Just' a, st, fs) b = do
1516 FL.Partial fs1 -> do
1517 r <- sstep defState st
1519 D.Yield x1 s -> return $ Continue 0 (Just' x1, s, fs1)
1523 D.Skip s -> return $ Continue 1 (Nothing', s, fs1)
1524 FL.Done x -> return $ Done 0 x
1525 step (Nothing', st, fs) b = do
1526 r <- sstep defState st
1533 return $ Continue 0 (Nothing', s, fs1)
1534 FL.Done x -> return $ Done 0 x
1538 D.Skip s -> return $ Continue 1 (Nothing', s, fs)
1540 extract _ = throwM $ ParseError "zipWithM: end of input"
1542-- | Zip the input of a fold with a stream.
1547zip :: MonadThrow m => D.Stream m a -> Fold m (a, b) x -> Parser m b x
1548zip = zipWithM (curry return)
1550-- | Pair each element of a fold input with its index, starting from index 0.
1553{-# INLINE indexed #-}
1554indexed :: forall m a b. MonadThrow m => Fold m (Int, a) b -> Parser m a b
1555indexed = zip (D.enumerateFromIntegral 0 :: D.Stream m Int)
1557-- | @makeIndexFilter indexer filter predicate@ generates a fold filtering
1558-- function using a fold indexing function that attaches an index to each input
1559-- element and a filtering function that filters using @(index, element) ->
1560-- Bool) as predicate.
1565-- filterWithIndex = makeIndexFilter indexed filter
1566-- filterWithAbsTime = makeIndexFilter timestamped filter
1567-- filterWithRelTime = makeIndexFilter timeIndexed filter
1571{-# INLINE makeIndexFilter #-}
1573 (Fold m (s, a) b -> Parser m a b)
1574 -> (((s, a) -> Bool) -> Fold m (s, a) b -> Fold m (s, a) b)
1575 -> (((s, a) -> Bool) -> Fold m a b -> Parser m a b)
1576makeIndexFilter f comb g = f . comb g . FL.lmap snd
1578-- | @sampleFromthen offset stride@ samples the element at @offset@ index and
1579-- then every element at strides of @stride@.
1582{-# INLINE sampleFromthen #-}
1583sampleFromthen :: MonadThrow m => Int -> Int -> Fold m a b -> Parser m a b
1584sampleFromthen offset size =
1585 makeIndexFilter indexed FL.filter (\(i, _) -> (i + offset) `mod` size == 0)
1587--------------------------------------------------------------------------------
1589--------------------------------------------------------------------------------
1591-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the
1592-- input as long as the predicate @p@ is 'True'. @f2@ consumes the rest of the
1596-- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs
1598-- > span_ (< 1) [1,2,3]
1601-- > span_ (< 2) [1,2,3]
1604-- > span_ (< 4) [1,2,3]
1611span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1612span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)
1614-- | Break the input stream into two groups, the first group takes the input as
1615-- long as the predicate applied to the first element of the stream and next
1616-- input element holds 'True', the second group takes the rest of the input.
1620{-# INLINE spanBy #-}
1623 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1624spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)
1626-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
1627-- predicate is applied to the previous and the next input elements.
1630{-# INLINE spanByRolling #-}
1633 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
1634spanByRolling eq f1 f2 =
1635 noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)
1637-------------------------------------------------------------------------------
1639-------------------------------------------------------------------------------
1641-- | See 'Streamly.Internal.Data.Parser.takeP'.
1645takeP :: Monad m => Int -> Parser m a b -> Parser m a b
1646takeP lim (Parser pstep pinitial pextract) = Parser step initial extract
1655 then return $ IPartial $ Tuple' 0 s
1656 else IDone <$> pextract s
1657 IDone b -> return $ IDone b
1658 IError e -> return $ IError e
1660 step (Tuple' cnt r) a = do
1661 assert (cnt < lim) (return ())
1666 assert (cnt1 >= 0) (return ())
1668 then return $ Partial 0 $ Tuple' cnt1 s
1669 else Done 0 <$> pextract s
1671 assert (cnt1 >= 0) (return ())
1673 then return $ Continue 0 $ Tuple' cnt1 s
1674 -- XXX This should error out?
1675 -- If designed properly, this will probably error out.
1676 -- "pextract" should error out
1680 -- This is a tricky case, we have the following options:
1681 -- 1. Done 0 with extract as you have written
1682 -- 2. Done n, will require buffering elements
1683 -- 3. Use a backtracking fold and not a parser, once we have
1684 -- backtracking in folds
1685 else Done 0 <$> pextract s
1687 let taken = cnt1 - n
1688 assert (taken >= 0) (return ())
1689 return $ Partial n $ Tuple' taken s
1691 let taken = cnt1 - n
1692 assert (taken >= 0) (return ())
1693 return $ Continue n $ Tuple' taken s
1694 Done n b -> return $ Done n b
1695 Error str -> return $ Error str
1697 extract (Tuple' _ r) = pextract r
1699-- | See 'Streamly.Internal.Data.Parser.lookahead'.
1703{-# INLINE lookAhead #-}
1704lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
1705lookAhead (Parser step1 initial1 _) = Parser step initial extract
1711 return $ case res of
1712 IPartial s -> IPartial (Tuple' 0 s)
1714 IError e -> IError e
1716 step (Tuple' cnt st) a = do
1721 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
1722 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
1723 Done _ b -> Done cnt1 b
1724 Error err -> Error err
1726 -- XXX returning an error let's us backtrack. To implement it in a way so
1727 -- that it terminates on eof without an error then we need a way to
1728 -- backtrack on eof, that will require extract to return 'Step' type.
1729 extract (Tuple' n _) =
1732 $ "lookAhead: end of input after consuming "
1733 ++ show n ++ " elements"
1735-------------------------------------------------------------------------------
1737-------------------------------------------------------------------------------
1739data DeintercalateState fs sp ss =
1740 DeintercalateL !fs !sp
1741 | DeintercalateR !fs !ss !Bool
1743-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
1747{-# INLINE deintercalate #-}
1748deintercalate :: Monad m =>
1749 Fold m (Either x y) z
1754 (Fold fstep finitial fextract)
1755 (Parser stepL initialL extractL)
1756 (Parser stepR initialR extractR) = Parser step initial extract
1761 error $ "deintercalate: " ++ p ++ " parser cannot "
1762 ++ status ++ " without input"
1770 IPartial sL -> return $ IPartial $ DeintercalateL fs sL
1771 IDone _ -> errMsg "left" "succeed"
1772 IError _ -> errMsg "left" "fail"
1773 FL.Done c -> return $ IDone c
1775 step (DeintercalateL fs sL) a = do
1778 Partial n s -> return $ Partial n (DeintercalateL fs s)
1779 Continue n s -> return $ Continue n (DeintercalateL fs s)
1781 fres <- fstep fs (Left b)
1783 FL.Partial fs1 -> do
1788 $ Partial n (DeintercalateR fs1 sR False)
1789 IDone _ -> errMsg "right" "succeed"
1790 IError _ -> errMsg "right" "fail"
1791 FL.Done c -> return $ Done n c
1792 Error err -> return $ Error err
1793 step (DeintercalateR fs sR consumed) a = do
1796 Partial n s -> return $ Partial n (DeintercalateR fs s True)
1797 Continue n s -> return $ Continue n (DeintercalateR fs s True)
1801 fres <- fstep fs (Right b)
1803 FL.Partial fs1 -> do
1807 return $ Partial n $ DeintercalateL fs1 sL
1808 IDone _ -> errMsg "left" "succeed"
1809 IError _ -> errMsg "left" "fail"
1810 FL.Done c -> return $ Done n c
1811 else error "deintercalate: infinite loop"
1812 Error err -> return $ Error err
1814 extract (DeintercalateL fs sL) = do
1816 res <- fstep fs (Left r)
1818 FL.Partial fs1 -> fextract fs1
1819 FL.Done c -> return c
1820 extract (DeintercalateR fs sR _) = do
1822 res <- fstep fs (Right r)
1824 FL.Partial fs1 -> fextract fs1
1825 FL.Done c -> return c
1827data SepByState fs sp ss =
1829 | SepBySeparator !fs !ss !Bool
1831-- This is a special case of deintercalate and can be easily implemented in
1832-- terms of deintercalate.
1834sepBy :: MonadCatch m =>
1835 Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
1837 (Fold fstep finitial fextract)
1838 (Parser pstep pinitial pextract)
1839 (Parser sstep sinitial _) = Parser step initial extract
1844 error $ "sepBy: " ++ p ++ " parser cannot "
1845 ++ status ++ " without input"
1853 IPartial sp -> return $ IPartial $ SepByInit fs sp
1854 IDone _ -> errMsg "content" "succeed"
1855 IError _ -> errMsg "content" "fail"
1856 FL.Done b -> return $ IDone b
1858 step (SepByInit fs sp) a = do
1861 Partial n s -> return $ Partial n (SepByInit fs s)
1862 Continue n s -> return $ Continue n (SepByInit fs s)
1866 FL.Partial fs1 -> do
1870 return $ Partial n (SepBySeparator fs1 ss False)
1871 IDone _ -> errMsg "separator" "succeed"
1872 IError _ -> errMsg "separator" "fail"
1873 FL.Done c -> return $ Done n c
1874 Error err -> return $ Error err
1875 step (SepBySeparator fs ss consumed) a = do
1878 Partial n s -> return $ Partial n (SepBySeparator fs s True)
1879 Continue n s -> return $ Continue n (SepBySeparator fs s True)
1885 IPartial sp -> return $ Partial n $ SepByInit fs sp
1886 IDone _ -> errMsg "content" "succeed"
1887 IError _ -> errMsg "content" "fail"
1888 else error "sepBy: infinite loop"
1889 Error err -> return $ Error err
1891 extract (SepByInit fs sp) = do
1895 FL.Partial fs1 -> fextract fs1
1896 FL.Done c -> return c
1897 extract (SepBySeparator fs _ _) = fextract fs
1899-------------------------------------------------------------------------------
1900-- Sequential Collection
1901-------------------------------------------------------------------------------
1903-- | See 'Streamly.Internal.Data.Parser.sequence'.
1907{-# INLINE sequence #-}
1910 Fold m b c -> t (Parser m a b) -> Parser m a c
1911sequence _f _p = undefined
1913-------------------------------------------------------------------------------
1914-- Alternative Collection
1915-------------------------------------------------------------------------------
1917-- | See 'Streamly.Internal.Data.Parser.choice'.
1921{-# INLINE choice #-}
1922choice :: (MonadCatch m, Foldable t) => t (Parser m a b) -> Parser m a b
1923choice = foldl1 shortest
1925-------------------------------------------------------------------------------
1926-- Sequential Repetition
1927-------------------------------------------------------------------------------
1930manyP :: -- MonadCatch m =>
1931 Parser m a b -> Parser m b c -> Parser m a c
1932manyP _p _f = undefined
1934-- | See 'Streamly.Internal.Data.Parser.many'.
1939many :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1941-- many = countBetween 0 maxBound
1943-- | See 'Streamly.Internal.Data.Parser.some'.
1948some :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1950-- some p f = manyP p (takeGE 1 f)
1951-- some = countBetween 1 maxBound
1953-- | See 'Streamly.Internal.Data.Parser.countBetween'.
1957{-# INLINE countBetween #-}
1958countBetween :: -- MonadCatch m =>
1959 Int -> Int -> Parser m a b -> Fold m b c -> Parser m a c
1960countBetween _m _n _p = undefined
1961-- countBetween m n p f = manyP p (takeBetween m n f)
1963-- | See 'Streamly.Internal.Data.Parser.count'.
1968count :: -- MonadCatch m =>
1969 Int -> Parser m a b -> Fold m b c -> Parser m a c
1970count n = countBetween n n
1971-- count n p f = manyP p (takeEQ n f)
1973data ManyTillState fs sr sl
1974 = ManyTillR Int fs sr
1975 | ManyTillL Int fs sl
1977-- | See 'Streamly.Internal.Data.Parser.manyTill'.
1981{-# INLINE manyTill #-}
1982manyTill :: MonadCatch m
1983 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
1984manyTill (Fold fstep finitial fextract)
1985 (Parser stepL initialL extractL)
1986 (Parser stepR initialR _) =
1987 Parser step initial extract
1991 -- Caution: Mutual recursion
1993 scrutL fs p c d e = do
1996 IPartial sl -> return $ c (ManyTillL 0 fs sl)
2000 FL.Partial fs1 -> scrutR fs1 p c d e
2001 FL.Done fb -> return $ d fb
2002 IError err -> return $ e err
2004 scrutR fs p c d e = do
2007 IPartial sr -> return $ p (ManyTillR 0 fs sr)
2008 IDone _ -> d <$> fextract fs
2009 IError _ -> scrutL fs p c d e
2014 FL.Partial fs -> scrutR fs IPartial IPartial IDone IError
2015 FL.Done b -> return $ IDone b
2017 step (ManyTillR cnt fs st) a = do
2020 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
2022 assert (cnt + 1 - n >= 0) (return ())
2023 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
2031 return $ Continue (cnt + 1) (ManyTillL 0 fs sl)
2039 FL.Partial fs1 -> scrutR fs1 p c d Error
2040 FL.Done fb -> return $ Done cnt1 fb
2041 IError err -> return $ Error err
2042 -- XXX the cnt is being used only by the assert
2043 step (ManyTillL cnt fs st) a = do
2046 Partial n s -> return $ Partial n (ManyTillL 0 fs s)
2048 assert (cnt + 1 - n >= 0) (return ())
2049 return $ Continue n (ManyTillL (cnt + 1 - n) fs s)
2054 scrutR s (Partial n) (Continue n) (Done n) Error
2055 FL.Done b1 -> return $ Done n b1
2056 Error err -> return $ Error err
2058 extract (ManyTillL _ fs sR) = do
2059 res <- extractL sR >>= fstep fs
2061 FL.Partial s -> fextract s
2062 FL.Done b -> return b
2063 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