2{-# LANGUAGE RankNTypes #-}
3{-# LANGUAGE ScopedTypeVariables #-}
8-- Module : Streamly.Internal.Data.Parser.ParserD
9-- Copyright : (c) 2020 Composewell Technologies
10-- License : BSD-3-Clause
11-- Maintainer : streamly@composewell.com
12-- Stability : experimental
15-- Direct style parser implementation with stream fusion.
17module Streamly.Internal.Data.Parser.ParserD
23 -- First order parsers
42 -- Parsers chained in series, if one parser terminates the composition
43 -- terminates. Currently we are using folds to collect the output of the
44 -- parsers but we can use Parsers instead of folds to make the composition
45 -- more powerful. For example, we can do:
47 -- sliceSepByMax cond n p = sliceBy cond (take n p)
48 -- sliceSepByBetween cond m n p = sliceBy cond (takeBetween m n p)
49 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
51 -- Grab a sequence of input elements without inspecting them
54 -- , takeLE -- take -- takeBetween 0 n
55 -- , takeLE1 -- take1 -- takeBetween 1 n
56 , takeEQ -- takeBetween n n
57 , takeGE -- takeBetween n maxBound
59 -- Grab a sequence of input elements by inspecting them
65 -- , sliceSepByBetween
70 -- , frameSepBy -- parse frames escaped by an escape char/sequence
76 -- , prefixOf -- match any prefix of a given string
77 -- , suffixOf -- match any suffix of a given string
78 -- , infixOf -- match any substring of a given string
80 -- Second order parsers (parsers using parsers)
81 -- * Binary Combinators
83 -- ** Sequential Applicative
87 -- ** Parallel Applicatives
91 -- , teeTill -- like manyTill but parallel
93 -- ** Sequential Interleaving
94 -- Use two folds, run a primary parser, its rejected values go to the
98 -- ** Sequential Alternative
101 -- ** Parallel Alternatives
106 -- * N-ary Combinators
107 -- ** Sequential Collection
111 -- ** Sequential Repetition
114 -- , countBetweenTill
120 -- -- ** Special cases
121 -- XXX traditional implmentations of these may be of limited use. For
122 -- example, consider parsing lines separated by "\r\n". The main parser
123 -- will have to detect and exclude the sequence "\r\n" anyway so that we
124 -- can apply the "sep" parser.
126 -- We can instead implement these as special cases of deintercalate.
137 -- A simple and stupid impl would be to just convert the stream to an array
138 -- and give the array reference to all consumers. The array can be grown on
139 -- demand by any consumer and truncated when nonbody needs it.
141 -- -- ** Distribute to collection
142 -- -- ** Distribute to repetition
144 -- -- ** Interleaved collection
147 -- -- ** Interleaved repetition
148 -- repeat one parser and when it fails run an error recovery parser
149 -- e.g. to find a key frame in the stream after an error
151 -- ** Collection of Alternatives
154 -- , fastestN -- first N successful in time
155 -- , choiceN -- first N successful in position
156 , choice -- first successful in position
158 -- -- ** Repeated Alternatives
159 -- , retryMax -- try N times
160 -- , retryUntil -- try until successful
161 -- , retryUntilN -- try until successful n times
165import Control.Exception (assert)
166import Control.Monad.Catch (MonadCatch, MonadThrow(..))
168 hiding (any, all, take, takeWhile, sequence, concatMap)
170import Streamly.Internal.Data.Fold.Types (Fold(..))
172import qualified Streamly.Internal.Data.Parser.ParserK.Types as K
173import qualified Streamly.Internal.Data.Zipper as Z
175import Streamly.Internal.Data.Parser.ParserD.Tee
176import Streamly.Internal.Data.Parser.ParserD.Types
177import Streamly.Internal.Data.Strict
179-------------------------------------------------------------------------------
180-- Upgrade folds to parses
181-------------------------------------------------------------------------------
183-- | See 'Streamly.Internal.Data.Parser.fromFold'.
187{-# INLINE fromFold #-}
188fromFold :: Monad m => Fold m a b -> Parser m a b
189fromFold (Fold fstep finitial fextract) = Parser step finitial fextract
193 step s a = Partial 0 <$> fstep s a
195-------------------------------------------------------------------------------
196-- Convert to and from CPS style parser representation
197-------------------------------------------------------------------------------
199-- | Convert a direct style 'Parser' to a CPS style 'K.Parser'.
203{-# INLINE_LATE toParserK #-}
204toParserK :: MonadCatch m => Parser m a b -> K.Parser m a b
205toParserK (Parser step initial extract) =
206 K.MkParser $ \inp yieldk ->
207 Z.parse step initial extract inp >>= yieldk
209-- | Convert a CPS style 'K.Parser' to a direct style 'Parser'.
213{-# NOINLINE fromParserK #-}
214fromParserK :: Monad m => K.Parser m a b -> Parser m a b
215fromParserK _ = Parser step initial extract
220 step () _ = error "fromParserK: unimplemented"
221 extract () = error "fromParserK: unimplemented"
223#ifndef DISABLE_FUSION
224{-# RULES "fromParserK/toParserK fusion" [2]
225 forall s. toParserK (fromParserK s) = s #-}
226{-# RULES "toParserK/fromParserK fusion" [2]
227 forall s. fromParserK (toParserK s) = s #-}
230-------------------------------------------------------------------------------
231-- Terminating but not failing folds
232-------------------------------------------------------------------------------
235any :: Monad m => (a -> Bool) -> Parser m a Bool
236any predicate = Parser step initial return
240 initial = return False
251all :: Monad m => (a -> Bool) -> Parser m a Bool
252all predicate = Parser step initial return
256 initial = return True
266-------------------------------------------------------------------------------
268-------------------------------------------------------------------------------
270-- | See 'Streamly.Internal.Data.Parser.peek'.
274{-# INLINABLE peek #-}
275peek :: MonadThrow m => Parser m a a
276peek = Parser step initial extract
282 step () a = return $ Done 1 a
284 extract () = throwM $ ParseError "peek: end of input"
286-- | See 'Streamly.Internal.Data.Parser.eof'.
291eof :: Monad m => Parser m a ()
292eof = Parser step initial return
298 step () _ = return $ Error "eof: not at end of input"
300-- | See 'Streamly.Internal.Data.Parser.satisfy'.
304{-# INLINE satisfy #-}
305satisfy :: MonadThrow m => (a -> Bool) -> Parser m a a
306satisfy predicate = Parser step initial extract
315 else Error "satisfy: predicate failed"
317 extract _ = throwM $ ParseError "satisfy: end of input"
319-------------------------------------------------------------------------------
321-------------------------------------------------------------------------------
323-- | See 'Streamly.Internal.Data.Parser.take'.
328take :: Monad m => Int -> Fold m a b -> Parser m a b
329take n (Fold fstep finitial fextract) = Parser step initial extract
333 initial = Tuple' 0 <$> finitial
335 step (Tuple' i r) a = do
340 then return $ Partial 0 s1
341 else Done 0 <$> fextract res
343 extract (Tuple' _ r) = fextract r
345-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
350takeEQ :: MonadThrow m => Int -> Fold m a b -> Parser m a b
351takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
355 initial = Tuple' 0 <$> finitial
357 step (Tuple' i r) a = do
361 if i1 < n then return (Continue 0 s1) else Done 0 <$> fextract res
363 extract (Tuple' i r) =
366 else throwM $ ParseError err
371 "takeEQ: Expecting exactly " ++ show n
372 ++ " elements, got " ++ show i
374-- | See 'Streamly.Internal.Data.Parser.takeGE'.
379takeGE :: MonadThrow m => Int -> Fold m a b -> Parser m a b
380takeGE n (Fold fstep finitial fextract) = Parser step initial extract
384 initial = Tuple' 0 <$> finitial
386 step (Tuple' i r) a = do
395 extract (Tuple' i r) = fextract r >>= f
400 "takeGE: Expecting at least " ++ show n
401 ++ " elements, got only " ++ show i
406 else throwM $ ParseError err
408-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
412{-# INLINE takeWhile #-}
413takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
414takeWhile predicate (Fold fstep finitial fextract) =
415 Parser step initial fextract
423 then Partial 0 <$> fstep s a
424 else Done 1 <$> fextract s
426-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
430{-# INLINE takeWhile1 #-}
431takeWhile1 :: MonadThrow m => (a -> Bool) -> Fold m a b -> Parser m a b
432takeWhile1 predicate (Fold fstep finitial fextract) =
433 Parser step initial extract
437 initial = return Nothing
444 return $ Partial 0 (Just r)
445 else return $ Error "takeWhile1: empty"
450 return $ Partial 0 (Just r)
455 extract Nothing = throwM $ ParseError "takeWhile1: end of input"
456 extract (Just s) = fextract s
458-- | See 'Streamly.Internal.Data.Parser.sliceSepBy'.
462{-# INLINABLE sliceSepBy #-}
463sliceSepBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
464sliceSepBy predicate (Fold fstep finitial fextract) =
465 Parser step initial fextract
472 then Partial 0 <$> fstep s a
473 else Done 0 <$> fextract s
475-- | See 'Streamly.Internal.Data.Parser.sliceEndWith'.
479{-# INLINABLE sliceEndWith #-}
482 (a -> Bool) -> Fold m a b -> Parser m a b
483sliceEndWith = undefined
485-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
489{-# INLINABLE sliceBeginWith #-}
492 (a -> Bool) -> Fold m a b -> Parser m a b
493sliceBeginWith = undefined
495-- | See 'Streamly.Internal.Data.Parser.sliceSepByMax'.
499{-# INLINABLE sliceSepByMax #-}
500sliceSepByMax :: Monad m
501 => (a -> Bool) -> Int -> Fold m a b -> Parser m a b
502sliceSepByMax predicate cnt (Fold fstep finitial fextract) =
503 Parser step initial extract
507 initial = Tuple' 0 <$> finitial
509 | not (predicate a) = do
514 then return $ Partial 0 s1
518 | otherwise = Done 0 <$> fextract r
519 extract (Tuple' _ r) = fextract r
521-- | See 'Streamly.Internal.Data.Parser.wordBy'.
525{-# INLINABLE wordBy #-}
528 (a -> Bool) -> Fold m a b -> Parser m a b
531-- | See 'Streamly.Internal.Data.Parser.groupBy'.
535{-# INLINABLE groupBy #-}
538 (a -> a -> Bool) -> Fold m a b -> Parser m a b
541-- XXX use an Unfold instead of a list?
542-- XXX custom combinators for matching list, array and stream?
544-- | See 'Streamly.Internal.Data.Parser.eqBy'.
549eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
550eqBy cmp str = Parser step initial extract
556 step [] _ = return $ Done 0 ()
557 step [x] a = return $
560 else Error "eqBy: failed, yet to match the last element"
561 step (x:xs) a = return $
565 "eqBy: failed, yet to match " ++ show (length xs + 1) ++ " elements"
567 extract xs = throwM $ ParseError $
568 "eqBy: end of input, yet to match " ++ show (length xs) ++ " elements"
570-------------------------------------------------------------------------------
572-------------------------------------------------------------------------------
574-- | See 'Streamly.Internal.Data.Parser.lookahead'.
578{-# INLINE lookAhead #-}
579lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
580lookAhead (Parser step1 initial1 _) =
581 Parser step initial extract
585 initial = Tuple' 0 <$> initial1
587 step (Tuple' cnt st) a = do
591 Partial n s -> Continue n (Tuple' (cnt1 - n) s)
592 Continue n s -> Continue n (Tuple' (cnt1 - n) s)
593 Done _ b -> Done cnt1 b
594 Error err -> Error err
596 -- XXX returning an error let's us backtrack. To implement it in a way so
597 -- that it terminates on eof without an error then we need a way to
598 -- backtrack on eof, that will require extract to return 'Step' type.
599 extract (Tuple' n _) = throwM $ ParseError $
600 "lookAhead: end of input after consuming " ++ show n ++ " elements"
602-------------------------------------------------------------------------------
604-------------------------------------------------------------------------------
606-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
610{-# INLINE deintercalate #-}
613 Fold m a y -> Parser m x a
614 -> Fold m b z -> Parser m x b
616deintercalate = undefined
618-------------------------------------------------------------------------------
619-- Sequential Collection
620-------------------------------------------------------------------------------
622-- | See 'Streamly.Internal.Data.Parser.sequence'.
626{-# INLINE sequence #-}
629 Fold m b c -> t (Parser m a b) -> Parser m a c
630sequence _f _p = undefined
632-------------------------------------------------------------------------------
633-- Alternative Collection
634-------------------------------------------------------------------------------
636-- | See 'Streamly.Internal.Data.Parser.choice'.
643 t (Parser m a b) -> Parser m a b
644choice _ps = undefined
646-------------------------------------------------------------------------------
647-- Sequential Repetition
648-------------------------------------------------------------------------------
650-- | See 'Streamly.Internal.Data.Parser.many'.
655many :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
657-- many = countBetween 0 maxBound
659-- | See 'Streamly.Internal.Data.Parser.some'.
664some :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
666-- some f p = many (takeGE 1 f) p
667-- many = countBetween 1 maxBound
669-- | See 'Streamly.Internal.Data.Parser.countBetween'.
673{-# INLINE countBetween #-}
676 Int -> Int -> Fold m b c -> Parser m a b -> Parser m a c
677countBetween _m _n _f = undefined
678-- countBetween m n f p = many (takeBetween m n f) p
680-- | See 'Streamly.Internal.Data.Parser.count'.
687 Int -> Fold m b c -> Parser m a b -> Parser m a c
688count n = countBetween n n
689-- count n f p = many (takeEQ n f) p
691data ManyTillState fs sr sl = ManyTillR Int fs sr | ManyTillL fs sl
693-- | See 'Streamly.Internal.Data.Parser.manyTill'.
697{-# INLINE manyTill #-}
698manyTill :: MonadCatch m
699 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
700manyTill (Fold fstep finitial fextract)
701 (Parser stepL initialL extractL)
702 (Parser stepR initialR _) =
703 Parser step initial extract
709 ManyTillR 0 fs <$> initialR
711 step (ManyTillR cnt fs st) a = do
714 Partial n s -> return $ Partial n (ManyTillR 0 fs s)
716 assert (cnt + 1 - n >= 0) (return ())
717 return $ Continue n (ManyTillR (cnt + 1 - n) fs s)
723 return $ Continue (cnt + 1) (ManyTillL fs rR)
725 step (ManyTillL fs st) a = do
728 Partial n s -> return $ Partial n (ManyTillL fs s)
729 Continue n s -> return $ Continue n (ManyTillL fs s)
733 return $ Partial n (ManyTillR 0 fs1 l)
734 Error err -> return $ Error err
736 extract (ManyTillL fs sR) = extractL sR >>= fstep fs >>= fextract
737 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