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 = Yield 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 $ Stop 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 $ Yield 0 s1
341 else Stop 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 (Skip 0 s1) else Stop 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 Yield 0 <$> fstep s a
424 else Stop 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 $ Yield 0 (Just r)
445 else return $ Error "takeWhile1: empty"
450 return $ Yield 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 Yield 0 <$> fstep s a
473 else Stop 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
508 step (Tuple' i r) a = do
512 if not (predicate a) && i1 < cnt
513 then return $ Yield 0 s1
517 extract (Tuple' _ r) = fextract r
519-- | See 'Streamly.Internal.Data.Parser.wordBy'.
523{-# INLINABLE wordBy #-}
526 (a -> Bool) -> Fold m a b -> Parser m a b
529-- | See 'Streamly.Internal.Data.Parser.groupBy'.
533{-# INLINABLE groupBy #-}
536 (a -> a -> Bool) -> Fold m a b -> Parser m a b
539-- XXX use an Unfold instead of a list?
540-- XXX custom combinators for matching list, array and stream?
542-- | See 'Streamly.Internal.Data.Parser.eqBy'.
547eqBy :: MonadThrow m => (a -> a -> Bool) -> [a] -> Parser m a ()
548eqBy cmp str = Parser step initial extract
554 step [] _ = error "Bug: unreachable"
555 step [x] a = return $
558 else Error "eqBy: failed, at the last element"
559 step (x:xs) a = return $
563 "eqBy: failed, yet to match " ++ show (length xs) ++ " elements"
565 extract xs = throwM $ ParseError $
566 "eqBy: end of input, yet to match " ++ show (length xs) ++ " elements"
568-------------------------------------------------------------------------------
570-------------------------------------------------------------------------------
572-- | See 'Streamly.Internal.Data.Parser.lookahead'.
576{-# INLINE lookAhead #-}
577lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
578lookAhead (Parser step1 initial1 _) =
579 Parser step initial extract
583 initial = Tuple' 0 <$> initial1
585 step (Tuple' cnt st) a = do
589 Yield _ s -> Skip 0 (Tuple' cnt1 s)
590 YieldB n s -> Skip n (Tuple' (cnt1 - n) s)
591 Skip n s -> Skip n (Tuple' (cnt1 - n) s)
592 Stop _ b -> Stop cnt1 b
593 Error err -> Error err
595 -- XXX returning an error let's us backtrack. To implement it in a way so
596 -- that it terminates on eof without an error then we need a way to
597 -- backtrack on eof, that will require extract to return 'Step' type.
598 extract (Tuple' n _) = throwM $ ParseError $
599 "lookAhead: end of input after consuming " ++ show n ++ " elements"
601-------------------------------------------------------------------------------
603-------------------------------------------------------------------------------
605-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
609{-# INLINE deintercalate #-}
612 Fold m a y -> Parser m x a
613 -> Fold m b z -> Parser m x b
615deintercalate = undefined
617-------------------------------------------------------------------------------
618-- Sequential Collection
619-------------------------------------------------------------------------------
621-- | See 'Streamly.Internal.Data.Parser.sequence'.
625{-# INLINE sequence #-}
628 Fold m b c -> t (Parser m a b) -> Parser m a c
629sequence _f _p = undefined
631-------------------------------------------------------------------------------
632-- Alternative Collection
633-------------------------------------------------------------------------------
635-- | See 'Streamly.Internal.Data.Parser.choice'.
642 t (Parser m a b) -> Parser m a b
643choice _ps = undefined
645-------------------------------------------------------------------------------
646-- Sequential Repetition
647-------------------------------------------------------------------------------
649-- | See 'Streamly.Internal.Data.Parser.many'.
654many :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
656-- many = countBetween 0 maxBound
658-- | See 'Streamly.Internal.Data.Parser.some'.
663some :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
665-- some f p = many (takeGE 1 f) p
666-- many = countBetween 1 maxBound
668-- | See 'Streamly.Internal.Data.Parser.countBetween'.
672{-# INLINE countBetween #-}
675 Int -> Int -> Fold m b c -> Parser m a b -> Parser m a c
676countBetween _m _n _f = undefined
677-- countBetween m n f p = many (takeBetween m n f) p
679-- | See 'Streamly.Internal.Data.Parser.count'.
686 Int -> Fold m b c -> Parser m a b -> Parser m a c
687count n = countBetween n n
688-- count n f p = many (takeEQ n f) p
690data ManyTillState fs sr sl = ManyTillR Int fs sr | ManyTillL fs sl
692-- | See 'Streamly.Internal.Data.Parser.manyTill'.
696{-# INLINE manyTill #-}
697manyTill :: MonadCatch m
698 => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
699manyTill (Fold fstep finitial fextract)
700 (Parser stepL initialL extractL)
701 (Parser stepR initialR _) =
702 Parser step initial extract
708 ManyTillR 0 fs <$> initialR
710 step (ManyTillR cnt fs st) a = do
713 Yield n s -> return $ Yield n (ManyTillR 0 fs s)
714 YieldB n s -> return $ YieldB n (ManyTillR 0 fs s)
716 assert (cnt + 1 - n >= 0) (return ())
717 return $ Skip n (ManyTillR (cnt + 1 - n) fs s)
723 return $ Skip (cnt + 1) (ManyTillL fs rR)
725 step (ManyTillL fs st) a = do
728 Yield n s -> return $ Yield n (ManyTillL fs s)
729 YieldB n s -> return $ YieldB n (ManyTillL fs s)
730 Skip n s -> return $ Skip n (ManyTillL fs s)
734 return $ YieldB n (ManyTillR 0 fs1 l)
735 Error err -> return $ Error err
737 extract (ManyTillL fs sR) = extractL sR >>= fstep fs >>= fextract
738 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