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(..))
163import Streamly.Internal.Data.Fold.Types (Fold(..))
164import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
167 (any, all, take, takeWhile, sequence, concatMap, maybe, either)
168import Streamly.Internal.Data.Parser.ParserD.Tee
169import Streamly.Internal.Data.Parser.ParserD.Types
171-------------------------------------------------------------------------------
172-- Upgrade folds to parses
173-------------------------------------------------------------------------------
175-- | See 'Streamly.Internal.Data.Parser.fromFold'.
179{-# INLINE fromFold #-}
180fromFold :: Monad m => Fold m a b -> Parser m a b
181fromFold (Fold fstep finitial fextract) = Parser step finitial fextract
185 step s a = Partial 0 <$> fstep s a
187-------------------------------------------------------------------------------
188-- Terminating but not failing folds
189-------------------------------------------------------------------------------
192any :: Monad m => (a -> Bool) -> Parser m a Bool
193any predicate = Parser step initial return
197 initial = return False
199 step s a = return (if s || predicate a then Done 0 True else Partial 0 False)
202all :: Monad m => (a -> Bool) -> Parser m a Bool
203all predicate = Parser step initial return
207 initial = return True
209 step s a = return (if s && predicate a then Partial 0 True else Done 0 False)
211-------------------------------------------------------------------------------
213-------------------------------------------------------------------------------
215-- | See 'Streamly.Internal.Data.Parser.peek'.
219{-# INLINABLE peek #-}
220peek :: MonadThrow m => Parser m a a
221peek = Parser step initial extract
227 step () a = return $ Done 1 a
229 extract () = throwM $ ParseError "peek: end of input"
231-- | See 'Streamly.Internal.Data.Parser.eof'.
236eof :: Monad m => Parser m a ()
237eof = Parser step initial return
243 step () _ = return $ Error "eof: not at end of input"
245-- | See 'Streamly.Internal.Data.Parser.satisfy'.
249{-# INLINE satisfy #-}
250satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
251satisfy predicate = Parser step initial extract
260 else Error "satisfy: predicate failed"
262 extract _ = throwM $ ParseError "satisfy: end of input"
264-- | See 'Streamly.Internal.Data.Parser.maybe'.
269maybe :: MonadThrow m => (a -> Maybe b) -> Parser m a b
270maybe parser = Parser step initial extract
279 Nothing -> Error "maybe: predicate failed"
281 extract _ = throwM $ ParseError "maybe: end of input"
283-- | See 'Streamly.Internal.Data.Parser.either'.
288either :: MonadThrow m => (a -> Either String b) -> Parser m a b
289either parser = Parser step initial extract
298 Left err -> Error $ "either: " ++ err
300 extract _ = throwM $ ParseError "either: end of input"
302-------------------------------------------------------------------------------
304-------------------------------------------------------------------------------
306-- | See 'Streamly.Internal.Data.Parser.take'.
311take :: Monad m => Int -> Fold m a b -> Parser m a b
312take n (Fold fstep finitial fextract) = Parser step initial extract
316 initial = Tuple' 0 <$> finitial
324 then return $ Partial 0 s1
325 else Done 0 <$> fextract res
326 | otherwise = Done 1 <$> fextract r
328 extract (Tuple' _ r) = fextract r
330-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
335takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
336takeEQ cnt (Fold fstep finitial fextract) = Parser step initial extract
342 initial = Tuple' 0 <$> finitial
350 then return (Continue 0 s1)
351 else Done 0 <$> fextract res
352 | otherwise = Done 1 <$> fextract r
354 extract (Tuple' i r) =
357 else throwM $ ParseError err
362 "takeEQ: Expecting exactly " ++ show n
363 ++ " elements, got " ++ show i
365-- | See 'Streamly.Internal.Data.Parser.takeGE'.
370takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
371takeGE cnt (Fold fstep finitial fextract) = Parser step initial extract
376 initial = Tuple' 0 <$> finitial
378 step (Tuple' i r) a = do
387 extract (Tuple' i r) = fextract r >>= f
392 "takeGE: Expecting at least " ++ show n
393 ++ " elements, got only " ++ show i
398 else throwM $ ParseError err
400-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
404{-# INLINE takeWhile #-}
405takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
406takeWhile predicate (Fold fstep finitial fextract) =
407 Parser step initial fextract
415 then Partial 0 <$> fstep s a
416 else Done 1 <$> fextract s
418-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
422{-# INLINE takeWhile1 #-}
423takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
424takeWhile1 predicate (Fold fstep finitial fextract) =
425 Parser step initial extract
429 initial = return Nothing
436 return $ Partial 0 (Just r)
437 else return $ Error "takeWhile1: empty"
442 return $ Partial 0 (Just r)
447 extract Nothing = throwM $ ParseError "takeWhile1: end of input"
448 extract (Just s) = fextract s
450-- | See 'Streamly.Internal.Data.Parser.sliceSepBy'.
454{-# INLINABLE sliceSepBy #-}
455sliceSepBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
456sliceSepBy predicate (Fold fstep finitial fextract) =
457 Parser step initial fextract
464 then Partial 0 <$> fstep s a
465 else Done 0 <$> fextract s
467-- | See 'Streamly.Internal.Data.Parser.sliceEndWith'.
471{-# INLINABLE sliceEndWith #-}
474 (a -> Bool) -> Fold m a b -> Parser m a b
475sliceEndWith = undefined
477-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
481{-# INLINABLE sliceBeginWith #-}
484 (a -> Bool) -> Fold m a b -> Parser m a b
485sliceBeginWith = undefined
487-- | See 'Streamly.Internal.Data.Parser.sliceSepByMax'.
491{-# INLINABLE sliceSepByMax #-}
492sliceSepByMax :: Monad m
493 => (a -> Bool) -> Int -> Fold m a b -> Parser m a b
494sliceSepByMax predicate cnt (Fold fstep finitial fextract) =
495 Parser step initial extract
499 initial = Tuple' 0 <$> finitial
502 | not (predicate a) =
508 return $ Partial 0 s1
509 else Done 1 <$> fextract r
510 | otherwise = Done 0 <$> fextract r
512 extract (Tuple' _ r) = fextract r
514-- | See 'Streamly.Internal.Data.Parser.wordBy'.
518{-# INLINABLE wordBy #-}
521 (a -> Bool) -> Fold m a b -> Parser m a b
524-- | See 'Streamly.Internal.Data.Parser.groupBy'.
528{-# INLINABLE groupBy #-}
531 (a -> a -> Bool) -> Fold m a b -> Parser m a b
534-- XXX use an Unfold instead of a list?
535-- XXX custom combinators for matching list, array and stream?
537-- | See 'Streamly.Internal.Data.Parser.eqBy'.
542eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
543eqBy cmp str = Parser step initial extract
549 step [] _ = return $ Done 0 ()
550 step [x] a = return $
553 else Error "eqBy: failed, yet to match the last element"
554 step (x:xs) a = return $
558 "eqBy: failed, yet to match " ++ show (length xs + 1) ++ " elements"
560 extract xs = throwM $ ParseError $
561 "eqBy: end of input, yet to match " ++ show (length xs) ++ " elements"
563-------------------------------------------------------------------------------
565-------------------------------------------------------------------------------
567-- | See 'Streamly.Internal.Data.Parser.lookahead'.
571{-# INLINE lookAhead #-}
572lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
573lookAhead (Parser step1 initial1 _) =
574 Parser step initial extract
578 initial = Tuple' 0 <$> initial1
580 step (Tuple' cnt st) a = do
584 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
585 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
586 Done _ b -> Done cnt1 b
587 Error err -> Error err
589 -- XXX returning an error let's us backtrack. To implement it in a way so
590 -- that it terminates on eof without an error then we need a way to
591 -- backtrack on eof, that will require extract to return 'Step' type.
592 extract (Tuple' n _) = throwM $ ParseError $
593 "lookAhead: end of input after consuming " ++ show n ++ " elements"
595-------------------------------------------------------------------------------
597-------------------------------------------------------------------------------
599-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
603{-# INLINE deintercalate #-}
606 Fold m a y -> Parser m x a
607 -> Fold m b z -> Parser m x b
609deintercalate = undefined
611-------------------------------------------------------------------------------
612-- Sequential Collection
613-------------------------------------------------------------------------------
615-- | See 'Streamly.Internal.Data.Parser.sequence'.
619{-# INLINE sequence #-}
622 Fold m b c -> t (Parser m a b) -> Parser m a c
623sequence _f _p = undefined
625-------------------------------------------------------------------------------
626-- Alternative Collection
627-------------------------------------------------------------------------------
629-- | See 'Streamly.Internal.Data.Parser.choice'.
636 t (Parser m a b) -> Parser m a b
637choice _ps = undefined
639-------------------------------------------------------------------------------
640-- Sequential Repetition
641-------------------------------------------------------------------------------
643-- | See 'Streamly.Internal.Data.Parser.many'.
648many :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
650-- many = countBetween 0 maxBound
652-- | See 'Streamly.Internal.Data.Parser.some'.
657some :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
659-- some f p = many (takeGE 1 f) p
660-- many = countBetween 1 maxBound
662-- | See 'Streamly.Internal.Data.Parser.countBetween'.
666{-# INLINE countBetween #-}
669 Int -> Int -> Fold m b c -> Parser m a b -> Parser m a c
670countBetween _m _n _f = undefined
671-- countBetween m n f p = many (takeBetween m n f) p
673-- | See 'Streamly.Internal.Data.Parser.count'.
680 Int -> Fold m b c -> Parser m a b -> Parser m a c
681count n = countBetween n n
682-- count n f p = many (takeEQ n f) p
684data ManyTillState fs sr sl = ManyTillR Int fs sr | ManyTillL fs sl
686-- | See 'Streamly.Internal.Data.Parser.manyTill'.
690{-# INLINE manyTill #-}
691manyTill :: MonadCatch m
692 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
693manyTill (Fold fstep finitial fextract)
694 (Parser stepL initialL extractL)
695 (Parser stepR initialR _) =
696 Parser step initial extract
702 ManyTillR 0 fs <$> initialR
704 step (ManyTillR cnt fs st) a = do
707 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
709 assert (cnt + 1 - n >= 0) (return ())
710 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
716 return $ Continue (cnt + 1) (ManyTillL fs rR)
718 step (ManyTillL fs st) a = do
721 Partial n s -> return $ Partial n (ManyTillL fs s)
722 Continue n s -> return $ Continue n (ManyTillL fs s)
726 return $ Partial n (ManyTillR 0 fs1 l)
727 Error err -> return $ Error err
729 extract (ManyTillL fs sR) = extractL sR >>= fstep fs >>= fextract
730 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