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 -- First order parsers
38 -- Parsers chained in series, if one parser terminates the composition
39 -- terminates. Currently we are using folds to collect the output of the
40 -- parsers but we can use Parsers instead of folds to make the composition
41 -- more powerful. For example, we can do:
43 -- sliceSepByMax cond n p = sliceBy cond (take n p)
44 -- sliceSepByBetween cond m n p = sliceBy cond (takeBetween m n p)
45 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
47 -- Grab a sequence of input elements without inspecting them
49 -- , take -- take -- takeBetween 0 n
50 -- , takeLE1 -- take1 -- takeBetween 1 n
51 , takeEQ -- takeBetween n n
52 , takeGE -- takeBetween n maxBound
54 -- Grab a sequence of input elements by inspecting them
59 -- , sliceSepByBetween
63 -- , frameSepBy -- parse frames escaped by an escape char/sequence
70 -- , prefixOf -- match any prefix of a given string
71 -- , suffixOf -- match any suffix of a given string
72 -- , infixOf -- match any substring of a given string
79 -- Second order parsers (parsers using parsers)
80 -- * Binary Combinators
82 -- ** Sequential Applicative
86 -- ** Parallel Applicatives
90 -- , teeTill -- like manyTill but parallel
92 -- ** Sequential Interleaving
93 -- Use two folds, run a primary parser, its rejected values go to the
97 -- ** Sequential Alternative
100 -- ** Parallel Alternatives
105 -- * N-ary Combinators
106 -- ** Sequential Collection
110 -- ** Sequential Repetition
113 -- , countBetweenTill
119 -- -- ** Special cases
120 -- XXX traditional implmentations of these may be of limited use. For
121 -- example, consider parsing lines separated by "\r\n". The main parser
122 -- will have to detect and exclude the sequence "\r\n" anyway so that we
123 -- can apply the "sep" parser.
125 -- We can instead implement these as special cases of deintercalate.
136 -- A simple and stupid impl would be to just convert the stream to an array
137 -- and give the array reference to all consumers. The array can be grown on
138 -- demand by any consumer and truncated when nonbody needs it.
140 -- -- ** Distribute to collection
141 -- -- ** Distribute to repetition
143 -- -- ** Interleaved collection
146 -- -- ** Interleaved repetition
147 -- repeat one parser and when it fails run an error recovery parser
148 -- e.g. to find a key frame in the stream after an error
150 -- ** Collection of Alternatives
153 -- , fastestN -- first N successful in time
154 -- , choiceN -- first N successful in position
155 , choice -- first successful in position
157 -- -- ** Repeated Alternatives
158 -- , retryMax -- try N times
159 -- , retryUntil -- try until successful
160 -- , retryUntilN -- try until successful n times
164import Control.Exception (assert)
165import Control.Monad.Catch (MonadCatch, MonadThrow(..))
166import Fusion.Plugin.Types (Fuse(..))
167import Streamly.Internal.Data.Fold.Type (Fold(..))
168import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
170import qualified Streamly.Internal.Data.Fold.Type as FL
173 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span)
174import Streamly.Internal.Data.Parser.ParserD.Tee
175import Streamly.Internal.Data.Parser.ParserD.Type
180-- >>> import Prelude hiding ()
181-- >>> import qualified Streamly.Prelude as Stream
182-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
183-- >>> import qualified Streamly.Data.Fold as Fold
184-- >>> import qualified Streamly.Internal.Data.Parser as Parser
186-------------------------------------------------------------------------------
187-- Upgrade folds to parses
188-------------------------------------------------------------------------------
190-- | See 'Streamly.Internal.Data.Parser.fromFold'.
194{-# INLINE fromFold #-}
195fromFold :: Monad m => Fold m a b -> Parser m a b
196fromFold (Fold fstep finitial fextract) = Parser step initial fextract
204 FL.Partial s1 -> IPartial s1
211 FL.Partial s1 -> Partial 0 s1
212 FL.Done b -> Done 0 b
214-------------------------------------------------------------------------------
216-------------------------------------------------------------------------------
218-- | See 'Streamly.Internal.Data.Parser.peek'.
222{-# INLINABLE peek #-}
223peek :: MonadThrow m => Parser m a a
224peek = Parser step initial extract
228 initial = return $ IPartial ()
230 step () a = return $ Done 1 a
232 extract () = throwM $ ParseError "peek: end of input"
234-- | See 'Streamly.Internal.Data.Parser.eof'.
239eof :: Monad m => Parser m a ()
240eof = Parser step initial return
244 initial = return $ IPartial ()
246 step () _ = return $ Error "eof: not at end of input"
248-- | See 'Streamly.Internal.Data.Parser.satisfy'.
252{-# INLINE satisfy #-}
253satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
254satisfy predicate = Parser step initial extract
258 initial = return $ IPartial ()
263 else Error "satisfy: predicate failed"
265 extract _ = throwM $ ParseError "satisfy: end of input"
267-- | See 'Streamly.Internal.Data.Parser.maybe'.
272maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
273maybe parser = Parser step initial extract
277 initial = return $ IPartial ()
282 Nothing -> Error "maybe: predicate failed"
284 extract _ = throwM $ ParseError "maybe: end of input"
286-- | See 'Streamly.Internal.Data.Parser.either'.
291either :: MonadThrow m => (a -> Either String b) -> Parser m a b
292either parser = Parser step initial extract
296 initial = return $ IPartial ()
301 Left err -> Error $ "either: " ++ err
303 extract _ = throwM $ ParseError "either: end of input"
305-------------------------------------------------------------------------------
307-------------------------------------------------------------------------------
309-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
313{-# INLINE takeBetween #-}
314takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
315takeBetween low high (Fold fstep finitial fextract) =
317 Parser step initial extract
324 FL.Partial s -> IPartial $ Tuple' 0 s
329 $ "takeBetween: the collecting fold terminated without"
330 ++ " consuming any elements"
331 ++ " minimum" ++ show low ++ " elements needed"
337 $ "takeBetween: lower bound - " ++ show low
338 ++ " is greater than higher bound - " ++ show high
339 | high <= 0 = Done 1 <$> fextract s
344 FL.Partial s1 -> Continue 0 $ Tuple' i1 s1
347 $ "takeBetween: the collecting fold terminated after"
348 ++ " consuming" ++ show i1 ++ " elements"
349 ++ " minimum" ++ show low ++ " elements needed"
355 then Done 0 <$> fextract s1
356 else return $ Partial 0 $ Tuple' i1 s1
357 FL.Done b -> return $ Done 0 b
364 | i >= low && i <= high = fextract s
365 | otherwise = throwM $ ParseError err
370 "takeBetween: Expecting alteast " ++ show low
371 ++ " elements, got " ++ show i
373-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
378takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
379takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
388 FL.Partial s -> IPartial $ Tuple' 0 s
393 $ "takeEQ: Expecting exactly " ++ show cnt
394 ++ " elements, fold terminated without"
395 ++ " consuming any elements"
402 FL.Partial s -> Continue 0 $ Tuple' i1 s
405 $ "takeEQ: Expecting exactly " ++ show cnt
406 ++ " elements, fold terminated on " ++ show i1
411 FL.Partial s -> fextract s
412 FL.Done b -> return b
413 -- XXX we should not reach here when initial returns Step type
414 -- reachable only when n == 0
415 | otherwise = Done 1 <$> fextract r
422 | i == 0 && cnt == 0 = fextract r
426 $ "takeEQ: Expecting exactly " ++ show cnt
427 ++ " elements, input terminated on " ++ show i
429-- | See 'Streamly.Internal.Data.Parser.takeGE'.
434takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
435takeGE n (Fold fstep finitial fextract) = Parser step initial extract
443 FL.Partial s -> IPartial $ Tuple' 0 s
448 $ "takeGE: Expecting at least " ++ show cnt
449 ++ " elements, fold terminated without"
450 ++ " consuming any elements"
457 FL.Partial s -> Continue 0 $ Tuple' i1 s
460 $ "takeGE: Expecting at least " ++ show cnt
461 ++ " elements, fold terminated on " ++ show i1
466 FL.Partial s -> Partial 0 $ Tuple' i1 s
467 FL.Done b -> Done 0 b
474 | i >= cnt = fextract r
478 $ "takeGE: Expecting at least " ++ show cnt
479 ++ " elements, input terminated on " ++ show i
481-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
485{-# INLINE takeWhile #-}
486takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
487takeWhile predicate (Fold fstep finitial fextract) =
488 Parser step initial fextract
495 FL.Partial s -> IPartial s
504 FL.Partial s1 -> Partial 0 s1
505 FL.Done b -> Done 0 b
506 else Done 1 <$> fextract s
508-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
512{-# INLINE takeWhile1 #-}
513takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
514takeWhile1 predicate (Fold fstep finitial fextract) =
515 Parser step initial extract
522 FL.Partial s -> IPartial (Left s)
525 $ "takeWhile1: fold terminated without consuming:"
528 {-# INLINE process #-}
533 FL.Partial s1 -> Partial 0 (Right s1)
534 FL.Done b -> Done 0 b
539 else return $ Error "takeWhile1: predicate failed on first element"
547 extract (Left _) = throwM $ ParseError "takeWhile1: end of input"
548 extract (Right s) = fextract s
550-- | See 'Streamly.Internal.Data.Parser.sliceSepByP'.
554sliceSepByP :: MonadCatch m =>
555 (a -> Bool) -> Parser m a b -> Parser m a b
556sliceSepByP cond (Parser pstep pinitial pextract) =
558 Parser step initial pextract
571-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
575data SliceBeginWithState s = Left' s | Right' s
577{-# INLINE sliceBeginWith #-}
578sliceBeginWith :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
579sliceBeginWith cond (Fold fstep finitial fextract) =
581 Parser step initial extract
589 FL.Partial s -> IPartial (Left' s)
590 FL.Done _ -> IError "sliceBeginWith : bad finitial"
592 {-# INLINE process #-}
597 FL.Partial s1 -> Partial 0 (Right' s1)
598 FL.Done b -> Done 0 b
603 else error $ "sliceBeginWith : slice begins with an element which "
604 ++ "fails the predicate"
608 else Done 1 <$> fextract s
610 extract (Left' s) = fextract s
611 extract (Right' s) = fextract s
613data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
615-- | See 'Streamly.Internal.Data.Parser.wordBy'.
619wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
620wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
624 {-# INLINE worder #-}
629 FL.Partial s1 -> Partial 0 $ WBWord s1
630 FL.Done b -> Done 0 b
636 FL.Partial s -> IPartial $ WBLeft s
642 else return $ Partial 0 $ WBLeft s
648 return $ Partial 0 $ WBRight b
651 $ if not (predicate a)
653 else Partial 0 $ WBRight b
655 extract (WBLeft s) = fextract s
656 extract (WBWord s) = fextract s
657 extract (WBRight b) = return b
659{-# ANN type GroupByState Fuse #-}
662 | GroupByGrouping !a !s
664-- | See 'Streamly.Internal.Data.Parser.groupBy'.
666{-# INLINE groupBy #-}
667groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
668groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
672 {-# INLINE grouper #-}
677 FL.Done b -> Done 0 b
678 FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
684 FL.Partial s -> IPartial $ GroupByInit s
687 step (GroupByInit s) a = grouper s a a
688 step (GroupByGrouping a0 s) a =
691 else Done 1 <$> fextract s
693 extract (GroupByInit s) = fextract s
694 extract (GroupByGrouping _ s) = fextract s
696-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
698{-# INLINE groupByRolling #-}
699groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
700groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
704 {-# INLINE grouper #-}
709 FL.Done b -> Done 0 b
710 FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
716 FL.Partial s -> IPartial $ GroupByInit s
719 step (GroupByInit s) a = grouper s a
720 step (GroupByGrouping a0 s) a =
723 else Done 1 <$> fextract s
725 extract (GroupByInit s) = fextract s
726 extract (GroupByGrouping _ s) = fextract s
728-- XXX use an Unfold instead of a list?
729-- XXX custom combinators for matching list, array and stream?
731-- | See 'Streamly.Internal.Data.Parser.eqBy'.
736eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
737eqBy cmp str = Parser step initial extract
741 initial = return $ IPartial str
743 step [] _ = return $ Done 0 ()
748 else Error "eqBy: failed, yet to match the last element"
754 $ "eqBy: failed, yet to match "
755 ++ show (length xs + 1) ++ " elements"
760 $ "eqBy: end of input, yet to match "
761 ++ show (length xs) ++ " elements"
763--------------------------------------------------------------------------------
765--------------------------------------------------------------------------------
767-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the
768-- input as long as the predicate @p@ is 'True'. @f2@ consumes the rest of the
772-- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs
774-- > span_ (< 1) [1,2,3]
777-- > span_ (< 2) [1,2,3]
780-- > span_ (< 4) [1,2,3]
787span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
788span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)
790-- | Break the input stream into two groups, the first group takes the input as
791-- long as the predicate applied to the first element of the stream and next
792-- input element holds 'True', the second group takes the rest of the input.
799 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
800spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)
802-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
803-- predicate is applied to the previous and the next input elements.
806{-# INLINE spanByRolling #-}
809 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
810spanByRolling eq f1 f2 =
811 noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)
813-------------------------------------------------------------------------------
815-------------------------------------------------------------------------------
817-- | See 'Streamly.Internal.Data.Parser.lookahead'.
821{-# INLINE lookAhead #-}
822lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
823lookAhead (Parser step1 initial1 _) = Parser step initial extract
830 IPartial s -> IPartial (Tuple' 0 s)
834 step (Tuple' cnt st) a = do
839 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
840 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
841 Done _ b -> Done cnt1 b
842 Error err -> Error err
844 -- XXX returning an error let's us backtrack. To implement it in a way so
845 -- that it terminates on eof without an error then we need a way to
846 -- backtrack on eof, that will require extract to return 'Step' type.
847 extract (Tuple' n _) =
850 $ "lookAhead: end of input after consuming "
851 ++ show n ++ " elements"
853-------------------------------------------------------------------------------
855-------------------------------------------------------------------------------
857-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
861{-# INLINE deintercalate #-}
864 Fold m a y -> Parser m x a
865 -> Fold m b z -> Parser m x b
867deintercalate = undefined
869-------------------------------------------------------------------------------
870-- Sequential Collection
871-------------------------------------------------------------------------------
873-- | See 'Streamly.Internal.Data.Parser.sequence'.
877{-# INLINE sequence #-}
880 Fold m b c -> t (Parser m a b) -> Parser m a c
881sequence _f _p = undefined
883-------------------------------------------------------------------------------
884-- Alternative Collection
885-------------------------------------------------------------------------------
887-- | See 'Streamly.Internal.Data.Parser.choice'.
894 t (Parser m a b) -> Parser m a b
895choice _ps = undefined
897-------------------------------------------------------------------------------
898-- Sequential Repetition
899-------------------------------------------------------------------------------
901-- | See 'Streamly.Internal.Data.Parser.many'.
906many :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
908-- many = countBetween 0 maxBound
910-- | See 'Streamly.Internal.Data.Parser.some'.
915some :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
917-- some f p = many (takeGE 1 f) p
918-- many = countBetween 1 maxBound
920-- | See 'Streamly.Internal.Data.Parser.countBetween'.
924{-# INLINE countBetween #-}
927 Int -> Int -> Parser m a b -> Fold m b c -> Parser m a c
928countBetween _m _n _p = undefined
929-- countBetween m n p f = many (takeBetween m n f) p
931-- | See 'Streamly.Internal.Data.Parser.count'.
938 Int -> Parser m a b -> Fold m b c -> Parser m a c
939count n = countBetween n n
940-- count n f p = many (takeEQ n f) p
942data ManyTillState fs sr sl
943 = ManyTillR Int fs sr
944 | ManyTillL Int fs sl
946-- | See 'Streamly.Internal.Data.Parser.manyTill'.
950{-# INLINE manyTill #-}
951manyTill :: MonadCatch m
952 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
953manyTill (Fold fstep finitial fextract)
954 (Parser stepL initialL extractL)
955 (Parser stepR initialR _) =
956 Parser step initial extract
960 -- Caution: Mutual recursion
963 scrutL fs p c d e = do
966 IPartial sl -> return $ c (ManyTillL 0 fs sl)
970 FL.Partial fs1 -> scrutR fs1 p c d e
971 FL.Done fb -> return $ d fb
972 IError err -> return $ e err
974 {-# INLINE scrutR #-}
975 scrutR fs p c d e = do
978 IPartial sr -> return $ p (ManyTillR 0 fs sr)
979 IDone _ -> d <$> fextract fs
980 IError _ -> scrutL fs p c d e
985 FL.Partial fs -> scrutR fs IPartial IPartial IDone IError
986 FL.Done b -> return $ IDone b
988 step (ManyTillR cnt fs st) a = do
991 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
993 assert (cnt + 1 - n >= 0) (return ())
994 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
1002 return $ Continue (cnt + 1) (ManyTillL 0 fs sl)
1010 FL.Partial fs1 -> scrutR fs1 p c d Error
1011 FL.Done fb -> return $ Done cnt1 fb
1012 IError err -> return $ Error err
1013 -- XXX the cnt is being used only by the assert
1014 step (ManyTillL cnt fs st) a = do
1017 Partial n s -> return $ Partial n (ManyTillL 0 fs s)
1019 assert (cnt + 1 - n >= 0) (return ())
1020 return $ Continue n (ManyTillL (cnt + 1 - n) fs s)
1025 scrutR s (Partial n) (Continue n) (Done n) Error
1026 FL.Done b1 -> return $ Done n b1
1027 Error err -> return $ Error err
1029 extract (ManyTillL _ fs sR) = do
1030 res <- extractL sR >>= fstep fs
1032 FL.Partial s -> fextract s
1033 FL.Done b -> return b
1034 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