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
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
50 -- , takeLE -- take -- takeBetween 0 n
51 -- , takeLE1 -- take1 -- takeBetween 1 n
52 , takeEQ -- takeBetween n n
53 , takeGE -- takeBetween n maxBound
55 -- Grab a sequence of input elements by inspecting them
61 -- , sliceSepByBetween
66 -- , frameSepBy -- parse frames escaped by an escape char/sequence
72 -- , prefixOf -- match any prefix of a given string
73 -- , suffixOf -- match any suffix of a given string
74 -- , infixOf -- match any substring of a given string
76 -- Second order parsers (parsers using parsers)
77 -- * Binary Combinators
79 -- ** Sequential Applicative
83 -- ** Parallel Applicatives
87 -- , teeTill -- like manyTill but parallel
89 -- ** Sequential Interleaving
90 -- Use two folds, run a primary parser, its rejected values go to the
94 -- ** Sequential Alternative
97 -- ** Parallel Alternatives
102 -- * N-ary Combinators
103 -- ** Sequential Collection
107 -- ** Sequential Repetition
110 -- , countBetweenTill
116 -- -- ** Special cases
117 -- XXX traditional implmentations of these may be of limited use. For
118 -- example, consider parsing lines separated by "\r\n". The main parser
119 -- will have to detect and exclude the sequence "\r\n" anyway so that we
120 -- can apply the "sep" parser.
122 -- We can instead implement these as special cases of deintercalate.
133 -- A simple and stupid impl would be to just convert the stream to an array
134 -- and give the array reference to all consumers. The array can be grown on
135 -- demand by any consumer and truncated when nonbody needs it.
137 -- -- ** Distribute to collection
138 -- -- ** Distribute to repetition
140 -- -- ** Interleaved collection
143 -- -- ** Interleaved repetition
144 -- repeat one parser and when it fails run an error recovery parser
145 -- e.g. to find a key frame in the stream after an error
147 -- ** Collection of Alternatives
150 -- , fastestN -- first N successful in time
151 -- , choiceN -- first N successful in position
152 , choice -- first successful in position
154 -- -- ** Repeated Alternatives
155 -- , retryMax -- try N times
156 -- , retryUntil -- try until successful
157 -- , retryUntilN -- try until successful n times
161import Control.Exception (assert)
162import Control.Monad.Catch (MonadCatch, MonadThrow(..))
164 hiding (any, all, take, takeWhile, sequence, concatMap)
166import Streamly.Internal.Data.Fold.Types (Fold(..))
168import qualified Streamly.Internal.Data.Parser.ParserK.Types as K
169import qualified Streamly.Internal.Data.Zipper as Z
171import Streamly.Internal.Data.Parser.ParserD.Tee
172import Streamly.Internal.Data.Parser.ParserD.Types
173import Streamly.Internal.Data.Strict
175-------------------------------------------------------------------------------
176-- Upgrade folds to parses
177-------------------------------------------------------------------------------
179-- | See 'Streamly.Internal.Data.Parser.fromFold'.
183{-# INLINE fromFold #-}
184fromFold :: Monad m => Fold m a b -> Parser m a b
185fromFold (Fold fstep finitial fextract) = Parser step finitial fextract
189 step s a = Partial 0 <$> fstep s a
191-------------------------------------------------------------------------------
192-- Convert to and from CPS style parser representation
193-------------------------------------------------------------------------------
195-- | Convert a direct style 'Parser' to a CPS style 'K.Parser'.
199{-# INLINE_LATE toParserK #-}
200toParserK :: MonadCatch m => Parser m a b -> K.Parser m a b
201toParserK (Parser step initial extract) =
202 K.MkParser $ K.parse step initial extract
204-- XXX The CPS style parsers use a zipper buffering the data, if a parserD is
205-- driving the parserK then it would also be buffering the same data. For
206-- ParserD, instead of maintaining the buffer in the common driver, each parser
207-- can have its own buffering and we can return the unconsumed buffer in the
208-- end. That way the zipper is maintained by the parser. If the parser fails
209-- then it has to return all of the input. It is anyway maintained by
210-- intermediate level parsers in a composition, so the only difference would be
211-- that even the leaf levels parsers would do it. If we abstract the zipper
212-- maintainance then it may not be too unwieldy.
214-- | Convert a CPS style 'K.Parser' to a direct style 'Parser'.
218{-# INLINE_LATE fromParserK #-}
219fromParserK :: MonadThrow m => K.Parser m a b -> Parser m a b
220fromParserK parser = Parser step initial extract
224 initial = return Nothing
227 let yieldk _ (K.Done b) = return $ K.Stop b
228 yieldk _ (K.Error e) = return $ K.Failed e
229 r <- K.runParser parser (Z.Zipper [] [] [a]) yieldk
231 -- XXX it should return the leftover count
232 K.Stop b -> return $ Done 0 b
233 K.Failed e -> return $ Error e
234 -- XXX when it pauses it may not have the result available, so it
235 -- is not necessarily Partial. It could be Skip instead.
236 -- Always returning Skip may lead to buffer accumulation.
237 -- XXX In case of Partial the backtrack count is always 0 because
238 -- ParserK would never ask to backtrack. Even for Skip it would
239 -- always be zero. The only place where it may need the ParserD
240 -- buffer is the final "Done" event where it can say there is a
242 K.Partial cont -> return $ Partial 0 $ Just cont
243 step (Just cont) a = do
246 K.Stop b -> return $ Done 0 b
247 K.Failed e -> return $ Error e
248 K.Partial cont1 -> return $ Partial 0 $ Just cont1
250 extract Nothing = throwM $ ParseError "end of input"
251 extract (Just cont) = K.extractParse cont
253#ifndef DISABLE_FUSION
254{-# RULES "fromParserK/toParserK fusion" [2]
255 forall s. toParserK (fromParserK s) = s #-}
256{-# RULES "toParserK/fromParserK fusion" [2]
257 forall s. fromParserK (toParserK s) = s #-}
260-------------------------------------------------------------------------------
261-- Terminating but not failing folds
262-------------------------------------------------------------------------------
265any :: Monad m => (a -> Bool) -> Parser m a Bool
266any predicate = Parser step initial return
270 initial = return False
272 step s a = return (if s || predicate a then Done 0 True else Partial 0 False)
275all :: Monad m => (a -> Bool) -> Parser m a Bool
276all predicate = Parser step initial return
280 initial = return True
282 step s a = return (if s && predicate a then Partial 0 True else Done 0 False)
284-------------------------------------------------------------------------------
286-------------------------------------------------------------------------------
288-- | See 'Streamly.Internal.Data.Parser.peek'.
292{-# INLINABLE peek #-}
293peek :: MonadThrow m => Parser m a a
294peek = Parser step initial extract
300 step () a = return $ Done 1 a
302 extract () = throwM $ ParseError "peek: end of input"
304-- | See 'Streamly.Internal.Data.Parser.eof'.
309eof :: Monad m => Parser m a ()
310eof = Parser step initial return
316 step () _ = return $ Error "eof: not at end of input"
318-- | See 'Streamly.Internal.Data.Parser.satisfy'.
322{-# INLINE satisfy #-}
323satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
324satisfy predicate = Parser step initial extract
333 else Error "satisfy: predicate failed"
335 extract _ = throwM $ ParseError "satisfy: end of input"
337-------------------------------------------------------------------------------
339-------------------------------------------------------------------------------
341-- | See 'Streamly.Internal.Data.Parser.take'.
346take :: Monad m => Int -> Fold m a b -> Parser m a b
347take n (Fold fstep finitial fextract) = Parser step initial extract
351 initial = Tuple' 0 <$> finitial
359 then return $ Partial 0 s1
360 else Done 0 <$> fextract res
361 | otherwise = Done 1 <$> fextract r
363 extract (Tuple' _ r) = fextract r
365-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
370takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
371takeEQ cnt (Fold fstep finitial fextract) = Parser step initial extract
377 initial = Tuple' 0 <$> finitial
385 then return (Continue 0 s1)
386 else Done 0 <$> fextract res
387 | otherwise = Done 1 <$> fextract r
389 extract (Tuple' i r) =
392 else throwM $ ParseError err
397 "takeEQ: Expecting exactly " ++ show n
398 ++ " elements, got " ++ show i
400-- | See 'Streamly.Internal.Data.Parser.takeGE'.
405takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
406takeGE cnt (Fold fstep finitial fextract) = Parser step initial extract
411 initial = Tuple' 0 <$> finitial
413 step (Tuple' i r) a = do
422 extract (Tuple' i r) = fextract r >>= f
427 "takeGE: Expecting at least " ++ show n
428 ++ " elements, got only " ++ show i
433 else throwM $ ParseError err
435-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
439{-# INLINE takeWhile #-}
440takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
441takeWhile predicate (Fold fstep finitial fextract) =
442 Parser step initial fextract
450 then Partial 0 <$> fstep s a
451 else Done 1 <$> fextract s
453-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
457{-# INLINE takeWhile1 #-}
458takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
459takeWhile1 predicate (Fold fstep finitial fextract) =
460 Parser step initial extract
464 initial = return Nothing
471 return $ Partial 0 (Just r)
472 else return $ Error "takeWhile1: empty"
477 return $ Partial 0 (Just r)
482 extract Nothing = throwM $ ParseError "takeWhile1: end of input"
483 extract (Just s) = fextract s
485-- | See 'Streamly.Internal.Data.Parser.sliceSepBy'.
489{-# INLINABLE sliceSepBy #-}
490sliceSepBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
491sliceSepBy predicate (Fold fstep finitial fextract) =
492 Parser step initial fextract
499 then Partial 0 <$> fstep s a
500 else Done 0 <$> fextract s
502-- | See 'Streamly.Internal.Data.Parser.sliceEndWith'.
506{-# INLINABLE sliceEndWith #-}
509 (a -> Bool) -> Fold m a b -> Parser m a b
510sliceEndWith = undefined
512-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
516{-# INLINABLE sliceBeginWith #-}
519 (a -> Bool) -> Fold m a b -> Parser m a b
520sliceBeginWith = undefined
522-- | See 'Streamly.Internal.Data.Parser.sliceSepByMax'.
526{-# INLINABLE sliceSepByMax #-}
527sliceSepByMax :: Monad m
528 => (a -> Bool) -> Int -> Fold m a b -> Parser m a b
529sliceSepByMax predicate cnt (Fold fstep finitial fextract) =
530 Parser step initial extract
534 initial = Tuple' 0 <$> finitial
537 | not (predicate a) =
543 return $ Partial 0 s1
544 else Done 1 <$> fextract r
545 | otherwise = Done 0 <$> fextract r
547 extract (Tuple' _ r) = fextract r
549-- | See 'Streamly.Internal.Data.Parser.wordBy'.
553{-# INLINABLE wordBy #-}
556 (a -> Bool) -> Fold m a b -> Parser m a b
559-- | See 'Streamly.Internal.Data.Parser.groupBy'.
563{-# INLINABLE groupBy #-}
566 (a -> a -> Bool) -> Fold m a b -> Parser m a b
569-- XXX use an Unfold instead of a list?
570-- XXX custom combinators for matching list, array and stream?
572-- | See 'Streamly.Internal.Data.Parser.eqBy'.
577eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
578eqBy cmp str = Parser step initial extract
584 step [] _ = return $ Done 0 ()
585 step [x] a = return $
588 else Error "eqBy: failed, yet to match the last element"
589 step (x:xs) a = return $
593 "eqBy: failed, yet to match " ++ show (length xs + 1) ++ " elements"
595 extract xs = throwM $ ParseError $
596 "eqBy: end of input, yet to match " ++ show (length xs) ++ " elements"
598-------------------------------------------------------------------------------
600-------------------------------------------------------------------------------
602-- | See 'Streamly.Internal.Data.Parser.lookahead'.
606{-# INLINE lookAhead #-}
607lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
608lookAhead (Parser step1 initial1 _) =
609 Parser step initial extract
613 initial = Tuple' 0 <$> initial1
615 step (Tuple' cnt st) a = do
619 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
620 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
621 Done _ b -> Done cnt1 b
622 Error err -> Error err
624 -- XXX returning an error let's us backtrack. To implement it in a way so
625 -- that it terminates on eof without an error then we need a way to
626 -- backtrack on eof, that will require extract to return 'Step' type.
627 extract (Tuple' n _) = throwM $ ParseError $
628 "lookAhead: end of input after consuming " ++ show n ++ " elements"
630-------------------------------------------------------------------------------
632-------------------------------------------------------------------------------
634-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
638{-# INLINE deintercalate #-}
641 Fold m a y -> Parser m x a
642 -> Fold m b z -> Parser m x b
644deintercalate = undefined
646-------------------------------------------------------------------------------
647-- Sequential Collection
648-------------------------------------------------------------------------------
650-- | See 'Streamly.Internal.Data.Parser.sequence'.
654{-# INLINE sequence #-}
657 Fold m b c -> t (Parser m a b) -> Parser m a c
658sequence _f _p = undefined
660-------------------------------------------------------------------------------
661-- Alternative Collection
662-------------------------------------------------------------------------------
664-- | See 'Streamly.Internal.Data.Parser.choice'.
671 t (Parser m a b) -> Parser m a b
672choice _ps = undefined
674-------------------------------------------------------------------------------
675-- Sequential Repetition
676-------------------------------------------------------------------------------
678-- | See 'Streamly.Internal.Data.Parser.many'.
683many :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
685-- many = countBetween 0 maxBound
687-- | See 'Streamly.Internal.Data.Parser.some'.
692some :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
694-- some f p = many (takeGE 1 f) p
695-- many = countBetween 1 maxBound
697-- | See 'Streamly.Internal.Data.Parser.countBetween'.
701{-# INLINE countBetween #-}
704 Int -> Int -> Fold m b c -> Parser m a b -> Parser m a c
705countBetween _m _n _f = undefined
706-- countBetween m n f p = many (takeBetween m n f) p
708-- | See 'Streamly.Internal.Data.Parser.count'.
715 Int -> Fold m b c -> Parser m a b -> Parser m a c
716count n = countBetween n n
717-- count n f p = many (takeEQ n f) p
719data ManyTillState fs sr sl = ManyTillR Int fs sr | ManyTillL fs sl
721-- | See 'Streamly.Internal.Data.Parser.manyTill'.
725{-# INLINE manyTill #-}
726manyTill :: MonadCatch m
727 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
728manyTill (Fold fstep finitial fextract)
729 (Parser stepL initialL extractL)
730 (Parser stepR initialR _) =
731 Parser step initial extract
737 ManyTillR 0 fs <$> initialR
739 step (ManyTillR cnt fs st) a = do
742 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
744 assert (cnt + 1 - n >= 0) (return ())
745 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
751 return $ Continue (cnt + 1) (ManyTillL fs rR)
753 step (ManyTillL fs st) a = do
756 Partial n s -> return $ Partial n (ManyTillL fs s)
757 Continue n s -> return $ Continue n (ManyTillL fs s)
761 return $ Partial n (ManyTillR 0 fs1 l)
762 Error err -> return $ Error err
764 extract (ManyTillL fs sR) = extractL sR >>= fstep fs >>= fextract
765 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