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
20 -- First order parsers
37 -- Parsers chained in series, if one parser terminates the composition
38 -- terminates. Currently we are using folds to collect the output of the
39 -- parsers but we can use Parsers instead of folds to make the composition
40 -- more powerful. For example, we can do:
42 -- sliceSepByMax cond n p = sliceBy cond (take n p)
43 -- sliceSepByBetween cond m n p = sliceBy cond (takeBetween m n p)
44 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
46 -- Grab a sequence of input elements without inspecting them
48 -- , take -- take -- takeBetween 0 n
49 -- , takeLE1 -- take1 -- takeBetween 1 n
50 , takeEQ -- takeBetween n n
51 , takeGE -- takeBetween n maxBound
53 -- Grab a sequence of input elements by inspecting them
58 -- , sliceSepByBetween
62 -- , frameSepBy -- parse frames escaped by an escape char/sequence
69 -- , prefixOf -- match any prefix of a given string
70 -- , suffixOf -- match any suffix of a given string
71 -- , infixOf -- match any substring of a given string
78 -- Second order parsers (parsers using parsers)
79 -- * Binary Combinators
81 -- ** Sequential Applicative
85 -- ** Parallel Applicatives
89 -- , teeTill -- like manyTill but parallel
91 -- ** Sequential Interleaving
92 -- Use two folds, run a primary parser, its rejected values go to the
96 -- ** Sequential Alternative
99 -- ** Parallel Alternatives
104 -- * N-ary Combinators
105 -- ** Sequential Collection
109 -- ** Sequential Repetition
112 -- , countBetweenTill
118 -- -- ** Special cases
119 -- XXX traditional implmentations of these may be of limited use. For
120 -- example, consider parsing lines separated by "\r\n". The main parser
121 -- will have to detect and exclude the sequence "\r\n" anyway so that we
122 -- can apply the "sep" parser.
124 -- We can instead implement these as special cases of deintercalate.
135 -- A simple and stupid impl would be to just convert the stream to an array
136 -- and give the array reference to all consumers. The array can be grown on
137 -- demand by any consumer and truncated when nonbody needs it.
139 -- -- ** Distribute to collection
140 -- -- ** Distribute to repetition
142 -- -- ** Interleaved collection
145 -- -- ** Interleaved repetition
146 -- repeat one parser and when it fails run an error recovery parser
147 -- e.g. to find a key frame in the stream after an error
149 -- ** Collection of Alternatives
152 -- , fastestN -- first N successful in time
153 -- , choiceN -- first N successful in position
154 , choice -- first successful in position
156 -- -- ** Repeated Alternatives
157 -- , retryMax -- try N times
158 -- , retryUntil -- try until successful
159 -- , retryUntilN -- try until successful n times
163import Control.Exception (assert)
164import Control.Monad.Catch (MonadCatch, MonadThrow(..))
165import Fusion.Plugin.Types (Fuse(..))
166import Streamly.Internal.Data.Fold.Type (Fold(..))
167import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
169import qualified Streamly.Internal.Data.Fold.Type as FL
172 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span)
173import Streamly.Internal.Data.Parser.ParserD.Tee
174import Streamly.Internal.Data.Parser.ParserD.Type
179-- >>> import Prelude hiding ()
180-- >>> import qualified Streamly.Prelude as Stream
181-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
182-- >>> import qualified Streamly.Data.Fold as Fold
183-- >>> import qualified Streamly.Internal.Data.Parser as Parser
185-------------------------------------------------------------------------------
186-- Upgrade folds to parses
187-------------------------------------------------------------------------------
189-- | See 'Streamly.Internal.Data.Parser.fromFold'.
193{-# INLINE fromFold #-}
194fromFold :: Monad m => Fold m a b -> Parser m a b
195fromFold (Fold fstep finitial fextract) = Parser step initial fextract
203 FL.Partial s1 -> IPartial s1
210 FL.Partial s1 -> Partial 0 s1
211 FL.Done b -> Done 0 b
213-------------------------------------------------------------------------------
215-------------------------------------------------------------------------------
217-- | See 'Streamly.Internal.Data.Parser.peek'.
221{-# INLINABLE peek #-}
222peek :: MonadThrow m => Parser m a a
223peek = Parser step initial extract
227 initial = return $ IPartial ()
229 step () a = return $ Done 1 a
231 extract () = throwM $ ParseError "peek: end of input"
233-- | See 'Streamly.Internal.Data.Parser.eof'.
238eof :: Monad m => Parser m a ()
239eof = Parser step initial return
243 initial = return $ IPartial ()
245 step () _ = return $ Error "eof: not at end of input"
247-- | See 'Streamly.Internal.Data.Parser.satisfy'.
251{-# INLINE satisfy #-}
252satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
253satisfy predicate = Parser step initial extract
257 initial = return $ IPartial ()
262 else Error "satisfy: predicate failed"
264 extract _ = throwM $ ParseError "satisfy: end of input"
266-- | See 'Streamly.Internal.Data.Parser.maybe'.
271maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
272maybe parser = Parser step initial extract
276 initial = return $ IPartial ()
281 Nothing -> Error "maybe: predicate failed"
283 extract _ = throwM $ ParseError "maybe: end of input"
285-- | See 'Streamly.Internal.Data.Parser.either'.
290either :: MonadThrow m => (a -> Either String b) -> Parser m a b
291either parser = Parser step initial extract
295 initial = return $ IPartial ()
300 Left err -> Error $ "either: " ++ err
302 extract _ = throwM $ ParseError "either: end of input"
304-------------------------------------------------------------------------------
306-------------------------------------------------------------------------------
308-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
312{-# INLINE takeBetween #-}
313takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
314takeBetween low high (Fold fstep finitial fextract) =
316 Parser step initial extract
323 FL.Partial s -> IPartial $ Tuple' 0 s
328 $ "takeBetween: the collecting fold terminated without"
329 ++ " consuming any elements"
330 ++ " minimum" ++ show low ++ " elements needed"
336 $ "takeBetween: lower bound - " ++ show low
337 ++ " is greater than higher bound - " ++ show high
338 | high <= 0 = Done 1 <$> fextract s
343 FL.Partial s1 -> Continue 0 $ Tuple' i1 s1
346 $ "takeBetween: the collecting fold terminated after"
347 ++ " consuming" ++ show i1 ++ " elements"
348 ++ " minimum" ++ show low ++ " elements needed"
354 then Done 0 <$> fextract s1
355 else return $ Partial 0 $ Tuple' i1 s1
356 FL.Done b -> return $ Done 0 b
363 | i >= low && i <= high = fextract s
364 | otherwise = throwM $ ParseError err
369 "takeBetween: Expecting alteast " ++ show low
370 ++ " elements, got " ++ show i
372-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
377takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
378takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
387 FL.Partial s -> IPartial $ Tuple' 0 s
392 $ "takeEQ: Expecting exactly " ++ show cnt
393 ++ " elements, fold terminated without"
394 ++ " consuming any elements"
401 FL.Partial s -> Continue 0 $ Tuple' i1 s
404 $ "takeEQ: Expecting exactly " ++ show cnt
405 ++ " elements, fold terminated on " ++ show i1
410 FL.Partial s -> fextract s
411 FL.Done b -> return b
412 -- XXX we should not reach here when initial returns Step type
413 -- reachable only when n == 0
414 | otherwise = Done 1 <$> fextract r
421 | i == 0 && cnt == 0 = fextract r
425 $ "takeEQ: Expecting exactly " ++ show cnt
426 ++ " elements, input terminated on " ++ show i
428-- | See 'Streamly.Internal.Data.Parser.takeGE'.
433takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
434takeGE n (Fold fstep finitial fextract) = Parser step initial extract
442 FL.Partial s -> IPartial $ Tuple' 0 s
447 $ "takeGE: Expecting at least " ++ show cnt
448 ++ " elements, fold terminated without"
449 ++ " consuming any elements"
456 FL.Partial s -> Continue 0 $ Tuple' i1 s
459 $ "takeGE: Expecting at least " ++ show cnt
460 ++ " elements, fold terminated on " ++ show i1
465 FL.Partial s -> Partial 0 $ Tuple' i1 s
466 FL.Done b -> Done 0 b
473 | i >= cnt = fextract r
477 $ "takeGE: Expecting at least " ++ show cnt
478 ++ " elements, input terminated on " ++ show i
480-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
484{-# INLINE takeWhile #-}
485takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
486takeWhile predicate (Fold fstep finitial fextract) =
487 Parser step initial fextract
494 FL.Partial s -> IPartial s
503 FL.Partial s1 -> Partial 0 s1
504 FL.Done b -> Done 0 b
505 else Done 1 <$> fextract s
507-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
511{-# INLINE takeWhile1 #-}
512takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
513takeWhile1 predicate (Fold fstep finitial fextract) =
514 Parser step initial extract
521 FL.Partial s -> IPartial (Left s)
524 $ "takeWhile1: fold terminated without consuming:"
527 {-# INLINE process #-}
532 FL.Partial s1 -> Partial 0 (Right s1)
533 FL.Done b -> Done 0 b
538 else return $ Error "takeWhile1: predicate failed on first element"
546 extract (Left _) = throwM $ ParseError "takeWhile1: end of input"
547 extract (Right s) = fextract s
549-- | See 'Streamly.Internal.Data.Parser.sliceSepByP'.
553sliceSepByP :: MonadCatch m =>
554 (a -> Bool) -> Parser m a b -> Parser m a b
555sliceSepByP cond (Parser pstep pinitial pextract) =
557 Parser step initial pextract
570-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
574data SliceBeginWithState s = Left' s | Right' s
576{-# INLINE sliceBeginWith #-}
577sliceBeginWith :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
578sliceBeginWith cond (Fold fstep finitial fextract) =
580 Parser step initial extract
588 FL.Partial s -> IPartial (Left' s)
589 FL.Done _ -> IError "sliceBeginWith : bad finitial"
591 {-# INLINE process #-}
596 FL.Partial s1 -> Partial 0 (Right' s1)
597 FL.Done b -> Done 0 b
602 else error $ "sliceBeginWith : slice begins with an element which "
603 ++ "fails the predicate"
607 else Done 1 <$> fextract s
609 extract (Left' s) = fextract s
610 extract (Right' s) = fextract s
612data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
614-- | See 'Streamly.Internal.Data.Parser.wordBy'.
618wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
619wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
623 {-# INLINE worder #-}
628 FL.Partial s1 -> Partial 0 $ WBWord s1
629 FL.Done b -> Done 0 b
635 FL.Partial s -> IPartial $ WBLeft s
641 else return $ Partial 0 $ WBLeft s
647 return $ Partial 0 $ WBRight b
650 $ if not (predicate a)
652 else Partial 0 $ WBRight b
654 extract (WBLeft s) = fextract s
655 extract (WBWord s) = fextract s
656 extract (WBRight b) = return b
658{-# ANN type GroupByState Fuse #-}
661 | GroupByGrouping !a !s
663-- | See 'Streamly.Internal.Data.Parser.groupBy'.
665{-# INLINE groupBy #-}
666groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
667groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
671 {-# INLINE grouper #-}
676 FL.Done b -> Done 0 b
677 FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
683 FL.Partial s -> IPartial $ GroupByInit s
686 step (GroupByInit s) a = grouper s a a
687 step (GroupByGrouping a0 s) a =
690 else Done 1 <$> fextract s
692 extract (GroupByInit s) = fextract s
693 extract (GroupByGrouping _ s) = fextract s
695-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
697{-# INLINE groupByRolling #-}
698groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
699groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
703 {-# INLINE grouper #-}
708 FL.Done b -> Done 0 b
709 FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
715 FL.Partial s -> IPartial $ GroupByInit s
718 step (GroupByInit s) a = grouper s a
719 step (GroupByGrouping a0 s) a =
722 else Done 1 <$> fextract s
724 extract (GroupByInit s) = fextract s
725 extract (GroupByGrouping _ s) = fextract s
727-- XXX use an Unfold instead of a list?
728-- XXX custom combinators for matching list, array and stream?
730-- | See 'Streamly.Internal.Data.Parser.eqBy'.
735eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
736eqBy cmp str = Parser step initial extract
740 initial = return $ IPartial str
742 step [] _ = return $ Done 0 ()
747 else Error "eqBy: failed, yet to match the last element"
753 $ "eqBy: failed, yet to match "
754 ++ show (length xs + 1) ++ " elements"
759 $ "eqBy: end of input, yet to match "
760 ++ show (length xs) ++ " elements"
762--------------------------------------------------------------------------------
764--------------------------------------------------------------------------------
766-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the
767-- input as long as the predicate @p@ is 'True'. @f2@ consumes the rest of the
771-- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs
773-- > span_ (< 1) [1,2,3]
776-- > span_ (< 2) [1,2,3]
779-- > span_ (< 4) [1,2,3]
786span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
787span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)
789-- | Break the input stream into two groups, the first group takes the input as
790-- long as the predicate applied to the first element of the stream and next
791-- input element holds 'True', the second group takes the rest of the input.
798 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
799spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)
801-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
802-- predicate is applied to the previous and the next input elements.
805{-# INLINE spanByRolling #-}
808 => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
809spanByRolling eq f1 f2 =
810 noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)
812-------------------------------------------------------------------------------
814-------------------------------------------------------------------------------
816-- | See 'Streamly.Internal.Data.Parser.lookahead'.
820{-# INLINE lookAhead #-}
821lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
822lookAhead (Parser step1 initial1 _) = Parser step initial extract
829 IPartial s -> IPartial (Tuple' 0 s)
833 step (Tuple' cnt st) a = do
838 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
839 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
840 Done _ b -> Done cnt1 b
841 Error err -> Error err
843 -- XXX returning an error let's us backtrack. To implement it in a way so
844 -- that it terminates on eof without an error then we need a way to
845 -- backtrack on eof, that will require extract to return 'Step' type.
846 extract (Tuple' n _) =
849 $ "lookAhead: end of input after consuming "
850 ++ show n ++ " elements"
852-------------------------------------------------------------------------------
854-------------------------------------------------------------------------------
856-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
860{-# INLINE deintercalate #-}
863 Fold m a y -> Parser m x a
864 -> Fold m b z -> Parser m x b
866deintercalate = undefined
868-------------------------------------------------------------------------------
869-- Sequential Collection
870-------------------------------------------------------------------------------
872-- | See 'Streamly.Internal.Data.Parser.sequence'.
876{-# INLINE sequence #-}
879 Fold m b c -> t (Parser m a b) -> Parser m a c
880sequence _f _p = undefined
882-------------------------------------------------------------------------------
883-- Alternative Collection
884-------------------------------------------------------------------------------
886-- | See 'Streamly.Internal.Data.Parser.choice'.
893 t (Parser m a b) -> Parser m a b
894choice _ps = undefined
896-------------------------------------------------------------------------------
897-- Sequential Repetition
898-------------------------------------------------------------------------------
900-- | See 'Streamly.Internal.Data.Parser.many'.
905many :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
907-- many = countBetween 0 maxBound
909-- | See 'Streamly.Internal.Data.Parser.some'.
914some :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
916-- some f p = many (takeGE 1 f) p
917-- many = countBetween 1 maxBound
919-- | See 'Streamly.Internal.Data.Parser.countBetween'.
923{-# INLINE countBetween #-}
926 Int -> Int -> Parser m a b -> Fold m b c -> Parser m a c
927countBetween _m _n _p = undefined
928-- countBetween m n p f = many (takeBetween m n f) p
930-- | See 'Streamly.Internal.Data.Parser.count'.
937 Int -> Parser m a b -> Fold m b c -> Parser m a c
938count n = countBetween n n
939-- count n f p = many (takeEQ n f) p
941data ManyTillState fs sr sl
942 = ManyTillR Int fs sr
943 | ManyTillL Int fs sl
945-- | See 'Streamly.Internal.Data.Parser.manyTill'.
949{-# INLINE manyTill #-}
950manyTill :: MonadCatch m
951 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
952manyTill (Fold fstep finitial fextract)
953 (Parser stepL initialL extractL)
954 (Parser stepR initialR _) =
955 Parser step initial extract
959 -- Caution: Mutual recursion
962 scrutL fs p c d e = do
965 IPartial sl -> return $ c (ManyTillL 0 fs sl)
969 FL.Partial fs1 -> scrutR fs1 p c d e
970 FL.Done fb -> return $ d fb
971 IError err -> return $ e err
973 {-# INLINE scrutR #-}
974 scrutR fs p c d e = do
977 IPartial sr -> return $ p (ManyTillR 0 fs sr)
978 IDone _ -> d <$> fextract fs
979 IError _ -> scrutL fs p c d e
984 FL.Partial fs -> scrutR fs IPartial IPartial IDone IError
985 FL.Done b -> return $ IDone b
987 step (ManyTillR cnt fs st) a = do
990 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
992 assert (cnt + 1 - n >= 0) (return ())
993 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
1001 return $ Continue (cnt + 1) (ManyTillL 0 fs sl)
1009 FL.Partial fs1 -> scrutR fs1 p c d Error
1010 FL.Done fb -> return $ Done cnt1 fb
1011 IError err -> return $ Error err
1012 -- XXX the cnt is being used only by the assert
1013 step (ManyTillL cnt fs st) a = do
1016 Partial n s -> return $ Partial n (ManyTillL 0 fs s)
1018 assert (cnt + 1 - n >= 0) (return ())
1019 return $ Continue n (ManyTillL (cnt + 1 - n) fs s)
1024 scrutR s (Partial n) (Continue n) (Done n) Error
1025 FL.Done b1 -> return $ Done n b1
1026 Error err -> return $ Error err
1028 extract (ManyTillL _ fs sR) = do
1029 res <- extractL sR >>= fstep fs
1031 FL.Partial s -> fextract s
1032 FL.Done b -> return b
1033 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