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
19 -- First order parsers
36 -- Parsers chained in series, if one parser terminates the composition
37 -- terminates. Currently we are using folds to collect the output of the
38 -- parsers but we can use Parsers instead of folds to make the composition
39 -- more powerful. For example, we can do:
41 -- sliceSepByMax cond n p = sliceBy cond (take n p)
42 -- sliceSepByBetween cond m n p = sliceBy cond (takeBetween m n p)
43 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
45 -- Grab a sequence of input elements without inspecting them
47 -- , takeLE -- take -- takeBetween 0 n
48 -- , takeLE1 -- take1 -- takeBetween 1 n
49 , takeEQ -- takeBetween n n
50 , takeGE -- takeBetween n maxBound
52 -- Grab a sequence of input elements by inspecting them
57 -- , sliceSepByBetween
61 -- , frameSepBy -- parse frames escaped by an escape char/sequence
68 -- , prefixOf -- match any prefix of a given string
69 -- , suffixOf -- match any suffix of a given string
70 -- , infixOf -- match any substring of a given string
72 -- Second order parsers (parsers using parsers)
73 -- * Binary Combinators
75 -- ** Sequential Applicative
79 -- ** Parallel Applicatives
83 -- , teeTill -- like manyTill but parallel
85 -- ** Sequential Interleaving
86 -- Use two folds, run a primary parser, its rejected values go to the
90 -- ** Sequential Alternative
93 -- ** Parallel Alternatives
98 -- * N-ary Combinators
99 -- ** Sequential Collection
103 -- ** Sequential Repetition
106 -- , countBetweenTill
112 -- -- ** Special cases
113 -- XXX traditional implmentations of these may be of limited use. For
114 -- example, consider parsing lines separated by "\r\n". The main parser
115 -- will have to detect and exclude the sequence "\r\n" anyway so that we
116 -- can apply the "sep" parser.
118 -- We can instead implement these as special cases of deintercalate.
129 -- A simple and stupid impl would be to just convert the stream to an array
130 -- and give the array reference to all consumers. The array can be grown on
131 -- demand by any consumer and truncated when nonbody needs it.
133 -- -- ** Distribute to collection
134 -- -- ** Distribute to repetition
136 -- -- ** Interleaved collection
139 -- -- ** Interleaved repetition
140 -- repeat one parser and when it fails run an error recovery parser
141 -- e.g. to find a key frame in the stream after an error
143 -- ** Collection of Alternatives
146 -- , fastestN -- first N successful in time
147 -- , choiceN -- first N successful in position
148 , choice -- first successful in position
150 -- -- ** Repeated Alternatives
151 -- , retryMax -- try N times
152 -- , retryUntil -- try until successful
153 -- , retryUntilN -- try until successful n times
157import Control.Exception (assert)
158import Control.Monad.Catch (MonadCatch, MonadThrow(..))
159import Streamly.Internal.Data.Fold.Types (Fold(..))
160import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
162import qualified Streamly.Internal.Data.Fold.Types as FL
165 (any, all, take, takeWhile, sequence, concatMap, maybe, either)
166import Streamly.Internal.Data.Parser.ParserD.Tee
167import Streamly.Internal.Data.Parser.ParserD.Types
169-------------------------------------------------------------------------------
170-- Upgrade folds to parses
171-------------------------------------------------------------------------------
173-- | See 'Streamly.Internal.Data.Parser.fromFold'.
177{-# INLINE fromFold #-}
178fromFold :: Monad m => Fold m a b -> Parser m a b
179fromFold (Fold fstep finitial fextract) = Parser step finitial extract
183 step (FL.Partial s) a = do
187 FL.Partial s1 -> Partial 0 (FL.Partial s1)
188 FL.Done b -> Done 0 b
189 step (FL.Done b) _ = return $ Done 1 b
191 extract (FL.Partial s) = fextract s
192 extract (FL.Done b) = return b
194-------------------------------------------------------------------------------
196-------------------------------------------------------------------------------
198-- | See 'Streamly.Internal.Data.Parser.peek'.
202{-# INLINABLE peek #-}
203peek :: MonadThrow m => Parser m a a
204peek = Parser step initial extract
210 step () a = return $ Done 1 a
212 extract () = throwM $ ParseError "peek: end of input"
214-- | See 'Streamly.Internal.Data.Parser.eof'.
219eof :: Monad m => Parser m a ()
220eof = Parser step initial return
226 step () _ = return $ Error "eof: not at end of input"
228-- | See 'Streamly.Internal.Data.Parser.satisfy'.
232{-# INLINE satisfy #-}
233satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
234satisfy predicate = Parser step initial extract
243 else Error "satisfy: predicate failed"
245 extract _ = throwM $ ParseError "satisfy: end of input"
247-- | See 'Streamly.Internal.Data.Parser.maybe'.
252maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
253maybe parser = Parser step initial extract
262 Nothing -> Error "maybe: predicate failed"
264 extract _ = throwM $ ParseError "maybe: end of input"
266-- | See 'Streamly.Internal.Data.Parser.either'.
271either :: MonadThrow m => (a -> Either String b) -> Parser m a b
272either parser = Parser step initial extract
281 Left err -> Error $ "either: " ++ err
283 extract _ = throwM $ ParseError "either: end of input"
285-------------------------------------------------------------------------------
287-------------------------------------------------------------------------------
289-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
293{-# INLINE takeBetween #-}
294takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
295takeBetween low high (Fold fstep finitial fextract) =
297 Parser step initial extract
304 FL.Partial s -> return $ Tuple' 0 s
306 error "takeBetween: Done/Error in initial not implemented yet."
312 $ "takeBetween: lower bound - " ++ show low
313 ++ " is greater than higher bound - " ++ show high
314 | high <= 0 = Done 1 <$> fextract s
319 FL.Partial s1 -> Continue 0 $ Tuple' i1 s1
322 $ "takeBetween: the collecting fold terminated after"
323 ++ " consuming" ++ show i1 ++ " elements"
324 ++ " minimum" ++ show low ++ " elements needed"
330 then Done 0 <$> fextract s1
331 else return $ Partial 0 $ Tuple' i1 s1
332 FL.Done b -> return $ Done 0 b
339 | i >= low && i <= high = fextract s
340 | otherwise = throwM $ ParseError err
345 "takeBetween: Expecting alteast " ++ show low
346 ++ " elements, got " ++ show i
348-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
353takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
354takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
363 FL.Partial s -> return $ Tuple' 0 s
365 error "takeEQ: Done/Error in initial not implemented yet."
372 FL.Partial s -> Continue 0 $ Tuple' i1 s
375 $ "takeEQ: Expecting exactly " ++ show cnt
376 ++ " elements, fold terminated on " ++ show i1
381 FL.Partial s -> fextract s
382 FL.Done b -> return b
383 -- XXX we should not reach here when initial returns Step type
384 -- reachable only when n == 0
385 | otherwise = Done 1 <$> fextract r
392 | i == 0 && cnt == 0 = fextract r
396 $ "takeEQ: Expecting exactly " ++ show cnt
397 ++ " elements, input terminated on " ++ show i
399-- | See 'Streamly.Internal.Data.Parser.takeGE'.
404takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
405takeGE n (Fold fstep finitial fextract) = Parser step initial extract
413 FL.Partial s -> return $ Tuple' 0 s
415 error "takeGE: Done/Error in initial not implemented yet."
422 FL.Partial s -> Continue 0 $ Tuple' i1 s
425 $ "takeGE: Expecting at least " ++ show cnt
426 ++ " elements, fold terminated on " ++ show i1
431 FL.Partial s -> Partial 0 $ Tuple' i1 s
432 FL.Done b -> Done 0 b
439 | i >= cnt = fextract r
443 $ "takeGE: Expecting at least " ++ show cnt
444 ++ " elements, input terminated on " ++ show i
446-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
450{-# INLINE takeWhile #-}
451takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
452takeWhile predicate (Fold fstep finitial fextract) =
453 Parser step initial fextract
460 FL.Partial s -> return s
462 error "takeWhile: Done/Error in initial not implemented yet."
470 FL.Partial s1 -> Partial 0 s1
471 FL.Done b -> Done 0 b
472 else Done 1 <$> fextract s
474-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
478{-# INLINE takeWhile1 #-}
479takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
480takeWhile1 predicate (Fold fstep finitial fextract) =
481 Parser step initial extract
485 initial = return Nothing
488 let err = Error "takeWhile1: predicate failed on first element"
497 FL.Partial s1 -> Partial 0 (Just s1)
498 FL.Done b -> Done 0 b
499 FL.Done _ -> return err
506 FL.Partial r -> return $ Partial 0 (Just r)
507 FL.Done b -> return $ Done 0 b
512 extract Nothing = throwM $ ParseError "takeWhile1: end of input"
513 extract (Just s) = fextract s
515-- | See 'Streamly.Internal.Data.Parser.sliceSepByP'.
519sliceSepByP :: MonadCatch m =>
520 (a -> Bool) -> Parser m a b -> Parser m a b
521sliceSepByP cond (Parser pstep pinitial pextract) =
523 Parser step initial pextract
536-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
540{-# INLINABLE sliceBeginWith #-}
543 (a -> Bool) -> Fold m a b -> Parser m a b
544sliceBeginWith = undefined
546data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
548-- | See 'Streamly.Internal.Data.Parser.wordBy'.
552wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
553wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
557 {-# INLINE worder #-}
562 FL.Partial s1 -> Partial 0 $ WBWord s1
563 FL.Done b -> Done 0 b
569 FL.Partial s -> WBLeft s
570 FL.Done _ -> error "wordBy: Done in initial not implemented"
575 else return $ Partial 0 $ WBLeft s
581 return $ Partial 0 $ WBRight b
584 $ if not (predicate a)
586 else Partial 0 $ WBRight b
588 extract (WBLeft s) = fextract s
589 extract (WBWord s) = fextract s
590 extract (WBRight b) = return b
594 | GroupByGrouping !a !s
596-- | See 'Streamly.Internal.Data.Parser.groupBy'.
598{-# INLINE groupBy #-}
599groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
600groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
604 {-# INLINE grouper #-}
609 FL.Done b -> Done 0 b
610 FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
616 FL.Partial s -> GroupByInit s
617 FL.Done _ -> error "groupBy: Done in initial not implemented"
619 step (GroupByInit s) a = grouper s a a
620 step (GroupByGrouping a0 s) a =
623 else Done 1 <$> fextract s
625 extract (GroupByInit s) = fextract s
626 extract (GroupByGrouping _ s) = fextract s
628-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
630{-# INLINE groupByRolling #-}
631groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
632groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
636 {-# INLINE grouper #-}
641 FL.Done b -> Done 0 b
642 FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
648 FL.Partial s -> GroupByInit s
649 FL.Done _ -> error "groupByRolling: Done in initial not implemented"
651 step (GroupByInit s) a = grouper s a
652 step (GroupByGrouping a0 s) a =
655 else Done 1 <$> fextract s
657 extract (GroupByInit s) = fextract s
658 extract (GroupByGrouping _ s) = fextract s
660-- XXX use an Unfold instead of a list?
661-- XXX custom combinators for matching list, array and stream?
663-- | See 'Streamly.Internal.Data.Parser.eqBy'.
668eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
669eqBy cmp str = Parser step initial extract
675 step [] _ = return $ Done 0 ()
680 else Error "eqBy: failed, yet to match the last element"
686 $ "eqBy: failed, yet to match "
687 ++ show (length xs + 1) ++ " elements"
692 $ "eqBy: end of input, yet to match "
693 ++ show (length xs) ++ " elements"
695-------------------------------------------------------------------------------
697-------------------------------------------------------------------------------
699-- | See 'Streamly.Internal.Data.Parser.lookahead'.
703{-# INLINE lookAhead #-}
704lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
705lookAhead (Parser step1 initial1 _) = Parser step initial extract
709 initial = Tuple' 0 <$> initial1
711 step (Tuple' cnt st) a = do
716 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
717 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
718 Done _ b -> Done cnt1 b
719 Error err -> Error err
721 -- XXX returning an error let's us backtrack. To implement it in a way so
722 -- that it terminates on eof without an error then we need a way to
723 -- backtrack on eof, that will require extract to return 'Step' type.
724 extract (Tuple' n _) =
727 $ "lookAhead: end of input after consuming "
728 ++ show n ++ " elements"
730-------------------------------------------------------------------------------
732-------------------------------------------------------------------------------
734-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
738{-# INLINE deintercalate #-}
741 Fold m a y -> Parser m x a
742 -> Fold m b z -> Parser m x b
744deintercalate = undefined
746-------------------------------------------------------------------------------
747-- Sequential Collection
748-------------------------------------------------------------------------------
750-- | See 'Streamly.Internal.Data.Parser.sequence'.
754{-# INLINE sequence #-}
757 Fold m b c -> t (Parser m a b) -> Parser m a c
758sequence _f _p = undefined
760-------------------------------------------------------------------------------
761-- Alternative Collection
762-------------------------------------------------------------------------------
764-- | See 'Streamly.Internal.Data.Parser.choice'.
771 t (Parser m a b) -> Parser m a b
772choice _ps = undefined
774-------------------------------------------------------------------------------
775-- Sequential Repetition
776-------------------------------------------------------------------------------
778-- | See 'Streamly.Internal.Data.Parser.many'.
783many :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
785-- many = countBetween 0 maxBound
787-- | See 'Streamly.Internal.Data.Parser.some'.
792some :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
794-- some f p = many (takeGE 1 f) p
795-- many = countBetween 1 maxBound
797-- | See 'Streamly.Internal.Data.Parser.countBetween'.
801{-# INLINE countBetween #-}
804 Int -> Int -> Fold m b c -> Parser m a b -> Parser m a c
805countBetween _m _n _f = undefined
806-- countBetween m n f p = many (takeBetween m n f) p
808-- | See 'Streamly.Internal.Data.Parser.count'.
815 Int -> Fold m b c -> Parser m a b -> Parser m a c
816count n = countBetween n n
817-- count n f p = many (takeEQ n f) p
819data ManyTillState fs sr sl
820 = ManyTillR Int fs sr
821 | ManyTillL Int fs sl
823-- | See 'Streamly.Internal.Data.Parser.manyTill'.
827{-# INLINE manyTill #-}
828manyTill :: MonadCatch m
829 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
830manyTill (Fold fstep finitial fextract)
831 (Parser stepL initialL extractL)
832 (Parser stepR initialR _) =
833 Parser step initial extract
840 FL.Partial fs -> ManyTillR 0 fs <$> initialR
841 FL.Done _ -> error "manyTill: Done in initial not implemented"
843 step (ManyTillR cnt fs st) a = do
846 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
848 assert (cnt + 1 - n >= 0) (return ())
849 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
855 return $ Continue (cnt + 1) (ManyTillL 0 fs rR)
856 -- XXX the cnt is being used only by the assert
857 step (ManyTillL cnt fs st) a = do
860 Partial n s -> return $ Partial n (ManyTillL 0 fs s)
862 assert (cnt + 1 - n >= 0) (return ())
863 return $ Continue n (ManyTillL (cnt + 1 - n) fs s)
869 return $ Partial n (ManyTillR 0 s l)
870 FL.Done b1 -> return $ Done n b1
871 Error err -> return $ Error err
873 extract (ManyTillL _ fs sR) = do
874 res <- extractL sR >>= fstep fs
876 FL.Partial s -> fextract s
877 FL.Done b -> return b
878 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