on master help sign in
Remove the rewrite rule based Parser wrapper layer
36bbd3bHarendra Kumar
benchmark/Streamly/Benchmark/Data/Parser.hs+16−60
@@ -18,7 +18,6 @@ module Main
18 ) where
18 ) where
19
19
20import Control.DeepSeq (NFData(..))
20import Control.DeepSeq (NFData(..))
21−import Data.Foldable (asum)
22import Data.Functor (($>))
21import Data.Functor (($>))
23import Data.Monoid (Sum(..))
22import Data.Monoid (Sum(..))
24import GHC.Magic (inline)
23import GHC.Magic (inline)
@@ -30,8 +29,6 @@ import Streamly.Internal.Data.Stream.StreamD (Stream)
30import Prelude hiding
29import Prelude hiding
31 (any, all, take, sequence, sequence_, sequenceA, takeWhile, dropWhile)
30 (any, all, take, sequence, sequence_, sequenceA, takeWhile, dropWhile)
32
31
33−import qualified Data.Traversable as TR
34−import qualified Data.Foldable as F
35import qualified Control.Applicative as AP
32import qualified Control.Applicative as AP
36import qualified Streamly.FileSystem.Handle as Handle
33import qualified Streamly.FileSystem.Handle as Handle
37import qualified Streamly.Internal.Data.Array as Array
34import qualified Streamly.Internal.Data.Array as Array
@@ -113,18 +110,6 @@ benchIOSink value name f =
113-- Parsers
110-- Parsers
114-------------------------------------------------------------------------------
111-------------------------------------------------------------------------------
115
112
116−{-# INLINE one #-}
117−one :: Monad m => Int -> Stream m Int -> m (Either ParseError (Maybe Int))
118−one value = Stream.parse p
119−
120− where
121−
122− p = do
123− m <- PR.fromFold Fold.one
124− case m of
125− Just i -> if i >= value then pure m else p
126− Nothing -> pure Nothing
127−
128{-# INLINE takeBetween #-}
113{-# INLINE takeBetween #-}
129takeBetween :: Monad m => Int -> Stream m a -> m (Either ParseError ())
114takeBetween :: Monad m => Int -> Stream m a -> m (Either ParseError ())
130takeBetween value = Stream.parse (PR.takeBetween 0 value Fold.drain)
115takeBetween value = Stream.parse (PR.takeBetween 0 value Fold.drain)
@@ -187,14 +172,20 @@ wordBy :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
187wordBy value = Stream.parse (PR.wordBy (>= value) Fold.drain)
172wordBy value = Stream.parse (PR.wordBy (>= value) Fold.drain)
188
173
189{-# INLINE sepByWords #-}
174{-# INLINE sepByWords #-}
190−sepByWords :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
175+sepByWords :: Monad m => Stream m Int -> m (Either ParseError ())
191−sepByWords _ = Stream.parse (wrds even Fold.drain)
176+sepByWords = Stream.parse (wrds even Fold.drain)
192 where
177 where
193 wrds p = PR.sepBy (PR.takeWhile (not . p) Fold.drain) (PR.dropWhile p)
178 wrds p = PR.sepBy (PR.takeWhile (not . p) Fold.drain) (PR.dropWhile p)
194
179
180+-- Returning a list to compare with the sepBy1 in ParserK
181+{-# INLINE sepBy1 #-}
182+sepBy1 :: Monad m => Stream m Int -> m (Either ParseError [Int])
183+sepBy1 xs = do
184+ Stream.parse (PR.sepBy1 (PR.satisfy odd) (PR.satisfy even) Fold.toList) xs
185+
195{-# INLINE sepByWords1 #-}
186{-# INLINE sepByWords1 #-}
196−sepByWords1 :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
187+sepByWords1 :: Monad m => Stream m Int -> m (Either ParseError ())
197−sepByWords1 _ = Stream.parse (wrds even Fold.drain)
188+sepByWords1 = Stream.parse (wrds even Fold.drain)
198 where
189 where
199 wrds p = PR.sepBy1 (PR.takeWhile (not . p) Fold.drain) (PR.dropWhile p)
190 wrds p = PR.sepBy1 (PR.takeWhile (not . p) Fold.drain) (PR.dropWhile p)
200
191
@@ -368,34 +359,6 @@ lookAhead :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
368lookAhead value =
359lookAhead value =
369 Stream.parse (PR.lookAhead (PR.takeWhile (<= value) Fold.drain) $> ())
360 Stream.parse (PR.lookAhead (PR.takeWhile (<= value) Fold.drain) $> ())
370
361
371−{-# INLINE sequenceA #-}
372−sequenceA :: Monad m => Int -> Stream m Int -> m Int
373−sequenceA value xs = do
374− x <- Stream.parse (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
375− return $ length x
376−
377−{-# INLINE sequenceA_ #-}
378−sequenceA_ :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
379−sequenceA_ value =
380− Stream.parse (F.sequenceA_ $ replicate value (PR.satisfy (> 0)))
381−
382−{-# INLINE sequence #-}
383−sequence :: Monad m => Int -> Stream m Int -> m Int
384−sequence value xs = do
385− x <- Stream.parse (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
386− return $ length x
387−
388−{-# INLINE sequence_ #-}
389−sequence_ :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
390−sequence_ value =
391− Stream.parse (F.sequence_ $ replicate value (PR.satisfy (> 0)))
392−
393−{-# INLINE choiceAsum #-}
394−choiceAsum :: Monad m => Int -> Stream m Int -> m (Either ParseError Int)
395−choiceAsum value =
396− Stream.parse (asum (replicate value (PR.satisfy (< 0)))
397− AP.<|> PR.satisfy (> 0))
398−
399{-
362{-
400{-# INLINE choice #-}
363{-# INLINE choice #-}
401choice :: Monad m => Int -> Stream m Int -> m (Either ParseError Int)
364choice :: Monad m => Int -> Stream m Int -> m (Either ParseError Int)
@@ -429,7 +392,8 @@ parseIterate n =
429
392
430{-# INLINE concatSequence #-}
393{-# INLINE concatSequence #-}
431concatSequence :: Monad m => Stream m Int -> m (Either ParseError ())
394concatSequence :: Monad m => Stream m Int -> m (Either ParseError ())
395+concatSequence =
432−concatSequence = Stream.parse $ PR.concatSequence Fold.drain $ Stream.repeat PR.one
396+ Stream.parse $ PR.sequence (Stream.repeat PR.one) Fold.drain
433
397
434{-# INLINE parseManyGroupBy #-}
398{-# INLINE parseManyGroupBy #-}
435parseManyGroupBy :: Monad m => (Int -> Int -> Bool) -> Stream m Int -> m ()
399parseManyGroupBy :: Monad m => (Int -> Int -> Bool) -> Stream m Int -> m ()
@@ -449,8 +413,7 @@ instance NFData ParseError where
449
413
450o_1_space_serial :: Int -> [Benchmark]
414o_1_space_serial :: Int -> [Benchmark]
451o_1_space_serial value =
415o_1_space_serial value =
452− [ benchIOSink value "one (fold)" $ one value
453− , benchIOSink value "takeBetween" $ takeBetween value
416+ [ benchIOSink value "takeBetween" $ takeBetween value
454 , benchIOSink value "takeEQ" $ takeEQ value
417 , benchIOSink value "takeEQ" $ takeEQ value
455 , benchIOSink value "takeWhile" $ takeWhile value
418 , benchIOSink value "takeWhile" $ takeWhile value
456 , benchIOSink value "takeWhileP" $ takeWhileP value
419 , benchIOSink value "takeWhileP" $ takeWhileP value
@@ -462,8 +425,9 @@ o_1_space_serial value =
462 , benchIOSink value "groupBy" $ groupBy
425 , benchIOSink value "groupBy" $ groupBy
463 , benchIOSink value "groupByRolling" $ groupByRolling
426 , benchIOSink value "groupByRolling" $ groupByRolling
464 , benchIOSink value "wordBy" $ wordBy value
427 , benchIOSink value "wordBy" $ wordBy value
465− , benchIOSink value "sepBy (words)" $ sepByWords value
428+ , benchIOSink value "sepBy (words)" sepByWords
429+ , benchIOSink value "sepBy1" sepBy1
466− , benchIOSink value "sepBy1 (words)" $ sepByWords1 value
430+ , benchIOSink value "sepBy1 (words)" sepByWords1
467 , benchIOSink value "deintercalate" $ deintercalate value
431 , benchIOSink value "deintercalate" $ deintercalate value
468 , benchIOSink value "splitAp" $ splitAp value
432 , benchIOSink value "splitAp" $ splitAp value
469 , benchIOSink value "splitApBefore" $ splitApBefore value
433 , benchIOSink value "splitApBefore" $ splitApBefore value
@@ -513,17 +477,9 @@ o_n_heap_serial value =
513 -- lookahead benchmark holds the entire input till end
477 -- lookahead benchmark holds the entire input till end
514 benchIOSink value "lookAhead" $ lookAhead value
478 benchIOSink value "lookAhead" $ lookAhead value
515
479
516− -- accumulates the results in a list
517− , benchIOSink value "sequence" $ sequence value
518− , benchIOSink value "sequenceA" $ sequenceA value
519−
520− -- XXX why should this take O(n) heap, it discards the results?
521− , benchIOSink value "sequence_" $ sequence_ value
522− , benchIOSink value "sequenceA_" $ sequenceA_ value
523 -- non-linear time complexity (parserD)
480 -- non-linear time complexity (parserD)
524 , benchIOSink value "split_" $ split_ value
481 , benchIOSink value "split_" $ split_ value
525 -- XXX why O(n) heap?
482 -- XXX why O(n) heap?
526− , benchIOSink value "choice (asum)" $ choiceAsum value
527 -- , benchIOSink value "choice" $ choice value
483 -- , benchIOSink value "choice" $ choice value
528
484
529 -- These show non-linear time complexity.
485 -- These show non-linear time complexity.
benchmark/Streamly/Benchmark/Data/Parser/ParserK.hs+48−7
@@ -19,11 +19,13 @@ import Data.Foldable (asum)
19import Streamly.Internal.Data.Parser (ParseError(..))
19import Streamly.Internal.Data.Parser (ParseError(..))
20import Streamly.Internal.Data.Stream.StreamD (Stream)
20import Streamly.Internal.Data.Stream.StreamD (Stream)
21import System.Random (randomRIO)
21import System.Random (randomRIO)
22+import Prelude hiding
22−import Prelude hiding (any, all, take, sequence, sequenceA, takeWhile)
23+ (any, all, take, sequence, sequence_, sequenceA, takeWhile)
23
24
24import qualified Control.Applicative as AP
25import qualified Control.Applicative as AP
25import qualified Data.Foldable as F
26import qualified Data.Foldable as F
26import qualified Data.Traversable as TR
27import qualified Data.Traversable as TR
28+import qualified Streamly.Data.Parser.ParserK as ParserK
27import qualified Streamly.Internal.Data.Fold as FL
29import qualified Streamly.Internal.Data.Fold as FL
28import qualified Streamly.Internal.Data.Parser.ParserK.Type as PR
30import qualified Streamly.Internal.Data.Parser.ParserK.Type as PR
29import qualified Streamly.Internal.Data.Parser.ParserD as PRD
31import qualified Streamly.Internal.Data.Parser.ParserD as PRD
@@ -59,11 +61,19 @@ benchIOSink value name f =
59-- Parsers
61-- Parsers
60-------------------------------------------------------------------------------
62-------------------------------------------------------------------------------
61
63
62−#ifdef FROM_PARSERK
63−#define PARSE_OP (Stream.parseD . PRD.fromParserK)
64+#define PARSE_OP (Stream.parse . PRD.fromParserK)
64−#else
65+
65−#define PARSE_OP Stream.parse
66+{-# INLINE one #-}
66−#endif
67+one :: Monad m => Int -> Stream m Int -> m (Either ParseError (Maybe Int))
68+one value = Stream.parse (ParserK.toParser p)
69+
70+ where
71+
72+ p = do
73+ m <- ParserK.fromFold FL.one
74+ case m of
75+ Just i -> if i >= value then pure m else p
76+ Nothing -> pure Nothing
67
77
68{-# INLINE satisfy #-}
78{-# INLINE satisfy #-}
69satisfy :: Monad m => (a -> Bool) -> PR.Parser a m a
79satisfy :: Monad m => (a -> Bool) -> PR.Parser a m a
@@ -106,6 +116,13 @@ sequence value xs = do
106 x <- PARSE_OP (TR.sequence list) xs
116 x <- PARSE_OP (TR.sequence list) xs
107 return $ Prelude.length x
117 return $ Prelude.length x
108
118
119+{-# INLINE sequence_ #-}
120+sequence_ :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
121+sequence_ value =
122+ let parser = satisfy (> 0)
123+ list = Prelude.replicate value parser
124+ in PARSE_OP (F.sequence_ list)
125+
109{-# INLINE manyAlt #-}
126{-# INLINE manyAlt #-}
110manyAlt :: Monad m => Stream m Int -> m Int
127manyAlt :: Monad m => Stream m Int -> m Int
111manyAlt xs = do
128manyAlt xs = do
@@ -141,17 +158,40 @@ o_1_space_serial value =
141 , benchIOSink value "splitApp" $ splitApp value
158 , benchIOSink value "splitApp" $ splitApp value
142 ]
159 ]
143
160
161+{-# INLINE sepBy1 #-}
162+sepBy1 :: Monad m => Stream m Int -> m Int
163+sepBy1 xs = do
164+ x <- PARSE_OP (parser (satisfy odd) (satisfy even)) xs
165+ return $ Prelude.length x
166+
167+ where
168+
169+ parser p sep = do
170+ x <- p
171+ fmap (x :) $ AP.many (sep >> p)
172+
144-- O(n) heap beacuse of accumulation of the list in strict IO monad?
173-- O(n) heap beacuse of accumulation of the list in strict IO monad?
145o_n_heap_serial :: Int -> [Benchmark]
174o_n_heap_serial :: Int -> [Benchmark]
146o_n_heap_serial value =
175o_n_heap_serial value =
176+ [
177+ -- accumulates the results in a list
178+ -- XXX why should this take O(n) heap, it discards the results?
147− [ benchIOSink value "sequenceA" $ sequenceA value
179+ benchIOSink value "sequence_" $ sequence_ value
148 , benchIOSink value "sequenceA_" $ sequenceA_ value
180 , benchIOSink value "sequenceA_" $ sequenceA_ value
149 , benchIOSink value "sequence" $ sequence value
181 , benchIOSink value "sequence" $ sequence value
182+ , benchIOSink value "sequenceA" $ sequenceA value
150 , benchIOSink value "manyAlt" manyAlt
183 , benchIOSink value "manyAlt" manyAlt
184+ , benchIOSink value "sepBy1" sepBy1
151 , benchIOSink value "someAlt" someAlt
185 , benchIOSink value "someAlt" someAlt
152 , benchIOSink value "choice" $ choice value
186 , benchIOSink value "choice" $ choice value
153 ]
187 ]
154
188
189+-- O(n) heap beacuse of accumulation of the list in strict IO monad?
190+o_1_space_recursive :: Int -> [Benchmark]
191+o_1_space_recursive value =
192+ [ benchIOSink value "one (recursive)" $ one value
193+ ]
194+
155-------------------------------------------------------------------------------
195-------------------------------------------------------------------------------
156-- Driver
196-- Driver
157-------------------------------------------------------------------------------
197-------------------------------------------------------------------------------
@@ -163,5 +203,6 @@ main = runWithCLIOpts defaultStreamSize allBenchmarks
163
203
164 allBenchmarks value =
204 allBenchmarks value =
165 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
205 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
206+ , bgroup (o_1_space_prefix moduleName) (o_1_space_recursive value)
166 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_serial value)
207 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_serial value)
167 ]
208 ]
core/src/Streamly/Data/Parser.hs+31
@@ -17,6 +17,37 @@
17-- (which should be the case almost always) you can just use 'fromEffect' to
17-- (which should be the case almost always) you can just use 'fromEffect' to
18-- execute the lower layer monad effects.
18-- execute the lower layer monad effects.
19--
19--
20+-- == Performance Notes
21+--
22+-- This module is designed for fusion, inline the operations in this module for
23+-- fusion to occur, avoid using these operations in recursive calls, avoid
24+-- operations like 'sequence', 'asum' on these parsers. If you need these then
25+-- use the 'ParserK' module instead.
26+--
27+-- The 'Parser' type represents a stream consumer by composing state as data
28+-- which enables stream fusion. Stream fusion generates a tight loop without
29+-- any constructor allocations between the stages, providing C like performance
30+-- for the loop. Stream fusion works when multiple functions are combined in a
31+-- pipeline statically. Therefore, the operations in this module must be
32+-- inlined and must not be used recursively to allow for stream fusion.
33+--
34+-- Using the 'Parser' type parsing operations like 'one', 'splitWith' etc.
35+-- degrade quadratically (O(n^2)) when combined many times. If you need to
36+-- combine these operations, say more than 50 times in a single loop, then you
37+-- should use the continuation style parser type 'ParserK' instead. Also, if
38+-- you need to use these operations in a recursive loop you should use
39+-- 'ParserK' instead.
40+--
41+-- The 'ParserK' type represents a stream consumer by composing function calls,
42+-- therefore, a function call overhead is incurred at each composition. It is
43+-- quite fast in general but may be a few times slower than a fused parser.
44+-- However, it allows for scalable dynamic composition especially parsers can
45+-- be used in recursive calls. Using the 'ParserK' type operations like
46+-- 'splitWith' provide linear (O(n)) performance with respect to the number of
47+-- compositions..
48+--
49+-- 'Parser' and 'ParserK' types can be interconverted.
50+--
20module Streamly.Data.Parser
51module Streamly.Data.Parser
21 (
52 (
22 -- * Parser Type
53 -- * Parser Type
core/src/Streamly/Data/Parser/ParserK.hsnew file+54
@@ -0,0 +1,54 @@
1+-- |
2+-- Module : Streamly.Data.Parser.ParserK
3+-- Copyright : (c) 2023 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : pre-release
7+-- Portability : GHC
8+--
9+-- Parsers using Continuation Passing Style (CPS). See notes in
10+-- "Streamly.Data.Parser" module to know when to use this module.
11+--
12+-- To run a 'ParserK' convert it to a 'Parser' and then run it.
13+--
14+module Streamly.Data.Parser.ParserK
15+ (
16+ -- * Parser Type
17+ ParserK
18+ , Parser
19+
20+ -- * Parsers
21+ -- ** Conversions
22+ , fromFold
23+ , fromParser
24+ , toParser
25+
26+ -- ** Without Input
27+ , fromPure
28+ , fromEffect
29+ , die
30+ )
31+
32+where
33+
34+import Streamly.Internal.Data.Fold (Fold)
35+import Streamly.Internal.Data.Parser.ParserK.Type
36+import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
37+
38+-- | Convert a 'Fold' to a 'ParserK'.
39+--
40+{-# INLINE fromFold #-}
41+fromFold :: Monad m => Fold m a b -> ParserK a m b
42+fromFold = ParserD.toParserK . ParserD.fromFold
43+
44+-- | Convert a 'Parser' to a 'ParserK'.
45+--
46+{-# INLINE fromParser #-}
47+fromParser :: Monad m => ParserD.Parser a m b -> ParserK a m b
48+fromParser = ParserD.toParserK
49+
50+-- | Convert a 'ParserK' to a 'Parser'.
51+--
52+{-# INLINE toParser #-}
53+toParser :: Monad m => ParserK a m b -> ParserD.Parser a m b
54+toParser = ParserD.fromParserK
core/src/Streamly/Internal/Data/Fold/Chunked.hs+1−1
@@ -182,7 +182,7 @@ fromParserD (ParserD.Parser step1 initial1 extract1) =
182{-# INLINE fromParser #-}
182{-# INLINE fromParser #-}
183fromParser :: forall m a b. (MonadIO m, Unbox a) =>
183fromParser :: forall m a b. (MonadIO m, Unbox a) =>
184 Parser.Parser a m b -> ChunkFold m a b
184 Parser.Parser a m b -> ChunkFold m a b
185−fromParser = fromParserD . ParserD.fromParserK
185+fromParser = fromParserD
186
186
187-- | Adapt an array stream fold.
187-- | Adapt an array stream fold.
188--
188--
core/src/Streamly/Internal/Data/Parser.hs+4−1534
@@ -1,1544 +1,14 @@
1−{-# OPTIONS_GHC -Wno-orphans #-}
2−
3-- |
1-- |
4-- Module : Streamly.Internal.Data.Parser
2-- Module : Streamly.Internal.Data.Parser
5−-- Copyright : (c) 2020 Composewell Technologies
3+-- Copyright : (c) 2019 Composewell Technologies
6-- License : BSD-3-Clause
4-- License : BSD-3-Clause
7-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
8−-- Stability : pre-release
6+-- Stability : experimental
9-- Portability : GHC
7-- Portability : GHC
10--
8--
11−-- Fast backtracking parsers with stream fusion and native streaming
12−-- capability.
13−--
14−-- 'Applicative' and 'Control.Applicative.Alternative' type class based
15−-- combinators from the
16−-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>
17−-- package can also be used with the 'Parser' type. However, there are two
18−-- important differences between @parser-combinators@ and the equivalent ones
19−-- provided in this module in terms of performance:
20−--
21−-- 1) @parser-combinators@ use plain Haskell lists to collect the results, in a
22−-- strict Monad like IO, the results are necessarily buffered before they can
23−-- be consumed. This may not perform optimally in streaming applications
24−-- processing large amounts of data. Equivalent combinators in this module can
25−-- consume the results of parsing using a 'Fold', thus providing a scalability
26−-- and a composable consumer.
27−--
28−-- 2) Several combinators in this module can be many times faster because of
29−-- stream fusion. For example, 'Streamly.Internal.Data.Parser.many' combinator
30−-- in this module is much faster than the 'Control.Applicative.many' combinator
31−-- of 'Control.Applicative.Alternative' type class.
32−--
33−-- = Errors
34−--
35−-- Failing parsers in this module throw the 'D.ParseError' exception.
36−--
37−-- = Naming
38−--
39−-- As far as possible, try that the names of the combinators in this module are
40−-- consistent with:
41−--
42−-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>
43−-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>
44−-- * <http://hackage.haskell.org/package/megaparsec megaparsec>
45−-- * <http://hackage.haskell.org/package/attoparsec attoparsec>
46−-- * <http://hackage.haskell.org/package/parsec parsec>
47−
48module Streamly.Internal.Data.Parser
9module Streamly.Internal.Data.Parser
49− (
10+ ( module Streamly.Internal.Data.Parser.ParserD
50− K.Parser (..)
51− , D.ParseError (..)
52− , D.Step (..)
53−
54− -- * Downgrade to Fold
55− , toFold
56−
57− -- First order parsers
58− -- * Accumulators
59− , fromFold
60− , fromFoldMaybe
61− , fromPure
62− , fromEffect
63− , die
64− , dieM
65−
66− -- * Map on input
67− , lmap
68− , lmapM
69− , filter
70−
71− -- * Map on output
72− , rmapM
73−
74− -- * Element parsers
75− , peek
76−
77− -- All of these can be expressed in terms of either
78− , one
79− , oneEq
80− , oneNotEq
81− , oneOf
82− , noneOf
83− , eof
84− , satisfy
85− , maybe
86− , either
87−
88− -- * Sequence parsers (tokenizers)
89− --
90− -- | Parsers chained in series, if one parser terminates the composition
91− -- terminates.
92−
93− , lookAhead
94−
95− -- ** By length
96− -- | Grab a sequence of input elements without inspecting them
97− , takeBetween
98− -- , take -- takeBetween 0 n
99− , takeEQ -- takeBetween n n
100− , takeGE -- takeBetween n maxBound
101− , takeP
102−
103− -- Grab a sequence of input elements by inspecting them
104− -- ** Exact match
105− , listEq
106− , listEqBy
107− , streamEqBy
108− , subsequenceBy
109−
110− -- ** By predicate
111− , takeWhileP
112− , takeWhile
113− -- $takeWhile
114− , takeWhile1
115− , dropWhile
116−
117− -- ** Separators
118− , takeEndBy
119− , takeEndBy_
120− , takeEndByEsc
121− -- , takeEndByEsc_
122− , takeStartBy
123− , takeStartBy_
124− , takeEitherSepBy
125− , wordBy
126− -- , wordByEsc
127−
128− -- ** By comparing
129− , groupBy
130− , groupByRolling
131− , groupByRollingEither
132−
133− -- ** Framing
134− -- , takeFramedBy
135− , takeFramedBy_
136− , takeFramedByEsc_
137− , takeFramedByGeneric
138− , wordFramedBy
139− , wordQuotedBy
140−
141− -- | Unimplemented
142− --
143− -- @
144− -- , prefixOf -- match any prefix of a given string
145− -- , suffixOf -- match any suffix of a given string
146− -- , infixOf -- match any substring of a given string
147− -- @
148−
149− -- Second order parsers (parsers using parsers)
150− -- * Binary Combinators
151−
152− -- ** Sequential Applicative
153− , splitWith
154− , split_
155−
156−{-
157− -- ** Parallel Applicatives
158− , teeWith
159− , teeWithFst
160− , teeWithMin
161− -- , teeTill -- like manyTill but parallel
162−-}
163−
164− -- ** Sequential Interleaving
165− -- Use two folds, run a primary parser, its rejected values go to the
166− -- secondary parser.
167− , deintercalate
168− -- , deintercalatePrefix
169− -- , deintercalateSuffix
170−
171− -- *** Special cases
172− -- | TODO: traditional implmentations of these may be of limited use. For
173− -- example, consider parsing lines separated by @\\r\\n@. The main parser
174− -- will have to detect and exclude the sequence @\\r\\n@ anyway so that we
175− -- can apply the "sep" parser.
176− --
177− -- We can instead implement these as special cases of deintercalate.
178− --
179− -- @
180− -- , endBy
181− -- , sepEndBy
182− -- , beginBy
183− -- , sepBeginBy
184− -- , sepAroundBy
185− -- @
186− , sepBy1
187− , sepBy
188−
189− -- ** Sequential Alternative
190− , alt
191−
192−{-
193− -- ** Parallel Alternatives
194− , shortest
195− , longest
196− -- , fastest
197−-}
198−
199− -- * N-ary Combinators
200− -- ** Sequential Collection
201− , concatSequence
202− , concatMap
203−
204− -- ** Sequential Repetition
205− , count
206− , countBetween
207−
208− , manyP
209− , many
210− , some
211− , manyTillP
212− , manyTill
213− , manyThen
214−
215− -- * Distribution
216− --
217− -- | A simple and stupid impl would be to just convert the stream to an
218− -- array and give the array reference to all consumers. The array can be
219− -- grown on demand by any consumer and truncated when nonbody needs it.
220−
221− -- ** Distribute to collection
222− -- ** Distribute to repetition
223−
224− -- ** Interleaved collection
225− -- |
226− --
227− -- 1. Round robin
228− -- 2. Priority based
229− , roundRobin
230−
231− -- ** Collection of Alternatives
232− -- | Unimplemented
233− --
234− -- @
235− -- , shortestN
236− -- , longestN
237− -- , fastestN -- first N successful in time
238− -- , choiceN -- first N successful in position
239− -- @
240− -- , choice -- first successful in position
241−
242− -- ** Repeated Alternatives
243− , retryMaxTotal
244− , retryMaxSuccessive
245− , retry
246−
247− -- * Deprecated
248− , next
249 )
11 )
250where
12where
251
13
252−import Streamly.Internal.Data.Fold.Type (Fold(..))
14+import Streamly.Internal.Data.Parser.ParserD
253−import Streamly.Internal.Data.Parser.ParserK.Type (Parser)
254−
255−import qualified Data.Foldable as Foldable
256−import qualified Streamly.Internal.Data.Fold.Type as FL
257−import qualified Streamly.Internal.Data.Parser.ParserD as D
258−import qualified Streamly.Internal.Data.Parser.ParserK.Type as K
259−
260−#ifdef USE_STREAMK
261−import Streamly.Internal.Data.StreamK (Stream)
262−import qualified Streamly.Internal.Data.StreamK as Stream
263−#else
264−import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
265−import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream
266−#endif
267−
268−import Prelude hiding
269− ( any, all, dropWhile, take, takeWhile, sequence, concatMap, maybe, either
270− , filter )
271−
272−--
273−-- $setup
274−-- >>> :m
275−-- >>> import Prelude hiding (any, all, dropWhile, take, takeWhile, sequence, concatMap, maybe, either, filter)
276−-- >>> import Control.Applicative ((<|>))
277−-- >>> import Data.Char (isSpace)
278−-- >>> import qualified Data.Maybe as Maybe
279−-- >>> import qualified Data.Foldable as Foldable
280−-- >>> import qualified Streamly.Data.Stream as Stream
281−-- >>> import qualified Streamly.Internal.Data.Stream as Stream (parse, parseMany)
282−-- >>> import qualified Streamly.Internal.Data.Fold as Fold
283−-- >>> import qualified Streamly.Internal.Data.Parser as Parser
284−
285−-------------------------------------------------------------------------------
286−-- Downgrade a parser to a Fold
287−-------------------------------------------------------------------------------
288−--
289−-- | Make a 'Fold' from a 'Parser'. The fold just throws an exception if the
290−-- parser fails or tries to backtrack.
291−--
292−-- This can be useful in combinators that accept a Fold and we know that a
293−-- Parser cannot fail or failure exception is acceptable as there is no way to
294−-- recover.
295−--
296−-- /Pre-release/
297−--
298−{-# INLINE toFold #-}
299−toFold :: Monad m => Parser a m b -> Fold m a b
300−toFold p = D.toFold $ D.fromParserK p
301−
302−-------------------------------------------------------------------------------
303−-- Upgrade folds to parses
304−-------------------------------------------------------------------------------
305−--
306−-- | Make a 'Parser' from a 'Fold'.
307−--
308−{-# INLINE fromFold #-}
309−fromFold :: Monad m => Fold m a b -> Parser a m b
310−fromFold = D.toParserK . D.fromFold
311−
312−-- | Convert a Maybe returning fold to an error returning parser. The first
313−-- argument is the error message that the parser would return when the fold
314−-- returns Nothing.
315−--
316−-- /Pre-release/
317−--
318−{-# INLINE fromFoldMaybe #-}
319−fromFoldMaybe :: Monad m => String -> Fold m a (Maybe b) -> Parser a m b
320−fromFoldMaybe err = D.toParserK . D.fromFoldMaybe err
321−
322−-------------------------------------------------------------------------------
323−-- Terminating but not failing folds
324−-------------------------------------------------------------------------------
325−--
326−-- This is the dual of stream "fromPure".
327−--
328−-- | A parser that always yields a pure value without consuming any input.
329−--
330−{-# INLINE [3] fromPure #-}
331−fromPure :: Monad m => b -> Parser a m b
332−fromPure = D.toParserK . D.fromPure
333−{-# RULES "fromPure fallback to CPS" [2]
334− forall a. D.toParserK (D.fromPure a) = K.fromPure a #-}
335−
336−-- This is the dual of stream "fromEffect".
337−--
338−-- | A parser that always yields the result of an effectful action without
339−-- consuming any input.
340−--
341−{-# INLINE fromEffect #-}
342−fromEffect :: Monad m => m b -> Parser a m b
343−fromEffect = K.fromEffect -- D.toParserK . D.fromEffect
344−
345−-- This is the dual of "nil".
346−--
347−-- | A parser that always fails with an error message without consuming
348−-- any input.
349−--
350−{-# INLINE [3] die #-}
351−die :: Monad m => String -> Parser a m b
352−die = D.toParserK . D.die
353−{-# RULES "die fallback to CPS" [2]
354− forall a. D.toParserK (D.die a) = K.die a #-}
355−
356−-- This is the dual of "nilM".
357−--
358−-- | A parser that always fails with an effectful error message and without
359−-- consuming any input.
360−--
361−-- /Pre-release/
362−--
363−{-# INLINE dieM #-}
364−dieM :: Monad m => m String -> Parser a m b
365−dieM = D.toParserK . D.dieM
366−
367−-------------------------------------------------------------------------------
368−-- Map on input
369−-------------------------------------------------------------------------------
370−
371−-- | @lmap f parser@ maps the function @f@ on the input of the parser.
372−--
373−-- >>> Stream.parse (Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)) (Stream.enumerateFromTo 1 100)
374−-- Right 338350
375−--
376−-- > lmap = Parser.lmapM return
377−--
378−{-# INLINE lmap #-}
379−lmap :: Monad m => (a -> b) -> Parser b m r -> Parser a m r
380−lmap f p = D.toParserK $ D.lmap f $ D.fromParserK p
381−
382−-- | @lmapM f parser@ maps the monadic function @f@ on the input of the parser.
383−--
384−{-# INLINE lmapM #-}
385−lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r
386−lmapM f p = D.toParserK $ D.lmapM f $ D.fromParserK p
387−
388−-- | @rmapM f parser@ maps the monadic function @f@ on the output of the parser.
389−--
390−-- >>> rmap = fmap
391−{-# INLINE rmapM #-}
392−rmapM :: Monad m => (b -> m c) -> Parser a m b -> Parser a m c
393−rmapM f p = D.toParserK $ D.rmapM f $ D.fromParserK p
394−
395−-- | Include only those elements that pass a predicate.
396−--
397−-- >>> Stream.parse (Parser.filter (> 5) (Parser.fromFold Fold.sum)) $ Stream.fromList [1..10]
398−-- Right 40
399−--
400−{-# INLINE filter #-}
401−filter :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
402−filter f p = D.toParserK $ D.filter f $ D.fromParserK p
403−
404−-------------------------------------------------------------------------------
405−-- Failing Parsers
406−-------------------------------------------------------------------------------
407−
408−-- | Peek the head element of a stream, without consuming it. Fails if it
409−-- encounters end of input.
410−--
411−-- >>> Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]
412−-- Right (1,1)
413−--
414−-- @
415−-- peek = lookAhead (satisfy True)
416−-- @
417−--
418−{-# INLINE peek #-}
419−peek :: Monad m => Parser a m a
420−peek = D.toParserK D.peek
421−
422−-- | Succeeds if we are at the end of input, fails otherwise.
423−--
424−-- >>> Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]
425−-- Right (1,())
426−--
427−{-# INLINE eof #-}
428−eof :: Monad m => Parser a m ()
429−eof = D.toParserK D.eof
430−
431−-- | Returns the next element if it passes the predicate, fails otherwise.
432−--
433−-- >>> Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]
434−-- Right 1
435−--
436−-- >>> toMaybe f x = if f x then Just x else Nothing
437−-- >>> satisfy f = Parser.maybe (toMaybe f)
438−--
439−{-# INLINE satisfy #-}
440−satisfy :: Monad m => (a -> Bool) -> Parser a m a
441−satisfy = D.toParserK . D.satisfy
442−
443−-- | Consume one element from the head of the stream. Fails if it encounters
444−-- end of input.
445−--
446−-- >>> one = Parser.satisfy $ const True
447−--
448−{-# INLINE one #-}
449−one :: Monad m => Parser a m a
450−one = satisfy $ const True
451−
452−-- Alternate names: "only", "onlyThis".
453−
454−-- | Match a specific element.
455−--
456−-- >>> oneEq x = Parser.satisfy (== x)
457−--
458−{-# INLINE oneEq #-}
459−oneEq :: (Monad m, Eq a) => a -> Parser a m a
460−oneEq x = satisfy (== x)
461−
462−-- Alternate names: "exclude", "notThis".
463−
464−-- | Match anything other than the supplied element.
465−--
466−-- >>> oneNotEq x = Parser.satisfy (/= x)
467−--
468−{-# INLINE oneNotEq #-}
469−oneNotEq :: (Monad m, Eq a) => a -> Parser a m a
470−oneNotEq x = satisfy (/= x)
471−
472−-- | Match any one of the elements in the supplied list.
473−--
474−-- >>> oneOf xs = Parser.satisfy (`Foldable.elem` xs)
475−--
476−-- When performance matters a pattern matching predicate could be more
477−-- efficient than a 'Foldable' datatype:
478−--
479−-- @
480−-- let p x =
481−-- case x of
482−-- 'a' -> True
483−-- 'e' -> True
484−-- _ -> False
485−-- in satisfy p
486−-- @
487−--
488−-- GHC may use a binary search instead of linear search in the list.
489−-- Alternatively, you can also use an array instead of list for storage and
490−-- search.
491−--
492−{-# INLINE oneOf #-}
493−oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
494−oneOf xs = satisfy (`Foldable.elem` xs)
495−
496−-- | See performance notes in 'oneOf'.
497−--
498−-- >>> noneOf xs = Parser.satisfy (`Foldable.notElem` xs)
499−--
500−{-# INLINE noneOf #-}
501−noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
502−noneOf xs = satisfy (`Foldable.notElem` xs)
503−
504−-- | Return the next element of the input. Returns 'Nothing'
505−-- on end of input. Also known as 'head'.
506−--
507−-- /Pre-release/
508−--
509−{-# DEPRECATED next "Please use \"fromFold Fold.one\" instead" #-}
510−{-# INLINE next #-}
511−next :: Monad m => Parser a m (Maybe a)
512−next = D.toParserK D.next
513−
514−-- | Map a 'Maybe' returning function on the next element in the stream. The
515−-- parser fails if the function returns 'Nothing' otherwise returns the 'Just'
516−-- value.
517−--
518−-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
519−-- >>> maybe f = Parser.either (toEither . f)
520−--
521−-- >>> maybe f = Parser.fromFoldMaybe "maybe: predicate failed" (Fold.maybe f)
522−--
523−-- /Pre-release/
524−--
525−{-# INLINE maybe #-}
526−maybe :: Monad m => (a -> Maybe b) -> Parser a m b
527−maybe = D.toParserK . D.maybe
528−
529−-- | Map an 'Either' returning function on the next element in the stream. If
530−-- the function returns 'Left err', the parser fails with the error message
531−-- @err@ otherwise returns the 'Right' value.
532−--
533−-- /Pre-release/
534−--
535−{-# INLINE either #-}
536−either :: Monad m => (a -> Either String b) -> Parser a m b
537−either = D.toParserK . D.either
538−
539−-------------------------------------------------------------------------------
540−-- Taking elements
541−-------------------------------------------------------------------------------
542−
543−-- | @takeBetween m n@ takes a minimum of @m@ and a maximum of @n@ input
544−-- elements and folds them using the supplied fold.
545−--
546−-- Stops after @n@ elements.
547−-- Fails if the stream ends before @m@ elements could be taken.
548−--
549−-- Examples: -
550−--
551−-- @
552−-- >>> :{
553−-- takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)
554−-- where prsr = Parser.takeBetween low high Fold.toList
555−-- :}
556−--
557−-- @
558−--
559−-- >>> takeBetween' 2 4 [1, 2, 3, 4, 5]
560−-- Right [1,2,3,4]
561−--
562−-- >>> takeBetween' 2 4 [1, 2]
563−-- Right [1,2]
564−--
565−-- >>> takeBetween' 2 4 [1]
566−-- Left (ParseError "takeBetween: Expecting alteast 2 elements, got 1")
567−--
568−-- >>> takeBetween' 0 0 [1, 2]
569−-- Right []
570−--
571−-- >>> takeBetween' 0 1 []
572−-- Right []
573−--
574−-- @takeBetween@ is the most general take operation, other take operations can
575−-- be defined in terms of takeBetween. For example:
576−--
577−-- >>> take n = Parser.takeBetween 0 n
578−-- >>> takeEQ n = Parser.takeBetween n n
579−-- >>> takeGE n = Parser.takeBetween n maxBound
580−--
581−-- /Pre-release/
582−--
583−{-# INLINE takeBetween #-}
584−takeBetween :: Monad m =>
585− Int -> Int -> Fold m a b -> Parser a m b
586−takeBetween m n = D.toParserK . D.takeBetween m n
587−
588−-- | Stops after taking exactly @n@ input elements.
589−--
590−-- * Stops - after consuming @n@ elements.
591−-- * Fails - if the stream or the collecting fold ends before it can collect
592−-- exactly @n@ elements.
593−--
594−-- >>> Stream.parse (Parser.takeEQ 4 Fold.toList) $ Stream.fromList [1,0,1]
595−-- Left (ParseError "takeEQ: Expecting exactly 4 elements, input terminated on 3")
596−--
597−{-# INLINE takeEQ #-}
598−takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b
599−takeEQ n = D.toParserK . D.takeEQ n
600−
601−-- | Take at least @n@ input elements, but can collect more.
602−--
603−-- * Stops - when the collecting fold stops.
604−-- * Fails - if the stream or the collecting fold ends before producing @n@
605−-- elements.
606−--
607−-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1]
608−-- Left (ParseError "takeGE: Expecting at least 4 elements, input terminated on 3")
609−--
610−-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]
611−-- Right [1,0,1,0,1]
612−--
613−-- /Pre-release/
614−--
615−{-# INLINE takeGE #-}
616−takeGE :: Monad m => Int -> Fold m a b -> Parser a m b
617−takeGE n = D.toParserK . D.takeGE n
618−
619−-------------------------------------------------------------------------------
620−-- Take until a condition
621−-------------------------------------------------------------------------------
622−
623−-- $takeWhile
624−-- Note: This is called @takeWhileP@ and @munch@ in some parser libraries.
625−
626−-- XXX We should perhaps use only takeWhileP and rename it to takeWhile.
627−--
628−-- | Like 'takeWhile' but uses a 'Parser' instead of a 'Fold' to collect the
629−-- input. The combinator stops when the condition fails or if the collecting
630−-- parser stops.
631−--
632−-- Other interesting parsers can be implemented in terms of this parser:
633−--
634−-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
635−-- >>> takeWhileBetween cond m n p = Parser.takeWhileP cond (Parser.takeBetween m n p)
636−--
637−-- Stops: when the condition fails or the collecting parser stops.
638−-- Fails: when the collecting parser fails.
639−--
640−-- /Pre-release/
641−--
642−{-# INLINE takeWhileP #-}
643−takeWhileP :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
644−takeWhileP cond p = D.toParserK $ D.takeWhileP cond (D.fromParserK p)
645−
646−-- | Collect stream elements until an element fails the predicate. The element
647−-- on which the predicate fails is returned back to the input stream.
648−--
649−-- * Stops - when the predicate fails or the collecting fold stops.
650−-- * Fails - never.
651−--
652−-- >>> Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]
653−-- Right [0,0]
654−--
655−-- >>> takeWhile cond f = Parser.takeWhileP cond (Parser.fromFold f)
656−--
657−-- We can implement a @breakOn@ using 'takeWhile':
658−--
659−-- @
660−-- breakOn p = takeWhile (not p)
661−-- @
662−--
663−{-# INLINE takeWhile #-}
664−takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
665−takeWhile cond = D.toParserK . D.takeWhile cond
666−-- takeWhile cond f = takeWhileP cond (fromFold f)
667−
668−-- | Like 'takeWhile' but takes at least one element otherwise fails.
669−--
670−-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
671−--
672−{-# INLINE takeWhile1 #-}
673−takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
674−takeWhile1 cond = D.toParserK . D.takeWhile1 cond
675−-- takeWhile1 cond f = takeWhileP cond (takeBetween 1 maxBound f)
676−
677−-- | Drain the input as long as the predicate succeeds, running the effects and
678−-- discarding the results.
679−--
680−-- This is also called @skipWhile@ in some parsing libraries.
681−--
682−-- >>> dropWhile p = Parser.takeWhile p Fold.drain
683−--
684−{-# INLINE dropWhile #-}
685−dropWhile :: Monad m => (a -> Bool) -> Parser a m ()
686−dropWhile p = takeWhile p FL.drain
687−
688−-------------------------------------------------------------------------------
689−-- Separators
690−-------------------------------------------------------------------------------
691−
692−-- XXX We can remove Maybe from esc
693−{-# INLINE takeFramedByGeneric #-}
694−takeFramedByGeneric :: Monad m =>
695− Maybe (a -> Bool)
696− -> Maybe (a -> Bool)
697− -> Maybe (a -> Bool)
698− -> Fold m a b
699− -> Parser a m b
700−takeFramedByGeneric esc begin end f =
701− D.toParserK $ D.takeFramedByGeneric esc begin end f
702−
703−-- | @takeEndBy cond parser@ parses a token that ends by a separator chosen by
704−-- the supplied predicate. The separator is also taken with the token.
705−--
706−-- This can be combined with other parsers to implement other interesting
707−-- parsers as follows:
708−--
709−-- >>> takeEndByLE cond n p = Parser.takeEndBy cond (Parser.fromFold $ Fold.take n p)
710−-- >>> takeEndByBetween cond m n p = Parser.takeEndBy cond (Parser.takeBetween m n p)
711−--
712−-- >>> takeEndBy = Parser.takeEndByEsc (const False)
713−--
714−-- See also "Streamly.Data.Fold.takeEndBy". Unlike the fold, the collecting
715−-- parser in the takeEndBy parser can decide whether to fail or not if the
716−-- stream does not end with separator.
717−--
718−-- /Pre-release/
719−--
720−{-# INLINE takeEndBy #-}
721−takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
722−takeEndBy cond = D.toParserK . D.takeEndBy cond . D.fromParserK
723−-- takeEndBy = takeEndByEsc (const False)
724−
725−-- | Like 'takeEndBy' but the separator is dropped.
726−--
727−-- See also "Streamly.Data.Fold.takeEndBy_".
728−--
729−-- /Pre-release/
730−--
731−{-# INLINE takeEndBy_ #-}
732−takeEndBy_ :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
733−takeEndBy_ cond = D.toParserK . D.takeEndBy_ cond . D.fromParserK
734−{-
735−takeEndBy_ isEnd p =
736− takeFramedByGeneric Nothing Nothing (Just isEnd) (toFold p)
737−-}
738−
739−-- | Take either the separator or the token. Separator is a Left value and
740−-- token is Right value.
741−--
742−-- /Unimplemented/
743−{-# INLINE takeEitherSepBy #-}
744−takeEitherSepBy :: -- Monad m =>
745− (a -> Bool) -> Fold m (Either a b) c -> Parser a m c
746−takeEitherSepBy _cond = undefined -- D.toParserK . D.takeEitherSepBy cond
747−
748−-- | Parse a token that starts with an element chosen by the predicate. The
749−-- parser fails if the input does not start with the selected element.
750−--
751−-- * Stops - when the predicate succeeds in non-leading position.
752−-- * Fails - when the predicate fails in the leading position.
753−--
754−-- >>> splitWithPrefix p f = Stream.parseMany (Parser.takeStartBy p f)
755−--
756−-- Examples: -
757−--
758−-- >>> p = Parser.takeStartBy (== ',') Fold.toList
759−-- >>> leadingComma = Stream.parse p . Stream.fromList
760−-- >>> leadingComma "a,b"
761−-- Left (ParseError "takeStartBy: missing frame start")
762−-- ...
763−-- >>> leadingComma ",,"
764−-- Right ","
765−-- >>> leadingComma ",a,b"
766−-- Right ",a"
767−-- >>> leadingComma ""
768−-- Right ""
769−--
770−-- /Pre-release/
771−--
772−{-# INLINE takeStartBy #-}
773−takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
774−takeStartBy cond = D.toParserK . D.takeStartBy cond
775−
776−-- | Like 'takeStartBy' but drops the separator.
777−--
778−-- >>> takeStartBy_ isBegin = Parser.takeFramedByGeneric Nothing (Just isBegin) Nothing
779−--
780−{-# INLINE takeStartBy_ #-}
781−takeStartBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
782−takeStartBy_ isBegin = takeFramedByGeneric Nothing (Just isBegin) Nothing
783−
784−-------------------------------------------------------------------------------
785−-- Quoting and Escaping
786−-------------------------------------------------------------------------------
787−
788−-- | Like 'takeEndBy' but the separator elements can be escaped using an
789−-- escape char determined by the first predicate. The escape characters are
790−-- removed.
791−--
792−-- /pre-release/
793−{-# INLINE takeEndByEsc #-}
794−takeEndByEsc :: Monad m =>
795− (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b
796−takeEndByEsc isEsc isEnd p =
797− D.toParserK $ D.takeEndByEsc isEsc isEnd (D.fromParserK p)
798−
799−-- | @takeFramedByEsc_ isEsc isBegin isEnd fold@ parses a token framed using a
800−-- begin and end predicate, and an escape character. The frame begin and end
801−-- characters lose their special meaning if preceded by the escape character.
802−--
803−-- Nested frames are allowed if begin and end markers are different, nested
804−-- frames must be balanced unless escaped, nested frame markers are emitted as
805−-- it is.
806−--
807−-- For example,
808−--
809−-- >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList
810−-- >>> Stream.parse p $ Stream.fromList "{hello}"
811−-- Right "hello"
812−-- >>> Stream.parse p $ Stream.fromList "{hello {world}}"
813−-- Right "hello {world}"
814−-- >>> Stream.parse p $ Stream.fromList "{hello \\{world}"
815−-- Right "hello {world"
816−-- >>> Stream.parse p $ Stream.fromList "{hello {world}"
817−-- Left (ParseError "takeFramedByEsc_: missing frame end")
818−--
819−-- /Pre-release/
820−{-# INLINE takeFramedByEsc_ #-}
821−takeFramedByEsc_ :: Monad m =>
822− (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
823−takeFramedByEsc_ isEsc isBegin isEnd f =
824− D.toParserK $ D.takeFramedByEsc_ isEsc isBegin isEnd f
825−-- takeEndByEsc_ isEsc isEnd p =
826−-- takeFramedByGeneric (Just isEsc) Nothing (Just isEnd) (toFold p)
827−
828−-- | @takeFramedBy_ isBegin isEnd fold@ parses a token framed by a begin and an
829−-- end predicate.
830−--
831−-- >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)
832−--
833−{-# INLINE takeFramedBy_ #-}
834−takeFramedBy_ :: Monad m =>
835− (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
836−takeFramedBy_ isBegin isEnd f = D.toParserK $ D.takeFramedBy_ isBegin isEnd f
837−-- takeFramedBy_ isBegin isEnd =
838−-- takeFramedByGeneric (Just (const False)) (Just isBegin) (Just isEnd)
839−
840−-------------------------------------------------------------------------------
841−-- Grouping and words
842−-------------------------------------------------------------------------------
843−
844−-- Note we can also get words using something like:
845−-- sepBy FL.toList (takeWhile (not . p) Fold.toList) (dropWhile p)
846−--
847−-- But that won't be as efficient and ergonomic.
848−--
849−-- | Like 'splitOn' but strips leading, trailing, and repeated separators.
850−-- Therefore, @".a..b."@ having '.' as the separator would be parsed as
851−-- @["a","b"]@. In other words, its like parsing words from whitespace
852−-- separated text.
853−--
854−-- * Stops - when it finds a word separator after a non-word element
855−-- * Fails - never.
856−--
857−-- >>> wordBy = Parser.wordFramedBy (const False) (const False) (const False)
858−--
859−-- @
860−-- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)
861−-- @
862−--
863−{-# INLINE wordBy #-}
864−wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
865−wordBy f = D.toParserK . D.wordBy f
866−
867−-- | Like 'wordBy' but treats anything inside a pair of quotes as a single
868−-- word, the quotes can be escaped by an escape character. Recursive quotes
869−-- are possible if quote begin and end characters are different, quotes must be
870−-- balanced. Outermost quotes are stripped.
871−--
872−-- >>> braces = Parser.wordFramedBy (== '\\') (== '{') (== '}') isSpace Fold.toList
873−-- >>> Stream.parse braces $ Stream.fromList "{ab} cd"
874−-- Right "ab"
875−-- >>> Stream.parse braces $ Stream.fromList "{ab}{cd}"
876−-- Right "abcd"
877−-- >>> Stream.parse braces $ Stream.fromList "a{b} cd"
878−-- Right "ab"
879−-- >>> Stream.parse braces $ Stream.fromList "a{{b}} cd"
880−-- Right "a{b}"
881−--
882−-- >>> quotes = Parser.wordFramedBy (== '\\') (== '"') (== '"') isSpace Fold.toList
883−-- >>> Stream.parse quotes $ Stream.fromList "\"a\"\"b\""
884−-- Right "ab"
885−--
886−{-# INLINE wordFramedBy #-}
887−wordFramedBy :: Monad m =>
888− (a -> Bool) -- ^ Escape
889− -> (a -> Bool) -- ^ left quote
890− -> (a -> Bool) -- ^ right quote
891− -> (a -> Bool) -- ^ word seperator
892− -> Fold m a b
893− -> Parser a m b
894−wordFramedBy isEsc isBegin isEnd isSpc =
895− D.toParserK . D.wordFramedBy isEsc isBegin isEnd isSpc
896−
897−-- | Like 'wordFramedBy' but the closing quote is determined by the opening
898−-- quote. The first quote begin starts a quote that is closed by its
899−-- corresponding closing quote.
900−--
901−-- 'wordFramedBy' and 'wordQuotedBy' both allow multiple quote characters based
902−-- on the predicates but 'wordQuotedBy' always fixes the quote at the first
903−-- occurrence and then it is closed only by the corresponding closing quote.
904−-- Therefore, other quoting characters can be embedded inside it as normal
905−-- characters. On the other hand, 'wordFramedBy' would close the quote as soon
906−-- as it encounters any of the closing quotes.
907−--
908−-- >>> q = (`elem` ['"', '\''])
909−-- >>> p kQ = Parser.wordQuotedBy kQ (== '\\') q q id isSpace Fold.toList
910−--
911−-- >>> Stream.parse (p False) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
912−-- Right "ab'c;d\"ef"
913−--
914−-- >>> Stream.parse (p True) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
915−-- Right "a\"b'c\";'d\"e'f"
916−--
917−{-# INLINE wordQuotedBy #-}
918−wordQuotedBy :: (Monad m, Eq a) =>
919− Bool -- ^ keep the quotes in the output
920− -> (a -> Bool) -- ^ Escape
921− -> (a -> Bool) -- ^ left quote
922− -> (a -> Bool) -- ^ right quote
923− -> (a -> a) -- ^ get right quote from left quote
924− -> (a -> Bool) -- ^ word seperator
925− -> Fold m a b
926− -> Parser a m b
927−wordQuotedBy keepQuotes isEsc isBegin isEnd toRight isSpc =
928− D.toParserK . D.wordQuotedBy keepQuotes isEsc isBegin isEnd toRight isSpc
929−
930−-- | Given an input stream @[a,b,c,...]@ and a comparison function @cmp@, the
931−-- parser assigns the element @a@ to the first group, then if @a \`cmp` b@ is
932−-- 'True' @b@ is also assigned to the same group. If @a \`cmp` c@ is 'True'
933−-- then @c@ is also assigned to the same group and so on. When the comparison
934−-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
935−-- the result of the fold is the result of the parser.
936−--
937−-- * Stops - when the comparison fails.
938−-- * Fails - never.
939−--
940−-- >>> :{
941−-- runGroupsBy eq =
942−-- Stream.fold Fold.toList
943−-- . Stream.parseMany (Parser.groupBy eq Fold.toList)
944−-- . Stream.fromList
945−-- :}
946−--
947−-- >>> runGroupsBy (<) []
948−-- []
949−--
950−-- >>> runGroupsBy (<) [1]
951−-- [Right [1]]
952−--
953−-- >>> runGroupsBy (<) [3, 5, 4, 1, 2, 0]
954−-- [Right [3,5,4],Right [1,2],Right [0]]
955−--
956−{-# INLINE groupBy #-}
957−groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
958−groupBy eq = D.toParserK . D.groupBy eq
959−
960−-- | Unlike 'groupBy' this combinator performs a rolling comparison of two
961−-- successive elements in the input stream. Assuming the input stream
962−-- is @[a,b,c,...]@ and the comparison function is @cmp@, the parser
963−-- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is
964−-- 'True' @b@ is also assigned to the same group. If @b \`cmp` c@ is 'True'
965−-- then @c@ is also assigned to the same group and so on. When the comparison
966−-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
967−-- the result of the fold is the result of the parser.
968−--
969−-- * Stops - when the comparison fails.
970−-- * Fails - never.
971−--
972−-- >>> :{
973−-- runGroupsByRolling eq =
974−-- Stream.fold Fold.toList
975−-- . Stream.parseMany (Parser.groupByRolling eq Fold.toList)
976−-- . Stream.fromList
977−-- :}
978−--
979−-- >>> runGroupsByRolling (<) []
980−-- []
981−--
982−-- >>> runGroupsByRolling (<) [1]
983−-- [Right [1]]
984−--
985−-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]
986−-- [Right [3,5],Right [4],Right [1,2],Right [0]]
987−--
988−-- /Pre-release/
989−--
990−{-# INLINE groupByRolling #-}
991−groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
992−groupByRolling eq = D.toParserK . D.groupByRolling eq
993−
994−-- | Like 'groupByRolling', but if the predicate is 'True' then collects using
995−-- the first fold as long as the predicate holds 'True', if the predicate is
996−-- 'False' collects using the second fold as long as it remains 'False'.
997−-- Returns 'Left' for the first case and 'Right' for the second case.
998−--
999−-- For example, if we want to detect sorted sequences in a stream, both
1000−-- ascending and descending cases we can use 'groupByRollingEither (<=)
1001−-- Fold.toList Fold.toList'.
1002−--
1003−-- /Pre-release/
1004−{-# INLINE groupByRollingEither #-}
1005−groupByRollingEither :: Monad m =>
1006− (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
1007−groupByRollingEither eq f1 = D.toParserK . D.groupByRollingEither eq f1
1008−
1009−-- | Like 'listEqBy' but uses a stream instead of a list and does not return
1010−-- the stream.
1011−--
1012−-- See also: "Streamly.Data.Stream.streamEqBy"
1013−--
1014−{-# INLINE streamEqBy #-}
1015−streamEqBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Parser a m ()
1016−streamEqBy cmp = D.toParserK . D.streamEqBy cmp
1017−
1018−-- | Match the given sequence of elements using the given comparison function.
1019−-- Returns the original sequence if successful.
1020−--
1021−-- Definition:
1022−--
1023−-- >>> listEqBy cmp xs = Parser.streamEqBy cmp (Stream.fromList xs) *> Parser.fromPure xs
1024−--
1025−-- Examples:
1026−--
1027−-- >>> Stream.parse (Parser.listEqBy (==) "string") $ Stream.fromList "string"
1028−-- Right "string"
1029−--
1030−-- >>> Stream.parse (Parser.listEqBy (==) "mismatch") $ Stream.fromList "match"
1031−-- Left (ParseError "streamEqBy: mismtach occurred")
1032−--
1033−{-# INLINE listEqBy #-}
1034−listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]
1035−-- listEqBy cmp xs = D.toParserK (D.listEqBy cmp xs)
1036−listEqBy cmp xs = streamEqBy cmp (Stream.fromList xs) *> fromPure xs
1037−
1038−-- Rename to "list".
1039−-- | Match the input sequence with the supplied list and return it if
1040−-- successful.
1041−--
1042−-- >>> listEq = Parser.listEqBy (==)
1043−--
1044−{-# INLINE listEq #-}
1045−listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]
1046−listEq = listEqBy (==)
1047−
1048−-- | Match if the input stream is a subsequence of the argument stream i.e. all
1049−-- the elements of the input stream occur, in order, in the argument stream.
1050−-- The elements do not have to occur consecutively. A sequence is considered a
1051−-- subsequence of itself.
1052−{-# INLINE subsequenceBy #-}
1053−subsequenceBy :: -- Monad m =>
1054− (a -> a -> Bool) -> Stream m a -> Parser a m ()
1055−subsequenceBy = undefined
1056−
1057−{-
1058−-- Should go in Data.Parser.Regex in streamly package so that it can depend on
1059−-- regex backends.
1060−{-# INLINE regexPosix #-}
1061−regexPosix :: -- Monad m =>
1062− Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
1063−regexPosix = undefined
1064−
1065−{-# INLINE regexPCRE #-}
1066−regexPCRE :: -- Monad m =>
1067− Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
1068−regexPCRE = undefined
1069−-}
1070−
1071−-------------------------------------------------------------------------------
1072−-- nested parsers
1073−-------------------------------------------------------------------------------
1074−
1075−-- | Sequential parser application. Apply two parsers sequentially to an input
1076−-- stream. The input is provided to the first parser, when it is done the
1077−-- remaining input is provided to the second parser. If both the parsers
1078−-- succeed their outputs are combined using the supplied function. The
1079−-- operation fails if any of the parsers fail.
1080−--
1081−-- Note: This is a parsing dual of appending streams using
1082−-- 'Streamly.Data.Stream.append', it splits the streams using two parsers and zips
1083−-- the results.
1084−--
1085−-- This implementation is strict in the second argument, therefore, the
1086−-- following will fail:
1087−--
1088−-- >>> Stream.parse (Parser.splitWith const (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]
1089−-- *** Exception: Prelude.undefined
1090−-- ...
1091−--
1092−-- Compare with 'Applicative' instance method '<*>'. This implementation allows
1093−-- stream fusion but has quadratic complexity. This can fuse with other
1094−-- operations and can be faster than 'Applicative' instance for small number
1095−-- (less than 8) of compositions.
1096−--
1097−-- Many combinators can be expressed using @splitWith@ and other parser
1098−-- primitives. Some common idioms are described below,
1099−--
1100−-- @
1101−-- span :: (a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
1102−-- span pred f1 f2 = splitWith (,) ('takeWhile' pred f1) ('fromFold' f2)
1103−-- @
1104−--
1105−-- @
1106−-- spanBy :: (a -> a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
1107−-- spanBy eq f1 f2 = splitWith (,) ('groupBy' eq f1) ('fromFold' f2)
1108−-- @
1109−--
1110−-- @
1111−-- spanByRolling :: (a -> a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
1112−-- spanByRolling eq f1 f2 = splitWith (,) ('groupByRolling' eq f1) ('fromFold' f2)
1113−-- @
1114−--
1115−-- /Pre-release/
1116−--
1117−{-# INLINE splitWith #-}
1118−splitWith :: Monad m
1119− => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
1120−splitWith f p1 p2 =
1121− D.toParserK $ D.splitWith f (D.fromParserK p1) (D.fromParserK p2)
1122−
1123−-- | Sequential parser application ignoring the output of the first parser.
1124−-- Apply two parsers sequentially to an input stream. The input is provided to
1125−-- the first parser, when it is done the remaining input is provided to the
1126−-- second parser. The output of the parser is the output of the second parser.
1127−-- The operation fails if any of the parsers fail.
1128−--
1129−-- This implementation is strict in the second argument, therefore, the
1130−-- following will fail:
1131−--
1132−-- >>> Stream.parse (Parser.split_ (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]
1133−-- *** Exception: Prelude.undefined
1134−-- ...
1135−--
1136−-- Compare with 'Applicative' instance method '*>'. This implementation allows
1137−-- stream fusion but has quadratic complexity. This can fuse with other
1138−-- operations, and can be faster than 'Applicative' instance for small
1139−-- number (less than 8) of compositions.
1140−--
1141−-- /Pre-release/
1142−--
1143−{-# INLINE split_ #-}
1144−split_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b
1145−split_ p1 p2 = D.toParserK $ D.split_ (D.fromParserK p1) (D.fromParserK p2)
1146−
1147−{-
1148−-- | @teeWith f p1 p2@ distributes its input to both @p1@ and @p2@ until both
1149−-- of them succeed or anyone of them fails and combines their output using @f@.
1150−-- The parser succeeds if both the parsers succeed.
1151−--
1152−-- /Pre-release/
1153−--
1154−{-# INLINE teeWith #-}
1155−teeWith :: Monad m
1156− => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
1157−teeWith f p1 p2 =
1158− D.toParserK $ D.teeWith f (D.fromParserK p1) (D.fromParserK p2)
1159−
1160−-- | Like 'teeWith' but ends parsing and zips the results, if available,
1161−-- whenever the first parser ends.
1162−--
1163−-- /Pre-release/
1164−--
1165−{-# INLINE teeWithFst #-}
1166−teeWithFst :: Monad m
1167− => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
1168−teeWithFst f p1 p2 =
1169− D.toParserK $ D.teeWithFst f (D.fromParserK p1) (D.fromParserK p2)
1170−
1171−-- | Like 'teeWith' but ends parsing and zips the results, if available,
1172−-- whenever any of the parsers ends or fails.
1173−--
1174−-- /Unimplemented/
1175−--
1176−{-# INLINE teeWithMin #-}
1177−teeWithMin :: Monad m
1178− => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
1179−teeWithMin f p1 p2 =
1180− D.toParserK $ D.teeWithMin f (D.fromParserK p1) (D.fromParserK p2)
1181−-}
1182−
1183−-- | Sequential alternative. Apply the input to the first parser and return the
1184−-- result if the parser succeeds. If the first parser fails then backtrack and
1185−-- apply the same input to the second parser and return the result.
1186−--
1187−-- Note: This implementation is not lazy in the second argument. The following
1188−-- will fail:
1189−--
1190−-- >>> Stream.parse (Parser.satisfy (> 0) `Parser.alt` undefined) $ Stream.fromList [1..10]
1191−-- Right 1
1192−--
1193−-- Compare with 'Alternative' instance method '<|>'. This implementation allows
1194−-- stream fusion but has quadratic complexity. This can fuse with other
1195−-- operations and can be much faster than 'Alternative' instance for small
1196−-- number (less than 8) of alternatives.
1197−--
1198−-- /Pre-release/
1199−--
1200−{-# INLINE alt #-}
1201−alt :: Monad m => Parser x m a -> Parser x m a -> Parser x m a
1202−alt p1 p2 = D.toParserK $ D.alt (D.fromParserK p1) (D.fromParserK p2)
1203−
1204−{-
1205−-- | Shortest alternative. Apply both parsers in parallel but choose the result
1206−-- from the one which consumed least input i.e. take the shortest succeeding
1207−-- parse.
1208−--
1209−-- /Pre-release/
1210−--
1211−{-# INLINE shortest #-}
1212−shortest :: Monad m
1213− => Parser x m a -> Parser x m a -> Parser x m a
1214−shortest p1 p2 = D.toParserK $ D.shortest (D.fromParserK p1) (D.fromParserK p2)
1215−
1216−-- | Longest alternative. Apply both parsers in parallel but choose the result
1217−-- from the one which consumed more input i.e. take the longest succeeding
1218−-- parse.
1219−--
1220−-- /Pre-release/
1221−--
1222−{-# INLINE longest #-}
1223−longest :: Monad m
1224− => Parser x m a -> Parser x m a -> Parser x m a
1225−longest p1 p2 = D.toParserK $ D.longest (D.fromParserK p1) (D.fromParserK p2)
1226−-}
1227−
1228−-- | Run a parser without consuming the input.
1229−--
1230−{-# INLINE lookAhead #-}
1231−lookAhead :: Monad m => Parser a m b -> Parser a m b
1232−lookAhead p = D.toParserK $ D.lookAhead $ D.fromParserK p
1233−
1234−-- | Takes at-most @n@ input elements.
1235−--
1236−-- * Stops - when the collecting parser stops.
1237−-- * Fails - when the collecting parser fails.
1238−--
1239−-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
1240−-- Right [1,2]
1241−--
1242−-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 5 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
1243−-- Left (ParseError "takeEQ: Expecting exactly 5 elements, input terminated on 4")
1244−--
1245−-- /Internal/
1246−{-# INLINE takeP #-}
1247−takeP :: Monad m => Int -> Parser a m b -> Parser a m b
1248−takeP i p = D.toParserK $ D.takeP i $ D.fromParserK p
1249−
1250−-------------------------------------------------------------------------------
1251−-- Sequential Collection
1252−-------------------------------------------------------------------------------
1253−--
1254−-- | @concatSequence f p@ collects sequential parses of parsers in a
1255−-- serial stream @p@ using the fold @f@. Fails if the input ends or any
1256−-- of the parsers fail.
1257−--
1258−-- An even more efficient implementation can use ParserD type Parser in
1259−-- the stream.
1260−--
1261−-- /Pre-release/
1262−--
1263−{-# INLINE concatSequence #-}
1264−concatSequence ::
1265− Monad m =>
1266− Fold m b c -> Stream m (Parser a m b) -> Parser a m c
1267−concatSequence f p =
1268− let sp = fmap D.fromParserK p
1269− in D.toParserK $ D.sequence sp f
1270−
1271−-- | Map a 'Parser' returning function on the result of a 'Parser'.
1272−--
1273−-- Compare with 'Monad' instance method '>>='. This implementation allows
1274−-- stream fusion but has quadratic complexity. This can fuse with other
1275−-- operations and can be much faster than 'Monad' instance for small number
1276−-- (less than 8) of compositions.
1277−--
1278−-- /Pre-release/
1279−--
1280−{-# INLINE concatMap #-}
1281−concatMap :: Monad m
1282− => (b -> Parser a m c) -> Parser a m b -> Parser a m c
1283−concatMap f p = D.toParserK $ D.concatMap (D.fromParserK . f) (D.fromParserK p)
1284−
1285−{-
1286−-------------------------------------------------------------------------------
1287−-- Alternative Collection
1288−-------------------------------------------------------------------------------
1289−--
1290−-- | @choice parsers@ applies the @parsers@ in order and returns the first
1291−-- successful parse.
1292−--
1293−-- This is same as 'asum' but more efficient.
1294−--
1295−-- /Broken/
1296−--
1297−{-# INLINE choice #-}
1298−choice ::
1299− (Functor t, Foldable t, Monad m) => t (Parser a m b) -> Parser a m b
1300−choice ps = D.toParserK $ D.choice $ D.fromParserK <$> ps
1301−-}
1302−
1303−-------------------------------------------------------------------------------
1304−-- Sequential Repetition
1305−-------------------------------------------------------------------------------
1306−
1307−-- | Like 'many' but uses a 'Parser' instead of a 'Fold' to collect the
1308−-- results. Parsing stops or fails if the collecting parser stops or fails.
1309−--
1310−-- /Unimplemented/
1311−--
1312−{-# INLINE manyP #-}
1313−manyP :: -- Monad m =>
1314− Parser a m b -> Parser b m c -> Parser a m c
1315−manyP _p _f = undefined -- D.toParserK $ D.manyP (D.fromParserK p) f
1316−
1317−-- | Collect zero or more parses. Apply the supplied parser repeatedly on the
1318−-- input stream and push the parse results to a downstream fold.
1319−--
1320−-- Stops: when the downstream fold stops or the parser fails.
1321−-- Fails: never, produces zero or more results.
1322−--
1323−-- >>> many = Parser.countBetween 0 maxBound
1324−--
1325−-- Compare with 'Control.Applicative.many'.
1326−--
1327−{-# INLINE many #-}
1328−many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
1329−many p f = D.toParserK $ D.many (D.fromParserK p) f
1330−
1331−-- Note: many1 would perhaps be a better name for this and consistent with
1332−-- other names like takeWhile1. But we retain the name "some" for
1333−-- compatibility.
1334−--
1335−-- | Collect one or more parses. Apply the supplied parser repeatedly on the
1336−-- input stream and push the parse results to a downstream fold.
1337−--
1338−-- Stops: when the downstream fold stops or the parser fails.
1339−-- Fails: if it stops without producing a single result.
1340−--
1341−-- >>> some p f = Parser.manyP p (Parser.takeGE 1 f)
1342−-- >>> some = Parser.countBetween 1 maxBound
1343−--
1344−-- Compare with 'Control.Applicative.some'.
1345−--
1346−{-# INLINE some #-}
1347−some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
1348−some p f = D.toParserK $ D.some (D.fromParserK p) f
1349−-- some p f = manyP p (takeGE 1 f)
1350−-- some = countBetween 1 maxBound
1351−
1352−-- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of
1353−-- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if
1354−-- the input ends or the parser fails before @m@ results are collected.
1355−--
1356−-- >>> countBetween m n p f = Parser.manyP p (Parser.takeBetween m n f)
1357−--
1358−-- /Unimplemented/
1359−--
1360−{-# INLINE countBetween #-}
1361−countBetween :: -- Monad m =>
1362− Int -> Int -> Parser a m b -> Fold m b c -> Parser a m c
1363−countBetween _m _n _p = undefined
1364−-- countBetween m n p f = manyP p (takeBetween m n f)
1365−
1366−-- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using
1367−-- the fold @f@. Fails if the input ends or the parser fails before @n@
1368−-- results are collected.
1369−--
1370−-- >>> count n = Parser.countBetween n n
1371−-- >>> count n p f = Parser.manyP p (Parser.takeEQ n f)
1372−--
1373−-- /Unimplemented/
1374−--
1375−{-# INLINE count #-}
1376−count :: -- Monad m =>
1377− Int -> Parser a m b -> Fold m b c -> Parser a m c
1378−count n = countBetween n n
1379−-- count n p f = manyP p (takeEQ n f)
1380−
1381−-- | Like 'manyTill' but uses a 'Parser' to collect the results instead of a
1382−-- 'Fold'. Parsing stops or fails if the collecting parser stops or fails.
1383−--
1384−-- We can implemnent parsers like the following using 'manyTillP':
1385−--
1386−-- @
1387−-- countBetweenTill m n f p = manyTillP (takeBetween m n f) p
1388−-- @
1389−--
1390−-- /Unimplemented/
1391−--
1392−{-# INLINE manyTillP #-}
1393−manyTillP :: -- Monad m =>
1394− Parser a m b -> Parser a m x -> Parser b m c -> Parser a m c
1395−manyTillP _p1 _p2 _f = undefined
1396− -- D.toParserK $ D.manyTillP (D.fromParserK p1) (D.fromParserK p2) f
1397−
1398−-- | @manyTill chunking test f@ tries the parser @test@ on the input, if @test@
1399−-- fails it backtracks and tries @chunking@, after @chunking@ succeeds @test@ is
1400−-- tried again and so on. The parser stops when @test@ succeeds. The output of
1401−-- @test@ is discarded and the output of @chunking@ is accumulated by the
1402−-- supplied fold. The parser fails if @chunking@ fails.
1403−--
1404−-- Stops when the fold @f@ stops.
1405−--
1406−{-# INLINE manyTill #-}
1407−manyTill :: Monad m
1408− => Parser a m b -- ^ Chunking parser. Parses chunks of input.
1409− -> Parser a m x -- ^ Test parser. Parsing stops when this parser succeeds
1410− -- else backtract and run the chunking parser.
1411− -> Fold m b c -- ^ Folds the output of the chunking parser.
1412− -> Parser a m c
1413−manyTill collect test f =
1414− D.toParserK $ D.manyTill (D.fromParserK collect) (D.fromParserK test) f
1415−
1416−-- | @manyThen f collect recover@ repeats the parser @collect@ on the input and
1417−-- collects the output in the supplied fold. If the the parser @collect@ fails,
1418−-- parser @recover@ is run until it stops and then we start repeating the
1419−-- parser @collect@ again. The parser fails if the recovery parser fails.
1420−--
1421−-- For example, this can be used to find a key frame in a video stream after an
1422−-- error.
1423−--
1424−-- /Unimplemented/
1425−--
1426−{-# INLINE manyThen #-}
1427−manyThen :: -- (Foldable t, Monad m) =>
1428− Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
1429−manyThen _parser _recover _f = undefined
1430−
1431−-------------------------------------------------------------------------------
1432−-- Interleaving
1433−-------------------------------------------------------------------------------
1434−--
1435−-- To deinterleave we can chain two parsers one behind the other. The input is
1436−-- given to the first parser and the input definitively rejected by the first
1437−-- parser is given to the second parser.
1438−--
1439−-- We can either have the parsers themselves buffer the input or use the shared
1440−-- global buffer to hold it until none of the parsers need it. When the first
1441−-- parser returns Skip (i.e. rewind) we let the second parser consume the
1442−-- rejected input and when it is done we move the cursor forward to the first
1443−-- parser again. This will require a "move forward" command as well.
1444−--
1445−-- To implement grep we can use three parsers, one to find the pattern, one
1446−-- to store the context behind the pattern and one to store the context in
1447−-- front of the pattern. When a match occurs we need to emit the accumulator of
1448−-- all the three parsers. One parser can count the line numbers to provide the
1449−-- line number info.
1450−
1451−-- XXX rename this to intercalate
1452−-- | Apply two parsers alternately to an input stream. The input stream is
1453−-- considered an interleaving of two patterns. The two parsers represent the
1454−-- two patterns.
1455−--
1456−{-# INLINE deintercalate #-}
1457−deintercalate :: Monad m =>
1458− Parser a m x
1459− -> Parser a m y
1460− -> Fold m (Either x y) z
1461− -> Parser a m z
1462−deintercalate contentL contentR sink =
1463− D.toParserK
1464− $ D.deintercalate
1465− (D.fromParserK contentL) (D.fromParserK contentR) sink
1466−
1467−-- | Parse items separated by a separator parsed by the supplied parser. At
1468−-- least one item must be present for the parser to succeed.
1469−--
1470−-- Note that this can go in infinite loop if both the parsers fail on some
1471−-- input. Detection of that would make the implementation more complex.
1472−--
1473−{-# INLINE sepBy1 #-}
1474−sepBy1 :: Monad m =>
1475− Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
1476−sepBy1 p sep sink = do
1477− x <- p
1478− f <- fromEffect $ FL.reduce sink
1479− f1 <- fromEffect $ FL.snoc f x
1480− many (sep >> p) f1
1481−
1482−-- | Run the content parser first, when it is done, the separator parser is
1483−-- run, when it is done content parser is run again and so on. If none of the
1484−-- parsers consumes an input then parser returns a failure.
1485−--
1486−-- >>> sepBy p1 p2 sink = Parser.deintercalate p1 p2 (Fold.catLefts sink)
1487−-- >>> sepBy content sep sink = Parser.sepBy1 content sep sink <|> return mempty
1488−--
1489−{-# INLINE sepBy #-}
1490−sepBy :: Monad m =>
1491− Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
1492−sepBy content sep sink =
1493− D.toParserK $ D.sepBy (D.fromParserK content) (D.fromParserK sep) sink
1494−-- sepBy sink = deintercalate (FL.catLefts sink)
1495−
1496−-------------------------------------------------------------------------------
1497−-- Interleaving a collection of parsers
1498−-------------------------------------------------------------------------------
1499−--
1500−-- | Apply a collection of parsers to an input stream in a round robin fashion.
1501−-- Each parser is applied until it stops and then we repeat starting with the
1502−-- the first parser again.
1503−--
1504−-- /Unimplemented/
1505−--
1506−{-# INLINE roundRobin #-}
1507−roundRobin :: -- (Foldable t, Monad m) =>
1508− t (Parser a m b) -> Fold m b c -> Parser a m c
1509−roundRobin _ps _f = undefined
1510−
1511−-------------------------------------------------------------------------------
1512−-- Repeated Alternatives
1513−-------------------------------------------------------------------------------
1514−
1515−-- | Keep trying a parser up to a maximum of @n@ failures. When the parser
1516−-- fails the input consumed till now is dropped and the new instance is tried
1517−-- on the fresh input.
1518−--
1519−-- /Unimplemented/
1520−--
1521−{-# INLINE retryMaxTotal #-}
1522−retryMaxTotal :: -- (Monad m) =>
1523− Int -> Parser a m b -> Fold m b c -> Parser a m c
1524−retryMaxTotal _n _p _f = undefined
1525−
1526−-- | Like 'retryMaxTotal' but aborts after @n@ successive failures.
1527−--
1528−-- /Unimplemented/
1529−--
1530−{-# INLINE retryMaxSuccessive #-}
1531−retryMaxSuccessive :: -- (Monad m) =>
1532− Int -> Parser a m b -> Fold m b c -> Parser a m c
1533−retryMaxSuccessive _n _p _f = undefined
1534−
1535−-- | Keep trying a parser until it succeeds. When the parser fails the input
1536−-- consumed till now is dropped and the new instance is tried on the fresh
1537−-- input.
1538−--
1539−-- /Unimplemented/
1540−--
1541−{-# INLINE retry #-}
1542−retry :: -- (Monad m) =>
1543− Parser a m b -> Parser a m b
1544−retry _p = undefined
core/src/Streamly/Internal/Data/Parser/Chunked/Type.hs+2
@@ -57,6 +57,8 @@ data Step a m r =
57 -- Array a -> m (Step a m r), m (Step a m r)
57 -- Array a -> m (Step a m r), m (Step a m r)
58 -- XXX The Array is the only difference from element parser, we can pass
58 -- XXX The Array is the only difference from element parser, we can pass
59 -- this as parameter?
59 -- this as parameter?
60+ -- XXX Unify element and chunked parser, by using the argument as
61+ -- None | Single a | Chunk (Array a).
60 | Partial !Int (Maybe (Array a) -> m (Step a m r))
62 | Partial !Int (Maybe (Array a) -> m (Step a m r))
61 | Continue !Int (Maybe (Array a) -> m (Step a m r))
63 | Continue !Int (Maybe (Array a) -> m (Step a m r))
62 | Error !Int String
64 | Error !Int String
core/src/Streamly/Internal/Data/Parser/ParserD.hs+780−104
@@ -1,5 +1,3 @@
1−#include "inline.hs"
2−
3-- |
1-- |
4-- Module : Streamly.Internal.Data.Parser.ParserD
2-- Module : Streamly.Internal.Data.Parser.ParserD
5-- Copyright : (c) 2020 Composewell Technologies
3-- Copyright : (c) 2020 Composewell Technologies
@@ -8,7 +6,42 @@
8-- Stability : experimental
6-- Stability : experimental
9-- Portability : GHC
7-- Portability : GHC
10--
8--
11−-- Direct style parser implementation with stream fusion.
9+-- Fast backtracking parsers with stream fusion and native streaming
10+-- capability.
11+--
12+-- 'Applicative' and 'Control.Applicative.Alternative' type class based
13+-- combinators from the
14+-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>
15+-- package can also be used with the 'Parser' type. However, there are two
16+-- important differences between @parser-combinators@ and the equivalent ones
17+-- provided in this module in terms of performance:
18+--
19+-- 1) @parser-combinators@ use plain Haskell lists to collect the results, in a
20+-- strict Monad like IO, the results are necessarily buffered before they can
21+-- be consumed. This may not perform optimally in streaming applications
22+-- processing large amounts of data. Equivalent combinators in this module can
23+-- consume the results of parsing using a 'Fold', thus providing a scalability
24+-- and a composable consumer.
25+--
26+-- 2) Several combinators in this module can be many times faster because of
27+-- stream fusion. For example, 'Streamly.Internal.Data.Parser.many' combinator
28+-- in this module is much faster than the 'Control.Applicative.many' combinator
29+-- of 'Control.Applicative.Alternative' type class.
30+--
31+-- = Errors
32+--
33+-- Failing parsers in this module throw the 'D.ParseError' exception.
34+--
35+-- = Naming
36+--
37+-- As far as possible, try that the names of the combinators in this module are
38+-- consistent with:
39+--
40+-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>
41+-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>
42+-- * <http://hackage.haskell.org/package/megaparsec megaparsec>
43+-- * <http://hackage.haskell.org/package/attoparsec attoparsec>
44+-- * <http://hackage.haskell.org/package/parsec parsec>
12
45
13module Streamly.Internal.Data.Parser.ParserD
46module Streamly.Internal.Data.Parser.ParserD
14 (
47 (
@@ -16,7 +49,6 @@ module Streamly.Internal.Data.Parser.ParserD
16 , ParseError (..)
49 , ParseError (..)
17 , Step (..)
50 , Step (..)
18 , Initial (..)
51 , Initial (..)
19− , rmapM
20
52
21 -- * Conversion to/from ParserK
53 -- * Conversion to/from ParserK
22 , fromParserK
54 , fromParserK
@@ -40,17 +72,26 @@ module Streamly.Internal.Data.Parser.ParserD
40 , postscan
72 , postscan
41 , filter
73 , filter
42
74
75+ -- * Map on output
76+ , rmapM
77+
43 -- * Element parsers
78 -- * Element parsers
44 , peek
79 , peek
80+
81+ -- All of these can be expressed in terms of either
82+ , one
83+ , oneEq
84+ , oneNotEq
85+ , oneOf
86+ , noneOf
45 , eof
87 , eof
46 , satisfy
88 , satisfy
47− , next
48 , maybe
89 , maybe
49 , either
90 , either
50
91
51− -- * Sequence parsers
92+ -- * Sequence parsers (tokenizers)
52 --
93 --
53− -- Parsers chained in series, if one parser terminates the composition
94+ -- | Parsers chained in series, if one parser terminates the composition
54 -- terminates. Currently we are using folds to collect the output of the
95 -- terminates. Currently we are using folds to collect the output of the
55 -- parsers but we can use Parsers instead of folds to make the composition
96 -- parsers but we can use Parsers instead of folds to make the composition
56 -- more powerful. For example, we can do:
97 -- more powerful. For example, we can do:
@@ -58,41 +99,54 @@ module Streamly.Internal.Data.Parser.ParserD
58 -- takeEndByOrMax cond n p = takeEndBy cond (take n p)
99 -- takeEndByOrMax cond n p = takeEndBy cond (take n p)
59 -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)
100 -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)
60 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
101 -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
102+ , lookAhead
61
103
104+ -- ** By length
62− -- Grab a sequence of input elements without inspecting them
105+ -- | Grab a sequence of input elements without inspecting them
63 , takeBetween
106 , takeBetween
64− -- , take -- take -- takeBetween 0 n
107+ -- , take -- takeBetween 0 n
65 , takeEQ -- takeBetween n n
108 , takeEQ -- takeBetween n n
66 , takeGE -- takeBetween n maxBound
109 , takeGE -- takeBetween n maxBound
67 -- , takeGE1 -- take1 -- takeBetween 1 n
110 -- , takeGE1 -- take1 -- takeBetween 1 n
68 , takeP
111 , takeP
69
112
70 -- Grab a sequence of input elements by inspecting them
113 -- Grab a sequence of input elements by inspecting them
71− , lookAhead
114+ -- ** Exact match
115+ , listEq
116+ , listEqBy
117+ , streamEqBy
118+ , subsequenceBy
119+
120+ -- ** By predicate
72 , takeWhile
121 , takeWhile
73 , takeWhileP
122 , takeWhileP
74 , takeWhile1
123 , takeWhile1
124+ , dropWhile
75
125
76− -- Separators
126+ -- ** Separators
77 , takeEndBy
127 , takeEndBy
78 , takeEndBy_
128 , takeEndBy_
79 , takeEndByEsc
129 , takeEndByEsc
130+ -- , takeEndByEsc_
80 , takeStartBy
131 , takeStartBy
132+ , takeStartBy_
133+ , takeEitherSepBy
134+ , wordBy
135+
136+ -- ** By comparing
137+ , groupBy
138+ , groupByRolling
139+ , groupByRollingEither
140+
141+ -- ** Framing
142+ -- , takeFramedBy
81 , takeFramedBy_
143 , takeFramedBy_
82 , takeFramedByEsc_
144 , takeFramedByEsc_
83 , takeFramedByGeneric
145 , takeFramedByGeneric
84−
85− -- Words and grouping
86− , wordBy
87 , wordFramedBy
146 , wordFramedBy
88 , wordQuotedBy
147 , wordQuotedBy
89− , groupBy
90− , groupByRolling
91− , groupByRollingEither
92
148
93 -- Matching strings
149 -- Matching strings
94− , listEqBy
95− , streamEqBy
96 -- , prefixOf -- match any prefix of a given string
150 -- , prefixOf -- match any prefix of a given string
97 -- , suffixOf -- match any suffix of a given string
151 -- , suffixOf -- match any suffix of a given string
98 -- , infixOf -- match any substring of a given string
152 -- , infixOf -- match any substring of a given string
@@ -121,6 +175,25 @@ module Streamly.Internal.Data.Parser.ParserD
121 -- Use two folds, run a primary parser, its rejected values go to the
175 -- Use two folds, run a primary parser, its rejected values go to the
122 -- secondary parser.
176 -- secondary parser.
123 , deintercalate
177 , deintercalate
178+ -- , deintercalatePrefix
179+ -- , deintercalateSuffix
180+
181+ -- *** Special cases
182+ -- | TODO: traditional implmentations of these may be of limited use. For
183+ -- example, consider parsing lines separated by @\\r\\n@. The main parser
184+ -- will have to detect and exclude the sequence @\\r\\n@ anyway so that we
185+ -- can apply the "sep" parser.
186+ --
187+ -- We can instead implement these as special cases of deintercalate.
188+ --
189+ -- @
190+ -- , endBy
191+ -- , sepEndBy
192+ -- , beginBy
193+ -- , sepBeginBy
194+ -- , sepAroundBy
195+ -- @
196+ , sepBy1
124 , sepBy
197 , sepBy
125
198
126 -- ** Sequential Alternative
199 -- ** Sequential Alternative
@@ -132,6 +205,7 @@ module Streamly.Internal.Data.Parser.ParserD
132 , longest
205 , longest
133 -- , fastest
206 -- , fastest
134 -}
207 -}
208+
135 -- * N-ary Combinators
209 -- * N-ary Combinators
136 -- ** Sequential Collection
210 -- ** Sequential Collection
137 , sequence
211 , sequence
@@ -142,25 +216,12 @@ module Streamly.Internal.Data.Parser.ParserD
142 , countBetween
216 , countBetween
143 -- , countBetweenTill
217 -- , countBetweenTill
144
218
145− , many
146 , manyP
219 , manyP
220+ , many
147 , some
221 , some
222+ , manyTillP
148 , manyTill
223 , manyTill
149−
224+ , manyThen
150− -- -- ** Special cases
151− -- XXX traditional implmentations of these may be of limited use. For
152− -- example, consider parsing lines separated by "\r\n". The main parser
153− -- will have to detect and exclude the sequence "\r\n" anyway so that we
154− -- can apply the "sep" parser.
155− --
156− -- We can instead implement these as special cases of deintercalate.
157− --
158− -- , endBy
159− -- , sepBy
160− -- , sepEndBy
161− -- , beginBy
162− -- , sepBeginBy
163− -- , sepAroundBy
164
225
165 -- -- * Distribution
226 -- -- * Distribution
166 --
227 --
@@ -171,24 +232,32 @@ module Streamly.Internal.Data.Parser.ParserD
171 -- -- ** Distribute to collection
232 -- -- ** Distribute to collection
172 -- -- ** Distribute to repetition
233 -- -- ** Distribute to repetition
173
234
174− -- -- ** Interleaved collection
235+ -- ** Interleaved collection
236+ -- |
237+ --
175− -- Round robin
238+ -- 1. Round robin
176− -- Priority based
239+ -- 2. Priority based
240+ , roundRobin
241+
177 -- -- ** Interleaved repetition
242 -- -- ** Interleaved repetition
178 -- repeat one parser and when it fails run an error recovery parser
243 -- repeat one parser and when it fails run an error recovery parser
179 -- e.g. to find a key frame in the stream after an error
244 -- e.g. to find a key frame in the stream after an error
180
245
181 -- ** Collection of Alternatives
246 -- ** Collection of Alternatives
247+ -- | Unimplemented
248+ --
249+ -- @
182 -- , shortestN
250 -- , shortestN
183 -- , longestN
251 -- , longestN
184 -- , fastestN -- first N successful in time
252 -- , fastestN -- first N successful in time
185 -- , choiceN -- first N successful in position
253 -- , choiceN -- first N successful in position
254+ -- @
186 -- , choice -- first successful in position
255 -- , choice -- first successful in position
187
256
188− -- -- ** Repeated Alternatives
257+ -- ** Repeated Alternatives
189− -- , retryMax -- try N times
258+ , retryMaxTotal
190− -- , retryUntil -- try until successful
259+ , retryMaxSuccessive
191− -- , retryUntilN -- try until successful n times
260+ , retry
192
261
193 -- ** Zipping Input
262 -- ** Zipping Input
194 , zipWithM
263 , zipWithM
@@ -196,9 +265,13 @@ module Streamly.Internal.Data.Parser.ParserD
196 , indexed
265 , indexed
197 , makeIndexFilter
266 , makeIndexFilter
198 , sampleFromthen
267 , sampleFromthen
268+
269+ -- * Deprecated
270+ , next
199 )
271 )
200where
272where
201
273
274+#include "inline.hs"
202#include "assert.hs"
275#include "assert.hs"
203
276
204import Control.Monad (when)
277import Control.Monad (when)
@@ -209,14 +282,16 @@ import Streamly.Internal.Data.SVar.Type (defState)
209import Streamly.Internal.Data.Either.Strict (Either'(..))
282import Streamly.Internal.Data.Either.Strict (Either'(..))
210import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
283import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
211import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
284import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
285+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
212
286
287+import qualified Data.Foldable as Foldable
213import qualified Streamly.Internal.Data.Fold.Type as FL
288import qualified Streamly.Internal.Data.Fold.Type as FL
214import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
289import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
215import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
290import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
216
291
217import Prelude hiding
292import Prelude hiding
218 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span
293 (any, all, take, takeWhile, sequence, concatMap, maybe, either, span
219− , zip, filter)
294+ , zip, filter, dropWhile)
220-- import Streamly.Internal.Data.Parser.ParserD.Tee
295-- import Streamly.Internal.Data.Parser.ParserD.Tee
221import Streamly.Internal.Data.Parser.ParserD.Type
296import Streamly.Internal.Data.Parser.ParserD.Type
222
297
@@ -224,19 +299,27 @@ import Streamly.Internal.Data.Parser.ParserD.Type
224-- $setup
299-- $setup
225-- >>> :m
300-- >>> :m
226-- >>> import Prelude hiding ()
301-- >>> import Prelude hiding ()
302+-- >>> import Control.Applicative
303+-- >>> import Data.Char (isSpace)
304+-- >>> import qualified Data.Foldable as Foldable
227-- >>> import qualified Data.Maybe as Maybe
305-- >>> import qualified Data.Maybe as Maybe
228-- >>> import qualified Streamly.Data.Stream as Stream
306-- >>> import qualified Streamly.Data.Stream as Stream
229-- >>> import qualified Streamly.Internal.Data.Stream as Stream
307-- >>> import qualified Streamly.Internal.Data.Stream as Stream
230−-- >>> import qualified Streamly.Data.Fold as Fold
308+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
231-- >>> import qualified Streamly.Internal.Data.Parser as Parser
309-- >>> import qualified Streamly.Internal.Data.Parser as Parser
232
310
233-------------------------------------------------------------------------------
311-------------------------------------------------------------------------------
234-- Downgrade a parser to a Fold
312-- Downgrade a parser to a Fold
235-------------------------------------------------------------------------------
313-------------------------------------------------------------------------------
236
314
237−-- | See 'Streamly.Internal.Data.Parser.toFold'.
315+-- | Make a 'Fold' from a 'Parser'. The fold just throws an exception if the
316+-- parser fails or tries to backtrack.
238--
317--
239−-- /Internal/
318+-- This can be useful in combinators that accept a Fold and we know that a
319+-- Parser cannot fail or failure exception is acceptable as there is no way to
320+-- recover.
321+--
322+-- /Pre-release/
240--
323--
241{-# INLINE toFold #-}
324{-# INLINE toFold #-}
242toFold :: Monad m => Parser a m b -> Fold m a b
325toFold :: Monad m => Parser a m b -> Fold m a b
@@ -280,10 +363,8 @@ toFold (Parser pstep pinitial pextract) = Fold step initial extract
280-------------------------------------------------------------------------------
363-------------------------------------------------------------------------------
281-- Upgrade folds to parses
364-- Upgrade folds to parses
282-------------------------------------------------------------------------------
365-------------------------------------------------------------------------------
283−--
366+
284−-- | See 'Streamly.Internal.Data.Parser.fromFold'.
367+-- | Make a 'Parser' from a 'Fold'.
285−--
286−-- /Pre-release/
287--
368--
288{-# INLINE fromFold #-}
369{-# INLINE fromFold #-}
289fromFold :: Monad m => Fold m a b -> Parser a m b
370fromFold :: Monad m => Fold m a b -> Parser a m b
@@ -307,7 +388,9 @@ fromFold (Fold fstep finitial fextract) = Parser step initial extract
307
388
308 extract = fmap (Done 0) . fextract
389 extract = fmap (Done 0) . fextract
309
390
310−-- | Convert Maybe returning folds to error returning parsers.
391+-- | Convert a Maybe returning fold to an error returning parser. The first
392+-- argument is the error message that the parser would return when the fold
393+-- returns Nothing.
311--
394--
312-- /Pre-release/
395-- /Pre-release/
313--
396--
@@ -348,9 +431,15 @@ fromFoldMaybe errMsg (Fold fstep finitial fextract) =
348-- Failing Parsers
431-- Failing Parsers
349-------------------------------------------------------------------------------
432-------------------------------------------------------------------------------
350
433
351−-- | See 'Streamly.Internal.Data.Parser.peek'.
434+-- | Peek the head element of a stream, without consuming it. Fails if it
435+-- encounters end of input.
352--
436--
353−-- /Pre-release/
437+-- >>> Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]
438+-- Right (1,1)
439+--
440+-- @
441+-- peek = lookAhead (satisfy True)
442+-- @
354--
443--
355{-# INLINE peek #-}
444{-# INLINE peek #-}
356peek :: Monad m => Parser a m a
445peek :: Monad m => Parser a m a
@@ -364,9 +453,10 @@ peek = Parser step initial extract
364
453
365 extract () = return $ Error "peek: end of input"
454 extract () = return $ Error "peek: end of input"
366
455
367−-- | See 'Streamly.Internal.Data.Parser.eof'.
456+-- | Succeeds if we are at the end of input, fails otherwise.
368--
457--
369−-- /Pre-release/
458+-- >>> Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]
459+-- Right (1,())
370--
460--
371{-# INLINE eof #-}
461{-# INLINE eof #-}
372eof :: Monad m => Parser a m ()
462eof :: Monad m => Parser a m ()
@@ -380,10 +470,12 @@ eof = Parser step initial extract
380
470
381 extract () = return $ Done 0 ()
471 extract () = return $ Done 0 ()
382
472
383−-- | See 'Streamly.Internal.Data.Parser.next'.
473+-- | Return the next element of the input. Returns 'Nothing'
474+-- on end of input. Also known as 'head'.
384--
475--
385-- /Pre-release/
476-- /Pre-release/
386--
477--
478+{-# DEPRECATED next "Please use \"fromFold Fold.one\" instead" #-}
387{-# INLINE next #-}
479{-# INLINE next #-}
388next :: Monad m => Parser a m (Maybe a)
480next :: Monad m => Parser a m (Maybe a)
389next = Parser step initial extract
481next = Parser step initial extract
@@ -396,7 +488,9 @@ next = Parser step initial extract
396
488
397 extract () = pure $ Done 0 Nothing
489 extract () = pure $ Done 0 Nothing
398
490
399−-- | See 'Streamly.Internal.Data.Parser.either'.
491+-- | Map an 'Either' returning function on the next element in the stream. If
492+-- the function returns 'Left err', the parser fails with the error message
493+-- @err@ otherwise returns the 'Right' value.
400--
494--
401-- /Pre-release/
495-- /Pre-release/
402--
496--
@@ -415,11 +509,15 @@ either f = Parser step initial extract
415
509
416 extract () = return $ Error "end of input"
510 extract () = return $ Error "end of input"
417
511
418−-- | See 'Streamly.Internal.Data.Parser.maybe'.
512+-- | Map a 'Maybe' returning function on the next element in the stream. The
513+-- parser fails if the function returns 'Nothing' otherwise returns the 'Just'
514+-- value.
419--
515--
420-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
516-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
421-- >>> maybe f = Parser.either (toEither . f)
517-- >>> maybe f = Parser.either (toEither . f)
422--
518--
519+-- >>> maybe f = Parser.fromFoldMaybe "maybe: predicate failed" (Fold.maybe f)
520+--
423-- /Pre-release/
521-- /Pre-release/
424--
522--
425{-# INLINE maybe #-}
523{-# INLINE maybe #-}
@@ -438,13 +536,14 @@ maybe parserF = Parser step initial extract
438
536
439 extract () = return $ Error "maybe: end of input"
537 extract () = return $ Error "maybe: end of input"
440
538
441−-- | See 'Streamly.Internal.Data.Parser.satisfy'.
539+-- | Returns the next element if it passes the predicate, fails otherwise.
540+--
541+-- >>> Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]
542+-- Right 1
442--
543--
443-- >>> toMaybe f x = if f x then Just x else Nothing
544-- >>> toMaybe f x = if f x then Just x else Nothing
444-- >>> satisfy f = Parser.maybe (toMaybe f)
545-- >>> satisfy f = Parser.maybe (toMaybe f)
445--
546--
446−-- /Pre-release/
447−--
448{-# INLINE satisfy #-}
547{-# INLINE satisfy #-}
449satisfy :: Monad m => (a -> Bool) -> Parser a m a
548satisfy :: Monad m => (a -> Bool) -> Parser a m a
450-- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)
549-- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)
@@ -461,6 +560,67 @@ satisfy predicate = Parser step initial extract
461
560
462 extract () = return $ Error "satisfy: end of input"
561 extract () = return $ Error "satisfy: end of input"
463
562
563+-- | Consume one element from the head of the stream. Fails if it encounters
564+-- end of input.
565+--
566+-- >>> one = Parser.satisfy $ const True
567+--
568+{-# INLINE one #-}
569+one :: Monad m => Parser a m a
570+one = satisfy $ const True
571+
572+-- Alternate names: "only", "onlyThis".
573+
574+-- | Match a specific element.
575+--
576+-- >>> oneEq x = Parser.satisfy (== x)
577+--
578+{-# INLINE oneEq #-}
579+oneEq :: (Monad m, Eq a) => a -> Parser a m a
580+oneEq x = satisfy (== x)
581+
582+-- Alternate names: "exclude", "notThis".
583+
584+-- | Match anything other than the supplied element.
585+--
586+-- >>> oneNotEq x = Parser.satisfy (/= x)
587+--
588+{-# INLINE oneNotEq #-}
589+oneNotEq :: (Monad m, Eq a) => a -> Parser a m a
590+oneNotEq x = satisfy (/= x)
591+
592+-- | Match any one of the elements in the supplied list.
593+--
594+-- >>> oneOf xs = Parser.satisfy (`Foldable.elem` xs)
595+--
596+-- When performance matters a pattern matching predicate could be more
597+-- efficient than a 'Foldable' datatype:
598+--
599+-- @
600+-- let p x =
601+-- case x of
602+-- 'a' -> True
603+-- 'e' -> True
604+-- _ -> False
605+-- in satisfy p
606+-- @
607+--
608+-- GHC may use a binary search instead of linear search in the list.
609+-- Alternatively, you can also use an array instead of list for storage and
610+-- search.
611+--
612+{-# INLINE oneOf #-}
613+oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
614+oneOf xs = satisfy (`Foldable.elem` xs)
615+
616+-- | See performance notes in 'oneOf'.
617+--
618+-- >>> noneOf xs = Parser.satisfy (`Foldable.notElem` xs)
619+--
620+{-# INLINE noneOf #-}
621+noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
622+noneOf xs = satisfy (`Foldable.notElem` xs)
623+
464-------------------------------------------------------------------------------
624-------------------------------------------------------------------------------
465-- Taking elements
625-- Taking elements
466-------------------------------------------------------------------------------
626-------------------------------------------------------------------------------
@@ -469,7 +629,43 @@ satisfy predicate = Parser step initial extract
469{-# ANN type Tuple'Fused Fuse #-}
629{-# ANN type Tuple'Fused Fuse #-}
470data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
630data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
471
631
632+-- | @takeBetween m n@ takes a minimum of @m@ and a maximum of @n@ input
633+-- elements and folds them using the supplied fold.
634+--
635+-- Stops after @n@ elements.
636+-- Fails if the stream ends before @m@ elements could be taken.
637+--
638+-- Examples: -
639+--
640+-- @
641+-- >>> :{
642+-- takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)
472−-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
643+-- where prsr = Parser.takeBetween low high Fold.toList
644+-- :}
645+--
646+-- @
647+--
648+-- >>> takeBetween' 2 4 [1, 2, 3, 4, 5]
649+-- Right [1,2,3,4]
650+--
651+-- >>> takeBetween' 2 4 [1, 2]
652+-- Right [1,2]
653+--
654+-- >>> takeBetween' 2 4 [1]
655+-- Left (ParseError "takeBetween: Expecting alteast 2 elements, got 1")
656+--
657+-- >>> takeBetween' 0 0 [1, 2]
658+-- Right []
659+--
660+-- >>> takeBetween' 0 1 []
661+-- Right []
662+--
663+-- @takeBetween@ is the most general take operation, other take operations can
664+-- be defined in terms of takeBetween. For example:
665+--
666+-- >>> take n = Parser.takeBetween 0 n
667+-- >>> takeEQ n = Parser.takeBetween n n
668+-- >>> takeGE n = Parser.takeBetween n maxBound
473--
669--
474-- /Pre-release/
670-- /Pre-release/
475--
671--
@@ -546,9 +742,14 @@ takeBetween low high (Fold fstep finitial fextract) =
546 | i >= low && i <= high = fmap IDone (fextract s)
742 | i >= low && i <= high = fmap IDone (fextract s)
547 | otherwise = return $ IError (f i)
743 | otherwise = return $ IError (f i)
548
744
549−-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
745+-- | Stops after taking exactly @n@ input elements.
550--
746--
551−-- /Pre-release/
747+-- * Stops - after consuming @n@ elements.
748+-- * Fails - if the stream or the collecting fold ends before it can collect
749+-- exactly @n@ elements.
750+--
751+-- >>> Stream.parse (Parser.takeEQ 4 Fold.toList) $ Stream.fromList [1,0,1]
752+-- Left (ParseError "takeEQ: Expecting exactly 4 elements, input terminated on 3")
552--
753--
553{-# INLINE takeEQ #-}
754{-# INLINE takeEQ #-}
554takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b
755takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b
@@ -602,7 +803,17 @@ takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
602 $ "takeEQ: Expecting exactly " ++ show cnt
803 $ "takeEQ: Expecting exactly " ++ show cnt
603 ++ " elements, input terminated on " ++ show i
804 ++ " elements, input terminated on " ++ show i
604
805
605−-- | See 'Streamly.Internal.Data.Parser.takeGE'.
806+-- | Take at least @n@ input elements, but can collect more.
807+--
808+-- * Stops - when the collecting fold stops.
809+-- * Fails - if the stream or the collecting fold ends before producing @n@
810+-- elements.
811+--
812+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1]
813+-- Left (ParseError "takeGE: Expecting at least 4 elements, input terminated on 3")
814+--
815+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]
816+-- Right [1,0,1,0,1]
606--
817--
607-- /Pre-release/
818-- /Pre-release/
608--
819--
@@ -659,7 +870,19 @@ takeGE n (Fold fstep finitial fextract) = Parser step initial extract
659-- Conditional splitting
870-- Conditional splitting
660-------------------------------------------------------------------------------
871-------------------------------------------------------------------------------
661
872
662−-- | See 'Streamly.Internal.Data.Parser.takeWhileP'.
873+-- XXX We should perhaps use only takeWhileP and rename it to takeWhile.
874+
875+-- | Like 'takeWhile' but uses a 'Parser' instead of a 'Fold' to collect the
876+-- input. The combinator stops when the condition fails or if the collecting
877+-- parser stops.
878+--
879+-- Other interesting parsers can be implemented in terms of this parser:
880+--
881+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
882+-- >>> takeWhileBetween cond m n p = Parser.takeWhileP cond (Parser.takeBetween m n p)
883+--
884+-- Stops: when the condition fails or the collecting parser stops.
885+-- Fails: when the collecting parser fails.
663--
886--
664-- /Pre-release/
887-- /Pre-release/
665--
888--
@@ -682,12 +905,26 @@ takeWhileP predicate (Parser pstep pinitial pextract) =
682 Partial _ _ -> error "Bug: takeWhileP: Partial in extract"
905 Partial _ _ -> error "Bug: takeWhileP: Partial in extract"
683 Continue n s1 -> return $ Continue (n + 1) s1
906 Continue n s1 -> return $ Continue (n + 1) s1
684
907
685−-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
908+-- | Collect stream elements until an element fails the predicate. The element
909+-- on which the predicate fails is returned back to the input stream.
686--
910--
687−-- /Pre-release/
911+-- * Stops - when the predicate fails or the collecting fold stops.
912+-- * Fails - never.
913+--
914+-- >>> Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]
915+-- Right [0,0]
916+--
917+-- >>> takeWhile cond f = Parser.takeWhileP cond (Parser.fromFold f)
918+--
919+-- We can implement a @breakOn@ using 'takeWhile':
920+--
921+-- @
922+-- breakOn p = takeWhile (not p)
923+-- @
688--
924--
689{-# INLINE takeWhile #-}
925{-# INLINE takeWhile #-}
690takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
926takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
927+-- takeWhile cond f = takeWhileP cond (fromFold f)
691takeWhile predicate (Fold fstep finitial fextract) =
928takeWhile predicate (Fold fstep finitial fextract) =
692 Parser step initial extract
929 Parser step initial extract
693
930
@@ -711,12 +948,13 @@ takeWhile predicate (Fold fstep finitial fextract) =
711
948
712 extract s = fmap (Done 0) (fextract s)
949 extract s = fmap (Done 0) (fextract s)
713
950
714−-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
951+-- | Like 'takeWhile' but takes at least one element otherwise fails.
715--
952--
716−-- /Pre-release/
953+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
717--
954--
718{-# INLINE takeWhile1 #-}
955{-# INLINE takeWhile1 #-}
719takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
956takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
957+-- takeWhile1 cond f = takeWhileP cond (takeBetween 1 maxBound f)
720takeWhile1 predicate (Fold fstep finitial fextract) =
958takeWhile1 predicate (Fold fstep finitial fextract) =
721 Parser step initial extract
959 Parser step initial extract
722
960
@@ -753,6 +991,17 @@ takeWhile1 predicate (Fold fstep finitial fextract) =
753 extract (Left' _) = return $ Error "takeWhile1: end of input"
991 extract (Left' _) = return $ Error "takeWhile1: end of input"
754 extract (Right' s) = fmap (Done 0) (fextract s)
992 extract (Right' s) = fmap (Done 0) (fextract s)
755
993
994+-- | Drain the input as long as the predicate succeeds, running the effects and
995+-- discarding the results.
996+--
997+-- This is also called @skipWhile@ in some parsing libraries.
998+--
999+-- >>> dropWhile p = Parser.takeWhile p Fold.drain
1000+--
1001+{-# INLINE dropWhile #-}
1002+dropWhile :: Monad m => (a -> Bool) -> Parser a m ()
1003+dropWhile p = takeWhile p FL.drain
1004+
756-------------------------------------------------------------------------------
1005-------------------------------------------------------------------------------
757-- Separators
1006-- Separators
758-------------------------------------------------------------------------------
1007-------------------------------------------------------------------------------
@@ -760,6 +1009,7 @@ takeWhile1 predicate (Fold fstep finitial fextract) =
760data FramedEscState s =
1009data FramedEscState s =
761 FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int
1010 FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int
762
1011
1012+-- XXX We can remove Maybe from esc
763{-# INLINE takeFramedByGeneric #-}
1013{-# INLINE takeFramedByGeneric #-}
764takeFramedByGeneric :: Monad m =>
1014takeFramedByGeneric :: Monad m =>
765 Maybe (a -> Bool)
1015 Maybe (a -> Bool)
@@ -857,12 +1107,26 @@ takeFramedByGeneric esc begin end (Fold fstep finitial fextract) =
857 Nothing -> err "takeFramedByGeneric: missing closing frame"
1107 Nothing -> err "takeFramedByGeneric: missing closing frame"
858 extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"
1108 extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"
859
1109
860−-- | See 'Streamly.Internal.Data.Parser.takeEndBy'.
1110+-- | @takeEndBy cond parser@ parses a token that ends by a separator chosen by
1111+-- the supplied predicate. The separator is also taken with the token.
1112+--
1113+-- This can be combined with other parsers to implement other interesting
1114+-- parsers as follows:
1115+--
1116+-- >>> takeEndByLE cond n p = Parser.takeEndBy cond (Parser.fromFold $ Fold.take n p)
1117+-- >>> takeEndByBetween cond m n p = Parser.takeEndBy cond (Parser.takeBetween m n p)
1118+--
1119+-- >>> takeEndBy = Parser.takeEndByEsc (const False)
1120+--
1121+-- See also "Streamly.Data.Fold.takeEndBy". Unlike the fold, the collecting
1122+-- parser in the takeEndBy parser can decide whether to fail or not if the
1123+-- stream does not end with separator.
861--
1124--
862-- /Pre-release/
1125-- /Pre-release/
863--
1126--
864{-# INLINE takeEndBy #-}
1127{-# INLINE takeEndBy #-}
865takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
1128takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
1129+-- takeEndBy = takeEndByEsc (const False)
866takeEndBy cond (Parser pstep pinitial pextract) =
1130takeEndBy cond (Parser pstep pinitial pextract) =
867
1131
868 Parser step initial pextract
1132 Parser step initial pextract
@@ -877,10 +1141,11 @@ takeEndBy cond (Parser pstep pinitial pextract) =
877 then return res
1141 then return res
878 else extractStep pextract res
1142 else extractStep pextract res
879
1143
880−-- | See 'Streamly.Internal.Data.Parser.takeEndByEsc'.
1144+-- | Like 'takeEndBy' but the separator elements can be escaped using an
881−--
1145+-- escape char determined by the first predicate. The escape characters are
882−-- /Pre-release/
1146+-- removed.
883--
1147--
1148+-- /pre-release/
884{-# INLINE takeEndByEsc #-}
1149{-# INLINE takeEndByEsc #-}
885takeEndByEsc :: Monad m =>
1150takeEndByEsc :: Monad m =>
886 (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b
1151 (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b
@@ -909,12 +1174,18 @@ takeEndByEsc isEsc isSep (Parser pstep pinitial pextract) =
909 extract (Right' _) =
1174 extract (Right' _) =
910 return $ Error "takeEndByEsc: trailing escape"
1175 return $ Error "takeEndByEsc: trailing escape"
911
1176
1177+-- | Like 'takeEndBy' but the separator is dropped.
1178+--
912−-- | See 'Streamly.Internal.Data.Parser.takeEndBy_'.
1179+-- See also "Streamly.Data.Fold.takeEndBy_".
913--
1180--
914-- /Pre-release/
1181-- /Pre-release/
915--
1182--
916{-# INLINE takeEndBy_ #-}
1183{-# INLINE takeEndBy_ #-}
917takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b
1184takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b
1185+{-
1186+takeEndBy_ isEnd p =
1187+ takeFramedByGeneric Nothing Nothing (Just isEnd) (toFold p)
1188+-}
918takeEndBy_ cond (Parser pstep pinitial pextract) =
1189takeEndBy_ cond (Parser pstep pinitial pextract) =
919
1190
920 Parser step pinitial pextract
1191 Parser step pinitial pextract
@@ -926,11 +1197,39 @@ takeEndBy_ cond (Parser pstep pinitial pextract) =
926 then pextract s
1197 then pextract s
927 else pstep s a
1198 else pstep s a
928
1199
929−-- | See 'Streamly.Internal.Data.Parser.takeStartBy'.
1200+-- | Take either the separator or the token. Separator is a Left value and
1201+-- token is Right value.
1202+--
1203+-- /Unimplemented/
1204+{-# INLINE takeEitherSepBy #-}
1205+takeEitherSepBy :: -- Monad m =>
1206+ (a -> Bool) -> Fold m (Either a b) c -> Parser a m c
1207+takeEitherSepBy _cond = undefined -- D.toParserK . D.takeEitherSepBy cond
1208+
1209+-- | Parse a token that starts with an element chosen by the predicate. The
1210+-- parser fails if the input does not start with the selected element.
1211+--
1212+-- * Stops - when the predicate succeeds in non-leading position.
1213+-- * Fails - when the predicate fails in the leading position.
1214+--
1215+-- >>> splitWithPrefix p f = Stream.parseMany (Parser.takeStartBy p f)
1216+--
1217+-- Examples: -
1218+--
1219+-- >>> p = Parser.takeStartBy (== ',') Fold.toList
1220+-- >>> leadingComma = Stream.parse p . Stream.fromList
1221+-- >>> leadingComma "a,b"
1222+-- Left (ParseError "takeStartBy: missing frame start")
1223+-- ...
1224+-- >>> leadingComma ",,"
1225+-- Right ","
1226+-- >>> leadingComma ",a,b"
1227+-- Right ",a"
1228+-- >>> leadingComma ""
1229+-- Right ""
930--
1230--
931-- /Pre-release/
1231-- /Pre-release/
932--
1232--
933−
934{-# INLINE takeStartBy #-}
1233{-# INLINE takeStartBy #-}
935takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
1234takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
936takeStartBy cond (Fold fstep finitial fextract) =
1235takeStartBy cond (Fold fstep finitial fextract) =
@@ -966,9 +1265,40 @@ takeStartBy cond (Fold fstep finitial fextract) =
966 extract (Left' s) = fmap (Done 0) $ fextract s
1265 extract (Left' s) = fmap (Done 0) $ fextract s
967 extract (Right' s) = fmap (Done 0) $ fextract s
1266 extract (Right' s) = fmap (Done 0) $ fextract s
968
1267
1268+-- | Like 'takeStartBy' but drops the separator.
1269+--
1270+-- >>> takeStartBy_ isBegin = Parser.takeFramedByGeneric Nothing (Just isBegin) Nothing
1271+--
1272+{-# INLINE takeStartBy_ #-}
1273+takeStartBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
1274+takeStartBy_ isBegin = takeFramedByGeneric Nothing (Just isBegin) Nothing
1275+
1276+-- | @takeFramedByEsc_ isEsc isBegin isEnd fold@ parses a token framed using a
1277+-- begin and end predicate, and an escape character. The frame begin and end
1278+-- characters lose their special meaning if preceded by the escape character.
1279+--
1280+-- Nested frames are allowed if begin and end markers are different, nested
1281+-- frames must be balanced unless escaped, nested frame markers are emitted as
1282+-- it is.
1283+--
1284+-- For example,
1285+--
1286+-- >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList
1287+-- >>> Stream.parse p $ Stream.fromList "{hello}"
1288+-- Right "hello"
1289+-- >>> Stream.parse p $ Stream.fromList "{hello {world}}"
1290+-- Right "hello {world}"
1291+-- >>> Stream.parse p $ Stream.fromList "{hello \\{world}"
1292+-- Right "hello {world"
1293+-- >>> Stream.parse p $ Stream.fromList "{hello {world}"
1294+-- Left (ParseError "takeFramedByEsc_: missing frame end")
1295+--
1296+-- /Pre-release/
969{-# INLINE takeFramedByEsc_ #-}
1297{-# INLINE takeFramedByEsc_ #-}
970takeFramedByEsc_ :: Monad m =>
1298takeFramedByEsc_ :: Monad m =>
971 (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
1299 (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
1300+-- takeFramedByEsc_ isEsc isEnd p =
1301+-- takeFramedByGeneric (Just isEsc) Nothing (Just isEnd) (toFold p)
972takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
1302takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
973
1303
974 Parser step initial extract
1304 Parser step initial extract
@@ -1017,9 +1347,16 @@ takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
1017
1347
1018data FramedState s = FrameInit !s | FrameGo !s Int
1348data FramedState s = FrameInit !s | FrameGo !s Int
1019
1349
1350+-- | @takeFramedBy_ isBegin isEnd fold@ parses a token framed by a begin and an
1351+-- end predicate.
1352+--
1353+-- >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)
1354+--
1020{-# INLINE takeFramedBy_ #-}
1355{-# INLINE takeFramedBy_ #-}
1021takeFramedBy_ :: Monad m =>
1356takeFramedBy_ :: Monad m =>
1022 (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
1357 (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
1358+-- takeFramedBy_ isBegin isEnd =
1359+-- takeFramedByGeneric (Just (const False)) (Just isBegin) (Just isEnd)
1023takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
1360takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
1024
1361
1025 Parser step initial extract
1362 Parser step initial extract
@@ -1064,8 +1401,24 @@ takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
1064
1401
1065data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
1402data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
1066
1403
1067−-- | See 'Streamly.Internal.Data.Parser.wordBy'.
1404+-- Note we can also get words using something like:
1405+-- sepBy FL.toList (takeWhile (not . p) Fold.toList) (dropWhile p)
1406+--
1407+-- But that won't be as efficient and ergonomic.
1408+
1409+-- | Like 'splitOn' but strips leading, trailing, and repeated separators.
1410+-- Therefore, @".a..b."@ having '.' as the separator would be parsed as
1411+-- @["a","b"]@. In other words, its like parsing words from whitespace
1412+-- separated text.
1068--
1413--
1414+-- * Stops - when it finds a word separator after a non-word element
1415+-- * Fails - never.
1416+--
1417+-- >>> wordBy = Parser.wordFramedBy (const False) (const False) (const False)
1418+--
1419+-- @
1420+-- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)
1421+-- @
1069--
1422--
1070{-# INLINE wordBy #-}
1423{-# INLINE wordBy #-}
1071wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
1424wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
@@ -1114,7 +1467,24 @@ data WordFramedState s b =
1114 | WordFramedEsc !s !Int
1467 | WordFramedEsc !s !Int
1115 | WordFramedSkipPost !b
1468 | WordFramedSkipPost !b
1116
1469
1470+-- | Like 'wordBy' but treats anything inside a pair of quotes as a single
1471+-- word, the quotes can be escaped by an escape character. Recursive quotes
1472+-- are possible if quote begin and end characters are different, quotes must be
1473+-- balanced. Outermost quotes are stripped.
1474+--
1475+-- >>> braces = Parser.wordFramedBy (== '\\') (== '{') (== '}') isSpace Fold.toList
1476+-- >>> Stream.parse braces $ Stream.fromList "{ab} cd"
1477+-- Right "ab"
1478+-- >>> Stream.parse braces $ Stream.fromList "{ab}{cd}"
1479+-- Right "abcd"
1480+-- >>> Stream.parse braces $ Stream.fromList "a{b} cd"
1481+-- Right "ab"
1482+-- >>> Stream.parse braces $ Stream.fromList "a{{b}} cd"
1483+-- Right "a{b}"
1484+--
1117−-- | See 'Streamly.Internal.Data.Parser.wordFramedBy'
1485+-- >>> quotes = Parser.wordFramedBy (== '\\') (== '"') (== '"') isSpace Fold.toList
1486+-- >>> Stream.parse quotes $ Stream.fromList "\"a\"\"b\""
1487+-- Right "ab"
1118--
1488--
1119{-# INLINE wordFramedBy #-}
1489{-# INLINE wordFramedBy #-}
1120wordFramedBy :: Monad m =>
1490wordFramedBy :: Monad m =>
@@ -1206,6 +1576,26 @@ data WordQuotedState s b a =
1206 | WordQuotedEsc !s !Int a
1576 | WordQuotedEsc !s !Int a
1207 | WordQuotedSkipPost !b
1577 | WordQuotedSkipPost !b
1208
1578
1579+-- | Like 'wordFramedBy' but the closing quote is determined by the opening
1580+-- quote. The first quote begin starts a quote that is closed by its
1581+-- corresponding closing quote.
1582+--
1583+-- 'wordFramedBy' and 'wordQuotedBy' both allow multiple quote characters based
1584+-- on the predicates but 'wordQuotedBy' always fixes the quote at the first
1585+-- occurrence and then it is closed only by the corresponding closing quote.
1586+-- Therefore, other quoting characters can be embedded inside it as normal
1587+-- characters. On the other hand, 'wordFramedBy' would close the quote as soon
1588+-- as it encounters any of the closing quotes.
1589+--
1590+-- >>> q = (`elem` ['"', '\''])
1591+-- >>> p kQ = Parser.wordQuotedBy kQ (== '\\') q q id isSpace Fold.toList
1592+--
1593+-- >>> Stream.parse (p False) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
1594+-- Right "ab'c;d\"ef"
1595+--
1596+-- >>> Stream.parse (p True) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
1597+-- Right "a\"b'c\";'d\"e'f"
1598+--
1209{-# INLINE wordQuotedBy #-}
1599{-# INLINE wordQuotedBy #-}
1210wordQuotedBy :: (Monad m, Eq a) =>
1600wordQuotedBy :: (Monad m, Eq a) =>
1211 Bool -- ^ keep the quotes in the output
1601 Bool -- ^ keep the quotes in the output
@@ -1313,7 +1703,31 @@ data GroupByState a s
1313 = GroupByInit !s
1703 = GroupByInit !s
1314 | GroupByGrouping !a !s
1704 | GroupByGrouping !a !s
1315
1705
1316−-- | See 'Streamly.Internal.Data.Parser.groupBy'.
1706+-- | Given an input stream @[a,b,c,...]@ and a comparison function @cmp@, the
1707+-- parser assigns the element @a@ to the first group, then if @a \`cmp` b@ is
1708+-- 'True' @b@ is also assigned to the same group. If @a \`cmp` c@ is 'True'
1709+-- then @c@ is also assigned to the same group and so on. When the comparison
1710+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
1711+-- the result of the fold is the result of the parser.
1712+--
1713+-- * Stops - when the comparison fails.
1714+-- * Fails - never.
1715+--
1716+-- >>> :{
1717+-- runGroupsBy eq =
1718+-- Stream.fold Fold.toList
1719+-- . Stream.parseMany (Parser.groupBy eq Fold.toList)
1720+-- . Stream.fromList
1721+-- :}
1722+--
1723+-- >>> runGroupsBy (<) []
1724+-- []
1725+--
1726+-- >>> runGroupsBy (<) [1]
1727+-- [Right [1]]
1728+--
1729+-- >>> runGroupsBy (<) [3, 5, 4, 1, 2, 0]
1730+-- [Right [3,5,4],Right [1,2],Right [0]]
1317--
1731--
1318{-# INLINE groupBy #-}
1732{-# INLINE groupBy #-}
1319groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
1733groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
@@ -1345,7 +1759,35 @@ groupBy eq (Fold fstep finitial fextract) = Parser step initial extract
1345 extract (GroupByInit s) = fmap (Done 0) $ fextract s
1759 extract (GroupByInit s) = fmap (Done 0) $ fextract s
1346 extract (GroupByGrouping _ s) = fmap (Done 0) $ fextract s
1760 extract (GroupByGrouping _ s) = fmap (Done 0) $ fextract s
1347
1761
1762+-- | Unlike 'groupBy' this combinator performs a rolling comparison of two
1763+-- successive elements in the input stream. Assuming the input stream
1764+-- is @[a,b,c,...]@ and the comparison function is @cmp@, the parser
1765+-- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is
1766+-- 'True' @b@ is also assigned to the same group. If @b \`cmp` c@ is 'True'
1767+-- then @c@ is also assigned to the same group and so on. When the comparison
1768+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
1769+-- the result of the fold is the result of the parser.
1770+--
1771+-- * Stops - when the comparison fails.
1772+-- * Fails - never.
1773+--
1774+-- >>> :{
1775+-- runGroupsByRolling eq =
1776+-- Stream.fold Fold.toList
1348−-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
1777+-- . Stream.parseMany (Parser.groupByRolling eq Fold.toList)
1778+-- . Stream.fromList
1779+-- :}
1780+--
1781+-- >>> runGroupsByRolling (<) []
1782+-- []
1783+--
1784+-- >>> runGroupsByRolling (<) [1]
1785+-- [Right [1]]
1786+--
1787+-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]
1788+-- [Right [3,5],Right [4],Right [1,2],Right [0]]
1789+--
1790+-- /Pre-release/
1349--
1791--
1350{-# INLINE groupByRolling #-}
1792{-# INLINE groupByRolling #-}
1351groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
1793groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
@@ -1384,6 +1826,16 @@ data GroupByStatePair a s1 s2
1384 | GroupByGroupingPairL !a !s1 !s2
1826 | GroupByGroupingPairL !a !s1 !s2
1385 | GroupByGroupingPairR !a !s1 !s2
1827 | GroupByGroupingPairR !a !s1 !s2
1386
1828
1829+-- | Like 'groupByRolling', but if the predicate is 'True' then collects using
1830+-- the first fold as long as the predicate holds 'True', if the predicate is
1831+-- 'False' collects using the second fold as long as it remains 'False'.
1832+-- Returns 'Left' for the first case and 'Right' for the second case.
1833+--
1834+-- For example, if we want to detect sorted sequences in a stream, both
1835+-- ascending and descending cases we can use 'groupByRollingEither (<=)
1836+-- Fold.toList Fold.toList'.
1837+--
1838+-- /Pre-release/
1387{-# INLINE groupByRollingEither #-}
1839{-# INLINE groupByRollingEither #-}
1388groupByRollingEither :: Monad m =>
1840groupByRollingEither :: Monad m =>
1389 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
1841 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
@@ -1468,13 +1920,26 @@ groupByRollingEither
1468-- XXX use an Unfold instead of a list?
1920-- XXX use an Unfold instead of a list?
1469-- XXX custom combinators for matching list, array and stream?
1921-- XXX custom combinators for matching list, array and stream?
1470-- XXX rename to listBy?
1922-- XXX rename to listBy?
1923+
1924+-- | Match the given sequence of elements using the given comparison function.
1925+-- Returns the original sequence if successful.
1471--
1926--
1472−-- | See 'Streamly.Internal.Data.Parser.streamEqBy'.
1927+-- Definition:
1473--
1928--
1474−-- /Pre-release/
1929+-- >>> listEqBy cmp xs = Parser.streamEqBy cmp (Stream.fromList xs) *> Parser.fromPure xs
1930+--
1931+-- Examples:
1932+--
1933+-- >>> Stream.parse (Parser.listEqBy (==) "string") $ Stream.fromList "string"
1934+-- Right "string"
1935+--
1936+-- >>> Stream.parse (Parser.listEqBy (==) "mismatch") $ Stream.fromList "match"
1937+-- Left (ParseError "streamEqBy: mismtach occurred")
1475--
1938--
1476{-# INLINE listEqBy #-}
1939{-# INLINE listEqBy #-}
1477listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]
1940listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]
1941+listEqBy cmp xs = streamEqBy cmp (D.fromList xs) *> fromPure xs
1942+{-
1478listEqBy cmp str = Parser step initial extract
1943listEqBy cmp str = Parser step initial extract
1479
1944
1480 where
1945 where
@@ -1501,8 +1966,13 @@ listEqBy cmp str = Parser step initial extract
1501 $ Error
1966 $ Error
1502 $ "listEqBy: end of input, yet to match "
1967 $ "listEqBy: end of input, yet to match "
1503 ++ show (length xs) ++ " elements"
1968 ++ show (length xs) ++ " elements"
1969+-}
1504
1970
1505−-- | Like 'listEqBy' but uses a stream instead of a list
1971+-- | Like 'listEqBy' but uses a stream instead of a list and does not return
1972+-- the stream.
1973+--
1974+-- See also: "Streamly.Data.Stream.streamEqBy"
1975+--
1506{-# INLINE streamEqBy #-}
1976{-# INLINE streamEqBy #-}
1507streamEqBy :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()
1977streamEqBy :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()
1508streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
1978streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
@@ -1540,6 +2010,39 @@ streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
1540
2010
1541 extract _ = return $ Error "streamEqBy: end of input"
2011 extract _ = return $ Error "streamEqBy: end of input"
1542
2012
2013+-- Rename to "list".
2014+-- | Match the input sequence with the supplied list and return it if
2015+-- successful.
2016+--
2017+-- >>> listEq = Parser.listEqBy (==)
2018+--
2019+{-# INLINE listEq #-}
2020+listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]
2021+listEq = listEqBy (==)
2022+
2023+-- | Match if the input stream is a subsequence of the argument stream i.e. all
2024+-- the elements of the input stream occur, in order, in the argument stream.
2025+-- The elements do not have to occur consecutively. A sequence is considered a
2026+-- subsequence of itself.
2027+{-# INLINE subsequenceBy #-}
2028+subsequenceBy :: -- Monad m =>
2029+ (a -> a -> Bool) -> Stream m a -> Parser a m ()
2030+subsequenceBy = undefined
2031+
2032+{-
2033+-- Should go in Data.Parser.Regex in streamly package so that it can depend on
2034+-- regex backends.
2035+{-# INLINE regexPosix #-}
2036+regexPosix :: -- Monad m =>
2037+ Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
2038+regexPosix = undefined
2039+
2040+{-# INLINE regexPCRE #-}
2041+regexPCRE :: -- Monad m =>
2042+ Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
2043+regexPCRE = undefined
2044+-}
2045+
1543-------------------------------------------------------------------------------
2046-------------------------------------------------------------------------------
1544-- Transformations on input
2047-- Transformations on input
1545-------------------------------------------------------------------------------
2048-------------------------------------------------------------------------------
@@ -1548,7 +2051,7 @@ streamEqBy cmp (D.Stream sstep state) = Parser step initial extract
1548-- parser can always return a Continue in initial when we feed the fold's
2051-- parser can always return a Continue in initial when we feed the fold's
1549-- initial result to it. We can work this around for postscan by introducing an
2052-- initial result to it. We can work this around for postscan by introducing an
1550-- initial state and calling "initial" only on the first input.
2053-- initial state and calling "initial" only on the first input.
1551−--
2054+
1552-- | Stateful scan on the input of a parser using a Fold.
2055-- | Stateful scan on the input of a parser using a Fold.
1553--
2056--
1554-- /Unimplemented/
2057-- /Unimplemented/
@@ -1709,7 +2212,16 @@ spanByRolling eq f1 f2 =
1709-- nested parsers
2212-- nested parsers
1710-------------------------------------------------------------------------------
2213-------------------------------------------------------------------------------
1711
2214
1712−-- | See 'Streamly.Internal.Data.Parser.takeP'.
2215+-- | Takes at-most @n@ input elements.
2216+--
2217+-- * Stops - when the collecting parser stops.
2218+-- * Fails - when the collecting parser fails.
2219+--
2220+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
2221+-- Right [1,2]
2222+--
2223+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 5 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
2224+-- Left (ParseError "takeEQ: Expecting exactly 5 elements, input terminated on 4")
1713--
2225--
1714-- /Internal/
2226-- /Internal/
1715{-# INLINE takeP #-}
2227{-# INLINE takeP #-}
@@ -1783,9 +2295,7 @@ takeP lim (Parser pstep pinitial pextract) = Parser step initial extract
1783 Error err -> IError err
2295 Error err -> IError err
1784 _ -> error "Bug: takeP invalid state in initial"
2296 _ -> error "Bug: takeP invalid state in initial"
1785
2297
1786−-- | See 'Streamly.Internal.Data.Parser.lookahead'.
2298+-- | Run a parser without consuming the input.
1787−--
1788−-- /Pre-release/
1789--
2299--
1790{-# INLINE lookAhead #-}
2300{-# INLINE lookAhead #-}
1791lookAhead :: Monad m => Parser a m b -> Parser a m b
2301lookAhead :: Monad m => Parser a m b -> Parser a m b
@@ -1822,14 +2332,32 @@ lookAhead (Parser step1 initial1 _) = Parser step initial extract
1822-------------------------------------------------------------------------------
2332-------------------------------------------------------------------------------
1823-- Interleaving
2333-- Interleaving
1824-------------------------------------------------------------------------------
2334-------------------------------------------------------------------------------
2335+--
2336+-- To deinterleave we can chain two parsers one behind the other. The input is
2337+-- given to the first parser and the input definitively rejected by the first
2338+-- parser is given to the second parser.
2339+--
2340+-- We can either have the parsers themselves buffer the input or use the shared
2341+-- global buffer to hold it until none of the parsers need it. When the first
2342+-- parser returns Skip (i.e. rewind) we let the second parser consume the
2343+-- rejected input and when it is done we move the cursor forward to the first
2344+-- parser again. This will require a "move forward" command as well.
2345+--
2346+-- To implement grep we can use three parsers, one to find the pattern, one
2347+-- to store the context behind the pattern and one to store the context in
2348+-- front of the pattern. When a match occurs we need to emit the accumulator of
2349+-- all the three parsers. One parser can count the line numbers to provide the
2350+-- line number info.
1825
2351
1826data DeintercalateState fs sp ss =
2352data DeintercalateState fs sp ss =
1827 DeintercalateL !fs !sp
2353 DeintercalateL !fs !sp
1828 | DeintercalateR !fs !ss !Bool
2354 | DeintercalateR !fs !ss !Bool
1829
2355
1830−-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
2356+-- XXX rename this to intercalate
1831−--
2357+
1832−-- /Internal/
2358+-- | Apply two parsers alternately to an input stream. The input stream is
2359+-- considered an interleaving of two patterns. The two parsers represent the
2360+-- two patterns.
1833--
2361--
1834{-# INLINE deintercalate #-}
2362{-# INLINE deintercalate #-}
1835deintercalate :: Monad m =>
2363deintercalate :: Monad m =>
@@ -1926,8 +2454,13 @@ data SepByState fs sp ss =
1926 SepByInit !fs !sp
2454 SepByInit !fs !sp
1927 | SepBySeparator !fs !ss !Bool
2455 | SepBySeparator !fs !ss !Bool
1928
2456
1929−-- This is a special case of deintercalate and can be easily implemented in
2457+-- | Run the content parser first, when it is done, the separator parser is
1930−-- terms of deintercalate.
2458+-- run, when it is done content parser is run again and so on. If none of the
2459+-- parsers consumes an input then parser returns a failure.
2460+--
2461+-- >>> sepBy p1 p2 sink = Parser.deintercalate p1 p2 (Fold.catLefts sink)
2462+-- >>> sepBy content sep sink = Parser.sepBy1 content sep sink <|> return mempty
2463+--
1931{-# INLINE sepBy #-}
2464{-# INLINE sepBy #-}
1932sepBy :: Monad m =>
2465sepBy :: Monad m =>
1933 Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
2466 Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
@@ -2000,11 +2533,49 @@ sepBy
2000
2533
2001 extract (SepBySeparator fs _ _) = fmap (Done 0) $ fextract fs
2534 extract (SepBySeparator fs _ _) = fmap (Done 0) $ fextract fs
2002
2535
2536+-- XXX This can be implemented using refold, parse one and then continue
2537+-- collecting the rest in that.
2538+
2539+-- | Parse items separated by a separator parsed by the supplied parser. At
2540+-- least one item must be present for the parser to succeed.
2541+--
2542+-- Note that this can go in infinite loop if both the parsers fail on some
2543+-- input. Detection of that would make the implementation more complex.
2544+--
2545+{-# INLINE sepBy1 #-}
2546+sepBy1 :: Monad m =>
2547+ Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
2548+sepBy1 p sep sink = do
2549+ x <- p
2550+ f <- fromEffect $ FL.reduce sink
2551+ f1 <- fromEffect $ FL.snoc f x
2552+ many (sep >> p) f1
2553+
2554+-------------------------------------------------------------------------------
2555+-- Interleaving a collection of parsers
2556+-------------------------------------------------------------------------------
2557+--
2558+-- | Apply a collection of parsers to an input stream in a round robin fashion.
2559+-- Each parser is applied until it stops and then we repeat starting with the
2560+-- the first parser again.
2561+--
2562+-- /Unimplemented/
2563+--
2564+{-# INLINE roundRobin #-}
2565+roundRobin :: -- (Foldable t, Monad m) =>
2566+ t (Parser a m b) -> Fold m b c -> Parser a m c
2567+roundRobin _ps _f = undefined
2568+
2003-------------------------------------------------------------------------------
2569-------------------------------------------------------------------------------
2004-- Sequential Collection
2570-- Sequential Collection
2005-------------------------------------------------------------------------------
2571-------------------------------------------------------------------------------
2572+
2573+-- | @sequence f p@ collects sequential parses of parsers in a
2574+-- serial stream @p@ using the fold @f@. Fails if the input ends or any
2575+-- of the parsers fail.
2576+--
2577+-- /Pre-release/
2006--
2578--
2007−-- | See 'Streamly.Internal.Data.Parser.sequence'.
2008{-# INLINE sequence #-}
2579{-# INLINE sequence #-}
2009sequence :: Monad m =>
2580sequence :: Monad m =>
2010 D.Stream m (Parser a m b) -> Fold m b c -> Parser a m c
2581 D.Stream m (Parser a m b) -> Fold m b c -> Parser a m c
@@ -2089,7 +2660,10 @@ sequence (D.Stream sstep sstate) (Fold fstep finitial fextract) =
2089-------------------------------------------------------------------------------
2660-------------------------------------------------------------------------------
2090
2661
2091{-
2662{-
2092−-- | See 'Streamly.Internal.Data.Parser.choice'.
2663+-- | @choice parsers@ applies the @parsers@ in order and returns the first
2664+-- successful parse.
2665+--
2666+-- This is same as 'asum' but more efficient.
2093--
2667--
2094-- /Broken/
2668-- /Broken/
2095--
2669--
@@ -2102,23 +2676,45 @@ choice = foldl1 shortest
2102-- Sequential Repetition
2676-- Sequential Repetition
2103-------------------------------------------------------------------------------
2677-------------------------------------------------------------------------------
2104
2678
2679+-- | Like 'many' but uses a 'Parser' instead of a 'Fold' to collect the
2680+-- results. Parsing stops or fails if the collecting parser stops or fails.
2681+--
2682+-- /Unimplemented/
2683+--
2105{-# INLINE manyP #-}
2684{-# INLINE manyP #-}
2106manyP :: -- MonadCatch m =>
2685manyP :: -- MonadCatch m =>
2107 Parser a m b -> Parser b m c -> Parser a m c
2686 Parser a m b -> Parser b m c -> Parser a m c
2108manyP _p _f = undefined
2687manyP _p _f = undefined
2109
2688
2110−-- | See 'Streamly.Internal.Data.Parser.many'.
2689+-- | Collect zero or more parses. Apply the supplied parser repeatedly on the
2690+-- input stream and push the parse results to a downstream fold.
2111--
2691--
2112−-- /Pre-release/
2692+-- Stops: when the downstream fold stops or the parser fails.
2693+-- Fails: never, produces zero or more results.
2694+--
2695+-- >>> many = Parser.countBetween 0 maxBound
2696+--
2697+-- Compare with 'Control.Applicative.many'.
2113--
2698--
2114{-# INLINE many #-}
2699{-# INLINE many #-}
2115many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
2700many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
2116many = splitMany
2701many = splitMany
2117-- many = countBetween 0 maxBound
2702-- many = countBetween 0 maxBound
2118
2703
2119−-- | See 'Streamly.Internal.Data.Parser.some'.
2704+-- Note: many1 would perhaps be a better name for this and consistent with
2705+-- other names like takeWhile1. But we retain the name "some" for
2706+-- compatibility.
2707+
2708+-- | Collect one or more parses. Apply the supplied parser repeatedly on the
2709+-- input stream and push the parse results to a downstream fold.
2120--
2710--
2121−-- /Pre-release/
2711+-- Stops: when the downstream fold stops or the parser fails.
2712+-- Fails: if it stops without producing a single result.
2713+--
2714+-- >>> some p f = Parser.manyP p (Parser.takeGE 1 f)
2715+-- >>> some = Parser.countBetween 1 maxBound
2716+--
2717+-- Compare with 'Control.Applicative.some'.
2122--
2718--
2123{-# INLINE some #-}
2719{-# INLINE some #-}
2124some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
2720some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
@@ -2126,7 +2722,11 @@ some = splitSome
2126-- some p f = manyP p (takeGE 1 f)
2722-- some p f = manyP p (takeGE 1 f)
2127-- some = countBetween 1 maxBound
2723-- some = countBetween 1 maxBound
2128
2724
2129−-- | See 'Streamly.Internal.Data.Parser.countBetween'.
2725+-- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of
2726+-- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if
2727+-- the input ends or the parser fails before @m@ results are collected.
2728+--
2729+-- >>> countBetween m n p f = Parser.manyP p (Parser.takeBetween m n f)
2130--
2730--
2131-- /Unimplemented/
2731-- /Unimplemented/
2132--
2732--
@@ -2136,7 +2736,12 @@ countBetween :: -- MonadCatch m =>
2136countBetween _m _n _p = undefined
2736countBetween _m _n _p = undefined
2137-- countBetween m n p f = manyP p (takeBetween m n f)
2737-- countBetween m n p f = manyP p (takeBetween m n f)
2138
2738
2139−-- | See 'Streamly.Internal.Data.Parser.count'.
2739+-- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using
2740+-- the fold @f@. Fails if the input ends or the parser fails before @n@
2741+-- results are collected.
2742+--
2743+-- >>> count n = Parser.countBetween n n
2744+-- >>> count n p f = Parser.manyP p (Parser.takeEQ n f)
2140--
2745--
2141-- /Unimplemented/
2746-- /Unimplemented/
2142--
2747--
@@ -2146,13 +2751,34 @@ count :: -- MonadCatch m =>
2146count n = countBetween n n
2751count n = countBetween n n
2147-- count n p f = manyP p (takeEQ n f)
2752-- count n p f = manyP p (takeEQ n f)
2148
2753
2754+-- | Like 'manyTill' but uses a 'Parser' to collect the results instead of a
2755+-- 'Fold'. Parsing stops or fails if the collecting parser stops or fails.
2756+--
2757+-- We can implemnent parsers like the following using 'manyTillP':
2758+--
2759+-- @
2760+-- countBetweenTill m n f p = manyTillP (takeBetween m n f) p
2761+-- @
2762+--
2763+-- /Unimplemented/
2764+--
2765+{-# INLINE manyTillP #-}
2766+manyTillP :: -- Monad m =>
2767+ Parser a m b -> Parser a m x -> Parser b m c -> Parser a m c
2768+manyTillP _p1 _p2 _f = undefined
2769+ -- D.toParserK $ D.manyTillP (D.fromParserK p1) (D.fromParserK p2) f
2770+
2149data ManyTillState fs sr sl
2771data ManyTillState fs sr sl
2150 = ManyTillR Int fs sr
2772 = ManyTillR Int fs sr
2151 | ManyTillL Int fs sl
2773 | ManyTillL Int fs sl
2152
2774
2153−-- | See 'Streamly.Internal.Data.Parser.manyTill'.
2775+-- | @manyTill chunking test f@ tries the parser @test@ on the input, if @test@
2776+-- fails it backtracks and tries @chunking@, after @chunking@ succeeds @test@ is
2777+-- tried again and so on. The parser stops when @test@ succeeds. The output of
2778+-- @test@ is discarded and the output of @chunking@ is accumulated by the
2779+-- supplied fold. The parser fails if @chunking@ fails.
2154--
2780--
2155−-- /Pre-release/
2781+-- Stops when the fold @f@ stops.
2156--
2782--
2157{-# INLINE manyTill #-}
2783{-# INLINE manyTill #-}
2158manyTill :: Monad m
2784manyTill :: Monad m
@@ -2245,3 +2871,53 @@ manyTill (Parser stepL initialL extractL)
2245 return $ Continue n (ManyTillL 0 fs s)
2871 return $ Continue n (ManyTillL 0 fs s)
2246 Partial _ _ -> error "Partial in extract"
2872 Partial _ _ -> error "Partial in extract"
2247 extract (ManyTillR _ fs _) = fmap (Done 0) $ fextract fs
2873 extract (ManyTillR _ fs _) = fmap (Done 0) $ fextract fs
2874+
2875+-- | @manyThen f collect recover@ repeats the parser @collect@ on the input and
2876+-- collects the output in the supplied fold. If the the parser @collect@ fails,
2877+-- parser @recover@ is run until it stops and then we start repeating the
2878+-- parser @collect@ again. The parser fails if the recovery parser fails.
2879+--
2880+-- For example, this can be used to find a key frame in a video stream after an
2881+-- error.
2882+--
2883+-- /Unimplemented/
2884+--
2885+{-# INLINE manyThen #-}
2886+manyThen :: -- (Foldable t, Monad m) =>
2887+ Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
2888+manyThen _parser _recover _f = undefined
2889+
2890+-------------------------------------------------------------------------------
2891+-- Repeated Alternatives
2892+-------------------------------------------------------------------------------
2893+
2894+-- | Keep trying a parser up to a maximum of @n@ failures. When the parser
2895+-- fails the input consumed till now is dropped and the new instance is tried
2896+-- on the fresh input.
2897+--
2898+-- /Unimplemented/
2899+--
2900+{-# INLINE retryMaxTotal #-}
2901+retryMaxTotal :: -- (Monad m) =>
2902+ Int -> Parser a m b -> Fold m b c -> Parser a m c
2903+retryMaxTotal _n _p _f = undefined
2904+
2905+-- | Like 'retryMaxTotal' but aborts after @n@ successive failures.
2906+--
2907+-- /Unimplemented/
2908+--
2909+{-# INLINE retryMaxSuccessive #-}
2910+retryMaxSuccessive :: -- (Monad m) =>
2911+ Int -> Parser a m b -> Fold m b c -> Parser a m c
2912+retryMaxSuccessive _n _p _f = undefined
2913+
2914+-- | Keep trying a parser until it succeeds. When the parser fails the input
2915+-- consumed till now is dropped and the new instance is tried on the fresh
2916+-- input.
2917+--
2918+-- /Unimplemented/
2919+--
2920+{-# INLINE retry #-}
2921+retry :: -- (Monad m) =>
2922+ Parser a m b -> Parser a m b
2923+retry _p = undefined
core/src/Streamly/Internal/Data/Parser/ParserD/Type.hs+103−18
@@ -214,6 +214,7 @@ import Fusion.Plugin.Types (Fuse(..))
214import Streamly.Internal.Data.Fold.Type (Fold(..), toList)
214import Streamly.Internal.Data.Fold.Type (Fold(..), toList)
215import Streamly.Internal.Data.Tuple.Strict (Tuple3'(..))
215import Streamly.Internal.Data.Tuple.Strict (Tuple3'(..))
216
216
217+import qualified Control.Monad.Fail as Fail
217import qualified Streamly.Internal.Data.Fold.Type as FL
218import qualified Streamly.Internal.Data.Fold.Type as FL
218import qualified Streamly.Internal.Data.Parser.ParserK.Type as K
219import qualified Streamly.Internal.Data.Parser.ParserK.Type as K
219
220
@@ -224,6 +225,7 @@ import Prelude hiding (concatMap, filter)
224-- >>> import Control.Applicative ((<|>))
225-- >>> import Control.Applicative ((<|>))
225-- >>> import Data.Bifunctor (second)
226-- >>> import Data.Bifunctor (second)
226-- >>> import Prelude hiding (concatMap)
227-- >>> import Prelude hiding (concatMap)
228+-- >>> import qualified Streamly.Data.Fold as Fold
227-- >>> import qualified Streamly.Data.Stream as Stream
229-- >>> import qualified Streamly.Data.Stream as Stream
228-- >>> import qualified Streamly.Internal.Data.Stream as Stream (parse)
230-- >>> import qualified Streamly.Internal.Data.Stream as Stream (parse)
229-- >>> import qualified Streamly.Internal.Data.Parser as Parser
231-- >>> import qualified Streamly.Internal.Data.Parser as Parser
@@ -625,9 +627,9 @@ fromParserK parser = Parser step initial extract
625-- Mapping on the output
627-- Mapping on the output
626------------------------------------------------------------------------------
628------------------------------------------------------------------------------
627
629
628−-- | Map a monadic function on the output of a parser.
630+-- | @rmapM f parser@ maps the monadic function @f@ on the output of the parser.
629--
631--
630−-- /Pre-release/
632+-- >>> rmap = fmap
631{-# INLINE rmapM #-}
633{-# INLINE rmapM #-}
632rmapM :: Monad m => (b -> m c) -> Parser a m b -> Parser a m c
634rmapM :: Monad m => (b -> m c) -> Parser a m b -> Parser a m c
633rmapM f (Parser step initial extract) =
635rmapM f (Parser step initial extract) =
@@ -644,17 +646,14 @@ rmapM f (Parser step initial extract) =
644 IError err -> return $ IError err
646 IError err -> return $ IError err
645 step1 s a = step s a >>= mapMStep f
647 step1 s a = step s a >>= mapMStep f
646
648
647−-- | See 'Streamly.Internal.Data.Parser.fromPure'.
649+-- | A parser that always yields a pure value without consuming any input.
648−--
649−-- /Pre-release/
650--
650--
651{-# INLINE_NORMAL fromPure #-}
651{-# INLINE_NORMAL fromPure #-}
652fromPure :: Monad m => b -> Parser a m b
652fromPure :: Monad m => b -> Parser a m b
653fromPure b = Parser undefined (pure $ IDone b) undefined
653fromPure b = Parser undefined (pure $ IDone b) undefined
654
654
655−-- | See 'Streamly.Internal.Data.Parser.fromEffect'.
655+-- | A parser that always yields the result of an effectful action without
656−--
656+-- consuming any input.
657−-- /Pre-release/
658--
657--
659{-# INLINE fromEffect #-}
658{-# INLINE fromEffect #-}
660fromEffect :: Monad m => m b -> Parser a m b
659fromEffect :: Monad m => m b -> Parser a m b
@@ -667,14 +666,52 @@ fromEffect b = Parser undefined (IDone <$> b) undefined
667{-# ANN type SeqParseState Fuse #-}
666{-# ANN type SeqParseState Fuse #-}
668data SeqParseState sl f sr = SeqParseL sl | SeqParseR f sr
667data SeqParseState sl f sr = SeqParseL sl | SeqParseR f sr
669
668
670−-- | See 'Streamly.Internal.Data.Parser.splitWith'.
671−--
672-- Note: this implementation of splitWith is fast because of stream fusion but
669-- Note: this implementation of splitWith is fast because of stream fusion but
673-- has quadratic time complexity, because each composition adds a new branch
670-- has quadratic time complexity, because each composition adds a new branch
674-- that each subsequent parse's input element has to go through, therefore, it
671-- that each subsequent parse's input element has to go through, therefore, it
675-- cannot scale to a large number of compositions. After around 100
672-- cannot scale to a large number of compositions. After around 100
676-- compositions the performance starts dipping rapidly beyond a CPS style
673-- compositions the performance starts dipping rapidly beyond a CPS style
677-- unfused implementation.
674-- unfused implementation.
675+
676+-- | Sequential parser application. Apply two parsers sequentially to an input
677+-- stream. The input is provided to the first parser, when it is done the
678+-- remaining input is provided to the second parser. If both the parsers
679+-- succeed their outputs are combined using the supplied function. The
680+-- operation fails if any of the parsers fail.
681+--
682+-- Note: This is a parsing dual of appending streams using
683+-- 'Streamly.Data.Stream.append', it splits the streams using two parsers and zips
684+-- the results.
685+--
686+-- This implementation is strict in the second argument, therefore, the
687+-- following will fail:
688+--
689+-- >>> Stream.parse (Parser.splitWith const (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]
690+-- *** Exception: Prelude.undefined
691+-- ...
692+--
693+-- Compare with 'Applicative' instance method '<*>'. This implementation allows
694+-- stream fusion but has quadratic complexity. This can fuse with other
695+-- operations and can be faster than 'Applicative' instance for small number
696+-- (less than 8) of compositions.
697+--
698+-- Many combinators can be expressed using @splitWith@ and other parser
699+-- primitives. Some common idioms are described below,
700+--
701+-- @
702+-- span :: (a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
703+-- span pred f1 f2 = splitWith (,) ('takeWhile' pred f1) ('fromFold' f2)
704+-- @
705+--
706+-- @
707+-- spanBy :: (a -> a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
708+-- spanBy eq f1 f2 = splitWith (,) ('groupBy' eq f1) ('fromFold' f2)
709+-- @
710+--
711+-- @
712+-- spanByRolling :: (a -> a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
713+-- spanByRolling eq f1 f2 = splitWith (,) ('groupByRolling' eq f1) ('fromFold' f2)
714+-- @
678--
715--
679-- /Pre-release/
716-- /Pre-release/
680--
717--
@@ -824,7 +861,24 @@ noErrorUnsafeSplitWith func (Parser stepL initialL extractL)
824data SeqAState sl sr = SeqAL sl | SeqAR sr
861data SeqAState sl sr = SeqAL sl | SeqAR sr
825
862
826-- This turns out to be slightly faster than splitWith
863-- This turns out to be slightly faster than splitWith
827−-- | See 'Streamly.Internal.Data.Parser.split_'.
864+
865+-- | Sequential parser application ignoring the output of the first parser.
866+-- Apply two parsers sequentially to an input stream. The input is provided to
867+-- the first parser, when it is done the remaining input is provided to the
868+-- second parser. The output of the parser is the output of the second parser.
869+-- The operation fails if any of the parsers fail.
870+--
871+-- This implementation is strict in the second argument, therefore, the
872+-- following will fail:
873+--
874+-- >>> Stream.parse (Parser.split_ (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]
875+-- *** Exception: Prelude.undefined
876+-- ...
877+--
878+-- Compare with 'Applicative' instance method '*>'. This implementation allows
879+-- stream fusion but has quadratic complexity. This can fuse with other
880+-- operations, and can be faster than 'Applicative' instance for small
881+-- number (less than 8) of compositions.
828--
882--
829-- /Pre-release/
883-- /Pre-release/
830--
884--
@@ -962,7 +1016,20 @@ data AltParseState sl sr = AltParseL Int sl | AltParseR sr
962-- each subsequent alternative's input element has to go through, therefore, it
1016-- each subsequent alternative's input element has to go through, therefore, it
963-- cannot scale to a large number of compositions
1017-- cannot scale to a large number of compositions
964
1018
965−-- | See 'Streamly.Internal.Data.Parser.alt'.
1019+-- | Sequential alternative. Apply the input to the first parser and return the
1020+-- result if the parser succeeds. If the first parser fails then backtrack and
1021+-- apply the same input to the second parser and return the result.
1022+--
1023+-- Note: This implementation is not lazy in the second argument. The following
1024+-- will fail:
1025+--
1026+-- >> Stream.parse (Parser.satisfy (> 0) `Parser.alt` undefined) $ Stream.fromList [1..10]
1027+-- *** Exception: Prelude.undefined
1028+--
1029+-- Compare with ParserK 'Alternative' instance method '<|>'. This
1030+-- implementation allows stream fusion but has quadratic complexity. This can
1031+-- fuse with other operations and can be much faster than 'Alternative'
1032+-- instance for small number (less than 8) of alternatives.
966--
1033--
967-- /Time Complexity:/ O(n^2) where n is the number of compositions.
1034-- /Time Complexity:/ O(n^2) where n is the number of compositions.
968--
1035--
@@ -1263,15 +1330,15 @@ splitSome (Parser step1 initial1 extract1) (Fold fstep finitial fextract) =
1263 assertM(n == cnt)
1330 assertM(n == cnt)
1264 return (Continue n (Tuple3' s1 0 (Right fs)))
1331 return (Continue n (Tuple3' s1 0 (Right fs)))
1265
1332
1266−-- | See 'Streamly.Internal.Data.Parser.die'.
1333+-- | A parser that always fails with an error message without consuming
1267−--
1334+-- any input.
1268−-- /Pre-release/
1269--
1335--
1270{-# INLINE_NORMAL die #-}
1336{-# INLINE_NORMAL die #-}
1271die :: Monad m => String -> Parser a m b
1337die :: Monad m => String -> Parser a m b
1272die err = Parser undefined (pure (IError err)) undefined
1338die err = Parser undefined (pure (IError err)) undefined
1273
1339
1274−-- | See 'Streamly.Internal.Data.Parser.dieM'.
1340+-- | A parser that always fails with an effectful error message and without
1341+-- consuming any input.
1275--
1342--
1276-- /Pre-release/
1343-- /Pre-release/
1277--
1344--
@@ -1292,7 +1359,7 @@ dieM err = Parser undefined (IError <$> err) undefined
1292-- Note: The implementation of '<|>' is not lazy in the second
1359-- Note: The implementation of '<|>' is not lazy in the second
1293-- argument. The following code will fail:
1360-- argument. The following code will fail:
1294--
1361--
1295−-- >>> Stream.parse (ParserD.toParserK $ ParserD.satisfy (> 0) <|> undefined) $ Stream.fromList [1..10]
1362+-- >>> Stream.parse (ParserD.satisfy (> 0) <|> undefined) $ Stream.fromList [1..10]
1296-- *** Exception: Prelude.undefined
1363-- *** Exception: Prelude.undefined
1297-- ...
1364-- ...
1298--
1365--
@@ -1314,7 +1381,7 @@ data ConcatParseState sl m a b =
1314 ConcatParseL sl
1381 ConcatParseL sl
1315 | forall s. ConcatParseR (s -> a -> m (Step s b)) s (s -> m (Step s b))
1382 | forall s. ConcatParseR (s -> a -> m (Step s b)) s (s -> m (Step s b))
1316
1383
1317−-- | See 'Streamly.Internal.Data.Parser.concatMap'.
1384+-- | Map a 'Parser' returning function on the result of a 'Parser'.
1318--
1385--
1319-- /Pre-release/
1386-- /Pre-release/
1320--
1387--
@@ -1469,6 +1536,10 @@ instance Monad m => Monad (Parser a m) where
1469 {-# INLINE (>>) #-}
1536 {-# INLINE (>>) #-}
1470 (>>) = (*>)
1537 (>>) = (*>)
1471
1538
1539+instance Monad m => Fail.MonadFail (Parser a m) where
1540+ {-# INLINE fail #-}
1541+ fail = die
1542+
1472-- | See documentation of 'Streamly.Internal.Data.Parser.ParserK.Type.Parser'.
1543-- | See documentation of 'Streamly.Internal.Data.Parser.ParserK.Type.Parser'.
1473--
1544--
1474instance Monad m => MonadPlus (Parser a m) where
1545instance Monad m => MonadPlus (Parser a m) where
@@ -1486,6 +1557,13 @@ instance (Monad m, MonadIO m) => MonadIO (Parser a m) where
1486-- Mapping on input
1557-- Mapping on input
1487------------------------------------------------------------------------------
1558------------------------------------------------------------------------------
1488
1559
1560+-- | @lmap f parser@ maps the function @f@ on the input of the parser.
1561+--
1562+-- >>> Stream.parse (Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)) (Stream.enumerateFromTo 1 100)
1563+-- Right 338350
1564+--
1565+-- > lmap = Parser.lmapM return
1566+--
1489{-# INLINE lmap #-}
1567{-# INLINE lmap #-}
1490lmap :: (a -> b) -> Parser b m r -> Parser a m r
1568lmap :: (a -> b) -> Parser b m r -> Parser a m r
1491lmap f (Parser step begin done) = Parser step1 begin done
1569lmap f (Parser step begin done) = Parser step1 begin done
@@ -1494,6 +1572,8 @@ lmap f (Parser step begin done) = Parser step1 begin done
1494
1572
1495 step1 x a = step x (f a)
1573 step1 x a = step x (f a)
1496
1574
1575+-- | @lmapM f parser@ maps the monadic function @f@ on the input of the parser.
1576+--
1497{-# INLINE lmapM #-}
1577{-# INLINE lmapM #-}
1498lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r
1578lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r
1499lmapM f (Parser step begin done) = Parser step1 begin done
1579lmapM f (Parser step begin done) = Parser step1 begin done
@@ -1502,6 +1582,11 @@ lmapM f (Parser step begin done) = Parser step1 begin done
1502
1582
1503 step1 x a = f a >>= step x
1583 step1 x a = f a >>= step x
1504
1584
1585+-- | Include only those elements that pass a predicate.
1586+--
1587+-- >>> Stream.parse (Parser.filter (> 5) (Parser.fromFold Fold.sum)) $ Stream.fromList [1..10]
1588+-- Right 40
1589+--
1505{-# INLINE filter #-}
1590{-# INLINE filter #-}
1506filter :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
1591filter :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
1507filter f (Parser step initial extract) = Parser step1 initial extract
1592filter f (Parser step initial extract) = Parser step1 initial extract
core/src/Streamly/Internal/Data/Parser/ParserDK.hsnew file+1544
@@ -0,0 +1,1544 @@
1+{-# OPTIONS_GHC -Wno-orphans #-}
2+
3+-- |
4+-- Module : Streamly.Internal.Data.Parser
5+-- Copyright : (c) 2020 Composewell Technologies
6+-- License : BSD-3-Clause
7+-- Maintainer : streamly@composewell.com
8+-- Stability : pre-release
9+-- Portability : GHC
10+--
11+-- Fast backtracking parsers with stream fusion and native streaming
12+-- capability.
13+--
14+-- 'Applicative' and 'Control.Applicative.Alternative' type class based
15+-- combinators from the
16+-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>
17+-- package can also be used with the 'Parser' type. However, there are two
18+-- important differences between @parser-combinators@ and the equivalent ones
19+-- provided in this module in terms of performance:
20+--
21+-- 1) @parser-combinators@ use plain Haskell lists to collect the results, in a
22+-- strict Monad like IO, the results are necessarily buffered before they can
23+-- be consumed. This may not perform optimally in streaming applications
24+-- processing large amounts of data. Equivalent combinators in this module can
25+-- consume the results of parsing using a 'Fold', thus providing a scalability
26+-- and a composable consumer.
27+--
28+-- 2) Several combinators in this module can be many times faster because of
29+-- stream fusion. For example, 'Streamly.Internal.Data.Parser.many' combinator
30+-- in this module is much faster than the 'Control.Applicative.many' combinator
31+-- of 'Control.Applicative.Alternative' type class.
32+--
33+-- = Errors
34+--
35+-- Failing parsers in this module throw the 'D.ParseError' exception.
36+--
37+-- = Naming
38+--
39+-- As far as possible, try that the names of the combinators in this module are
40+-- consistent with:
41+--
42+-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>
43+-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>
44+-- * <http://hackage.haskell.org/package/megaparsec megaparsec>
45+-- * <http://hackage.haskell.org/package/attoparsec attoparsec>
46+-- * <http://hackage.haskell.org/package/parsec parsec>
47+
48+module Streamly.Internal.Data.Parser
49+ (
50+ K.Parser (..)
51+ , D.ParseError (..)
52+ , D.Step (..)
53+
54+ -- * Downgrade to Fold
55+ , toFold
56+
57+ -- First order parsers
58+ -- * Accumulators
59+ , fromFold
60+ , fromFoldMaybe
61+ , fromPure
62+ , fromEffect
63+ , die
64+ , dieM
65+
66+ -- * Map on input
67+ , lmap
68+ , lmapM
69+ , filter
70+
71+ -- * Map on output
72+ , rmapM
73+
74+ -- * Element parsers
75+ , peek
76+
77+ -- All of these can be expressed in terms of either
78+ , one
79+ , oneEq
80+ , oneNotEq
81+ , oneOf
82+ , noneOf
83+ , eof
84+ , satisfy
85+ , maybe
86+ , either
87+
88+ -- * Sequence parsers (tokenizers)
89+ --
90+ -- | Parsers chained in series, if one parser terminates the composition
91+ -- terminates.
92+
93+ , lookAhead
94+
95+ -- ** By length
96+ -- | Grab a sequence of input elements without inspecting them
97+ , takeBetween
98+ -- , take -- takeBetween 0 n
99+ , takeEQ -- takeBetween n n
100+ , takeGE -- takeBetween n maxBound
101+ , takeP
102+
103+ -- Grab a sequence of input elements by inspecting them
104+ -- ** Exact match
105+ , listEq
106+ , listEqBy
107+ , streamEqBy
108+ , subsequenceBy
109+
110+ -- ** By predicate
111+ , takeWhileP
112+ , takeWhile
113+ -- $takeWhile
114+ , takeWhile1
115+ , dropWhile
116+
117+ -- ** Separators
118+ , takeEndBy
119+ , takeEndBy_
120+ , takeEndByEsc
121+ -- , takeEndByEsc_
122+ , takeStartBy
123+ , takeStartBy_
124+ , takeEitherSepBy
125+ , wordBy
126+ -- , wordByEsc
127+
128+ -- ** By comparing
129+ , groupBy
130+ , groupByRolling
131+ , groupByRollingEither
132+
133+ -- ** Framing
134+ -- , takeFramedBy
135+ , takeFramedBy_
136+ , takeFramedByEsc_
137+ , takeFramedByGeneric
138+ , wordFramedBy
139+ , wordQuotedBy
140+
141+ -- | Unimplemented
142+ --
143+ -- @
144+ -- , prefixOf -- match any prefix of a given string
145+ -- , suffixOf -- match any suffix of a given string
146+ -- , infixOf -- match any substring of a given string
147+ -- @
148+
149+ -- Second order parsers (parsers using parsers)
150+ -- * Binary Combinators
151+
152+ -- ** Sequential Applicative
153+ , splitWith
154+ , split_
155+
156+{-
157+ -- ** Parallel Applicatives
158+ , teeWith
159+ , teeWithFst
160+ , teeWithMin
161+ -- , teeTill -- like manyTill but parallel
162+-}
163+
164+ -- ** Sequential Interleaving
165+ -- Use two folds, run a primary parser, its rejected values go to the
166+ -- secondary parser.
167+ , deintercalate
168+ -- , deintercalatePrefix
169+ -- , deintercalateSuffix
170+
171+ -- *** Special cases
172+ -- | TODO: traditional implmentations of these may be of limited use. For
173+ -- example, consider parsing lines separated by @\\r\\n@. The main parser
174+ -- will have to detect and exclude the sequence @\\r\\n@ anyway so that we
175+ -- can apply the "sep" parser.
176+ --
177+ -- We can instead implement these as special cases of deintercalate.
178+ --
179+ -- @
180+ -- , endBy
181+ -- , sepEndBy
182+ -- , beginBy
183+ -- , sepBeginBy
184+ -- , sepAroundBy
185+ -- @
186+ , sepBy1
187+ , sepBy
188+
189+ -- ** Sequential Alternative
190+ , alt
191+
192+{-
193+ -- ** Parallel Alternatives
194+ , shortest
195+ , longest
196+ -- , fastest
197+-}
198+
199+ -- * N-ary Combinators
200+ -- ** Sequential Collection
201+ , concatSequence
202+ , concatMap
203+
204+ -- ** Sequential Repetition
205+ , count
206+ , countBetween
207+
208+ , manyP
209+ , many
210+ , some
211+ , manyTillP
212+ , manyTill
213+ , manyThen
214+
215+ -- * Distribution
216+ --
217+ -- | A simple and stupid impl would be to just convert the stream to an
218+ -- array and give the array reference to all consumers. The array can be
219+ -- grown on demand by any consumer and truncated when nonbody needs it.
220+
221+ -- ** Distribute to collection
222+ -- ** Distribute to repetition
223+
224+ -- ** Interleaved collection
225+ -- |
226+ --
227+ -- 1. Round robin
228+ -- 2. Priority based
229+ , roundRobin
230+
231+ -- ** Collection of Alternatives
232+ -- | Unimplemented
233+ --
234+ -- @
235+ -- , shortestN
236+ -- , longestN
237+ -- , fastestN -- first N successful in time
238+ -- , choiceN -- first N successful in position
239+ -- @
240+ -- , choice -- first successful in position
241+
242+ -- ** Repeated Alternatives
243+ , retryMaxTotal
244+ , retryMaxSuccessive
245+ , retry
246+
247+ -- * Deprecated
248+ , next
249+ )
250+where
251+
252+import Streamly.Internal.Data.Fold.Type (Fold(..))
253+import Streamly.Internal.Data.Parser.ParserK.Type (Parser)
254+
255+import qualified Data.Foldable as Foldable
256+import qualified Streamly.Internal.Data.Fold.Type as FL
257+import qualified Streamly.Internal.Data.Parser.ParserD as D
258+import qualified Streamly.Internal.Data.Parser.ParserK.Type as K
259+
260+#ifdef USE_STREAMK
261+import Streamly.Internal.Data.StreamK (Stream)
262+import qualified Streamly.Internal.Data.StreamK as Stream
263+#else
264+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
265+import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream
266+#endif
267+
268+import Prelude hiding
269+ ( any, all, dropWhile, take, takeWhile, sequence, concatMap, maybe, either
270+ , filter )
271+
272+--
273+-- $setup
274+-- >>> :m
275+-- >>> import Prelude hiding (any, all, dropWhile, take, takeWhile, sequence, concatMap, maybe, either, filter)
276+-- >>> import Control.Applicative ((<|>))
277+-- >>> import Data.Char (isSpace)
278+-- >>> import qualified Data.Maybe as Maybe
279+-- >>> import qualified Data.Foldable as Foldable
280+-- >>> import qualified Streamly.Data.Stream as Stream
281+-- >>> import qualified Streamly.Internal.Data.Stream as Stream (parse, parseMany)
282+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
283+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
284+
285+-------------------------------------------------------------------------------
286+-- Downgrade a parser to a Fold
287+-------------------------------------------------------------------------------
288+--
289+-- | Make a 'Fold' from a 'Parser'. The fold just throws an exception if the
290+-- parser fails or tries to backtrack.
291+--
292+-- This can be useful in combinators that accept a Fold and we know that a
293+-- Parser cannot fail or failure exception is acceptable as there is no way to
294+-- recover.
295+--
296+-- /Pre-release/
297+--
298+{-# INLINE toFold #-}
299+toFold :: Monad m => Parser a m b -> Fold m a b
300+toFold p = D.toFold $ D.fromParserK p
301+
302+-------------------------------------------------------------------------------
303+-- Upgrade folds to parses
304+-------------------------------------------------------------------------------
305+--
306+-- | Make a 'Parser' from a 'Fold'.
307+--
308+{-# INLINE fromFold #-}
309+fromFold :: Monad m => Fold m a b -> Parser a m b
310+fromFold = D.toParserK . D.fromFold
311+
312+-- | Convert a Maybe returning fold to an error returning parser. The first
313+-- argument is the error message that the parser would return when the fold
314+-- returns Nothing.
315+--
316+-- /Pre-release/
317+--
318+{-# INLINE fromFoldMaybe #-}
319+fromFoldMaybe :: Monad m => String -> Fold m a (Maybe b) -> Parser a m b
320+fromFoldMaybe err = D.toParserK . D.fromFoldMaybe err
321+
322+-------------------------------------------------------------------------------
323+-- Terminating but not failing folds
324+-------------------------------------------------------------------------------
325+--
326+-- This is the dual of stream "fromPure".
327+--
328+-- | A parser that always yields a pure value without consuming any input.
329+--
330+{-# INLINE [3] fromPure #-}
331+fromPure :: Monad m => b -> Parser a m b
332+fromPure = D.toParserK . D.fromPure
333+{-# RULES "fromPure fallback to CPS" [2]
334+ forall a. D.toParserK (D.fromPure a) = K.fromPure a #-}
335+
336+-- This is the dual of stream "fromEffect".
337+--
338+-- | A parser that always yields the result of an effectful action without
339+-- consuming any input.
340+--
341+{-# INLINE fromEffect #-}
342+fromEffect :: Monad m => m b -> Parser a m b
343+fromEffect = K.fromEffect -- D.toParserK . D.fromEffect
344+
345+-- This is the dual of "nil".
346+--
347+-- | A parser that always fails with an error message without consuming
348+-- any input.
349+--
350+{-# INLINE [3] die #-}
351+die :: Monad m => String -> Parser a m b
352+die = D.toParserK . D.die
353+{-# RULES "die fallback to CPS" [2]
354+ forall a. D.toParserK (D.die a) = K.die a #-}
355+
356+-- This is the dual of "nilM".
357+--
358+-- | A parser that always fails with an effectful error message and without
359+-- consuming any input.
360+--
361+-- /Pre-release/
362+--
363+{-# INLINE dieM #-}
364+dieM :: Monad m => m String -> Parser a m b
365+dieM = D.toParserK . D.dieM
366+
367+-------------------------------------------------------------------------------
368+-- Map on input
369+-------------------------------------------------------------------------------
370+
371+-- | @lmap f parser@ maps the function @f@ on the input of the parser.
372+--
373+-- >>> Stream.parse (Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)) (Stream.enumerateFromTo 1 100)
374+-- Right 338350
375+--
376+-- > lmap = Parser.lmapM return
377+--
378+{-# INLINE lmap #-}
379+lmap :: Monad m => (a -> b) -> Parser b m r -> Parser a m r
380+lmap f p = D.toParserK $ D.lmap f $ D.fromParserK p
381+
382+-- | @lmapM f parser@ maps the monadic function @f@ on the input of the parser.
383+--
384+{-# INLINE lmapM #-}
385+lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r
386+lmapM f p = D.toParserK $ D.lmapM f $ D.fromParserK p
387+
388+-- | @rmapM f parser@ maps the monadic function @f@ on the output of the parser.
389+--
390+-- >>> rmap = fmap
391+{-# INLINE rmapM #-}
392+rmapM :: Monad m => (b -> m c) -> Parser a m b -> Parser a m c
393+rmapM f p = D.toParserK $ D.rmapM f $ D.fromParserK p
394+
395+-- | Include only those elements that pass a predicate.
396+--
397+-- >>> Stream.parse (Parser.filter (> 5) (Parser.fromFold Fold.sum)) $ Stream.fromList [1..10]
398+-- Right 40
399+--
400+{-# INLINE filter #-}
401+filter :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
402+filter f p = D.toParserK $ D.filter f $ D.fromParserK p
403+
404+-------------------------------------------------------------------------------
405+-- Failing Parsers
406+-------------------------------------------------------------------------------
407+
408+-- | Peek the head element of a stream, without consuming it. Fails if it
409+-- encounters end of input.
410+--
411+-- >>> Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]
412+-- Right (1,1)
413+--
414+-- @
415+-- peek = lookAhead (satisfy True)
416+-- @
417+--
418+{-# INLINE peek #-}
419+peek :: Monad m => Parser a m a
420+peek = D.toParserK D.peek
421+
422+-- | Succeeds if we are at the end of input, fails otherwise.
423+--
424+-- >>> Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]
425+-- Right (1,())
426+--
427+{-# INLINE eof #-}
428+eof :: Monad m => Parser a m ()
429+eof = D.toParserK D.eof
430+
431+-- | Returns the next element if it passes the predicate, fails otherwise.
432+--
433+-- >>> Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]
434+-- Right 1
435+--
436+-- >>> toMaybe f x = if f x then Just x else Nothing
437+-- >>> satisfy f = Parser.maybe (toMaybe f)
438+--
439+{-# INLINE satisfy #-}
440+satisfy :: Monad m => (a -> Bool) -> Parser a m a
441+satisfy = D.toParserK . D.satisfy
442+
443+-- | Consume one element from the head of the stream. Fails if it encounters
444+-- end of input.
445+--
446+-- >>> one = Parser.satisfy $ const True
447+--
448+{-# INLINE one #-}
449+one :: Monad m => Parser a m a
450+one = satisfy $ const True
451+
452+-- Alternate names: "only", "onlyThis".
453+
454+-- | Match a specific element.
455+--
456+-- >>> oneEq x = Parser.satisfy (== x)
457+--
458+{-# INLINE oneEq #-}
459+oneEq :: (Monad m, Eq a) => a -> Parser a m a
460+oneEq x = satisfy (== x)
461+
462+-- Alternate names: "exclude", "notThis".
463+
464+-- | Match anything other than the supplied element.
465+--
466+-- >>> oneNotEq x = Parser.satisfy (/= x)
467+--
468+{-# INLINE oneNotEq #-}
469+oneNotEq :: (Monad m, Eq a) => a -> Parser a m a
470+oneNotEq x = satisfy (/= x)
471+
472+-- | Match any one of the elements in the supplied list.
473+--
474+-- >>> oneOf xs = Parser.satisfy (`Foldable.elem` xs)
475+--
476+-- When performance matters a pattern matching predicate could be more
477+-- efficient than a 'Foldable' datatype:
478+--
479+-- @
480+-- let p x =
481+-- case x of
482+-- 'a' -> True
483+-- 'e' -> True
484+-- _ -> False
485+-- in satisfy p
486+-- @
487+--
488+-- GHC may use a binary search instead of linear search in the list.
489+-- Alternatively, you can also use an array instead of list for storage and
490+-- search.
491+--
492+{-# INLINE oneOf #-}
493+oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
494+oneOf xs = satisfy (`Foldable.elem` xs)
495+
496+-- | See performance notes in 'oneOf'.
497+--
498+-- >>> noneOf xs = Parser.satisfy (`Foldable.notElem` xs)
499+--
500+{-# INLINE noneOf #-}
501+noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
502+noneOf xs = satisfy (`Foldable.notElem` xs)
503+
504+-- | Return the next element of the input. Returns 'Nothing'
505+-- on end of input. Also known as 'head'.
506+--
507+-- /Pre-release/
508+--
509+{-# DEPRECATED next "Please use \"fromFold Fold.one\" instead" #-}
510+{-# INLINE next #-}
511+next :: Monad m => Parser a m (Maybe a)
512+next = D.toParserK D.next
513+
514+-- | Map a 'Maybe' returning function on the next element in the stream. The
515+-- parser fails if the function returns 'Nothing' otherwise returns the 'Just'
516+-- value.
517+--
518+-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right
519+-- >>> maybe f = Parser.either (toEither . f)
520+--
521+-- >>> maybe f = Parser.fromFoldMaybe "maybe: predicate failed" (Fold.maybe f)
522+--
523+-- /Pre-release/
524+--
525+{-# INLINE maybe #-}
526+maybe :: Monad m => (a -> Maybe b) -> Parser a m b
527+maybe = D.toParserK . D.maybe
528+
529+-- | Map an 'Either' returning function on the next element in the stream. If
530+-- the function returns 'Left err', the parser fails with the error message
531+-- @err@ otherwise returns the 'Right' value.
532+--
533+-- /Pre-release/
534+--
535+{-# INLINE either #-}
536+either :: Monad m => (a -> Either String b) -> Parser a m b
537+either = D.toParserK . D.either
538+
539+-------------------------------------------------------------------------------
540+-- Taking elements
541+-------------------------------------------------------------------------------
542+
543+-- | @takeBetween m n@ takes a minimum of @m@ and a maximum of @n@ input
544+-- elements and folds them using the supplied fold.
545+--
546+-- Stops after @n@ elements.
547+-- Fails if the stream ends before @m@ elements could be taken.
548+--
549+-- Examples: -
550+--
551+-- @
552+-- >>> :{
553+-- takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)
554+-- where prsr = Parser.takeBetween low high Fold.toList
555+-- :}
556+--
557+-- @
558+--
559+-- >>> takeBetween' 2 4 [1, 2, 3, 4, 5]
560+-- Right [1,2,3,4]
561+--
562+-- >>> takeBetween' 2 4 [1, 2]
563+-- Right [1,2]
564+--
565+-- >>> takeBetween' 2 4 [1]
566+-- Left (ParseError "takeBetween: Expecting alteast 2 elements, got 1")
567+--
568+-- >>> takeBetween' 0 0 [1, 2]
569+-- Right []
570+--
571+-- >>> takeBetween' 0 1 []
572+-- Right []
573+--
574+-- @takeBetween@ is the most general take operation, other take operations can
575+-- be defined in terms of takeBetween. For example:
576+--
577+-- >>> take n = Parser.takeBetween 0 n
578+-- >>> takeEQ n = Parser.takeBetween n n
579+-- >>> takeGE n = Parser.takeBetween n maxBound
580+--
581+-- /Pre-release/
582+--
583+{-# INLINE takeBetween #-}
584+takeBetween :: Monad m =>
585+ Int -> Int -> Fold m a b -> Parser a m b
586+takeBetween m n = D.toParserK . D.takeBetween m n
587+
588+-- | Stops after taking exactly @n@ input elements.
589+--
590+-- * Stops - after consuming @n@ elements.
591+-- * Fails - if the stream or the collecting fold ends before it can collect
592+-- exactly @n@ elements.
593+--
594+-- >>> Stream.parse (Parser.takeEQ 4 Fold.toList) $ Stream.fromList [1,0,1]
595+-- Left (ParseError "takeEQ: Expecting exactly 4 elements, input terminated on 3")
596+--
597+{-# INLINE takeEQ #-}
598+takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b
599+takeEQ n = D.toParserK . D.takeEQ n
600+
601+-- | Take at least @n@ input elements, but can collect more.
602+--
603+-- * Stops - when the collecting fold stops.
604+-- * Fails - if the stream or the collecting fold ends before producing @n@
605+-- elements.
606+--
607+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1]
608+-- Left (ParseError "takeGE: Expecting at least 4 elements, input terminated on 3")
609+--
610+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]
611+-- Right [1,0,1,0,1]
612+--
613+-- /Pre-release/
614+--
615+{-# INLINE takeGE #-}
616+takeGE :: Monad m => Int -> Fold m a b -> Parser a m b
617+takeGE n = D.toParserK . D.takeGE n
618+
619+-------------------------------------------------------------------------------
620+-- Take until a condition
621+-------------------------------------------------------------------------------
622+
623+-- $takeWhile
624+-- Note: This is called @takeWhileP@ and @munch@ in some parser libraries.
625+
626+-- XXX We should perhaps use only takeWhileP and rename it to takeWhile.
627+--
628+-- | Like 'takeWhile' but uses a 'Parser' instead of a 'Fold' to collect the
629+-- input. The combinator stops when the condition fails or if the collecting
630+-- parser stops.
631+--
632+-- Other interesting parsers can be implemented in terms of this parser:
633+--
634+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
635+-- >>> takeWhileBetween cond m n p = Parser.takeWhileP cond (Parser.takeBetween m n p)
636+--
637+-- Stops: when the condition fails or the collecting parser stops.
638+-- Fails: when the collecting parser fails.
639+--
640+-- /Pre-release/
641+--
642+{-# INLINE takeWhileP #-}
643+takeWhileP :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
644+takeWhileP cond p = D.toParserK $ D.takeWhileP cond (D.fromParserK p)
645+
646+-- | Collect stream elements until an element fails the predicate. The element
647+-- on which the predicate fails is returned back to the input stream.
648+--
649+-- * Stops - when the predicate fails or the collecting fold stops.
650+-- * Fails - never.
651+--
652+-- >>> Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]
653+-- Right [0,0]
654+--
655+-- >>> takeWhile cond f = Parser.takeWhileP cond (Parser.fromFold f)
656+--
657+-- We can implement a @breakOn@ using 'takeWhile':
658+--
659+-- @
660+-- breakOn p = takeWhile (not p)
661+-- @
662+--
663+{-# INLINE takeWhile #-}
664+takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
665+takeWhile cond = D.toParserK . D.takeWhile cond
666+-- takeWhile cond f = takeWhileP cond (fromFold f)
667+
668+-- | Like 'takeWhile' but takes at least one element otherwise fails.
669+--
670+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)
671+--
672+{-# INLINE takeWhile1 #-}
673+takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
674+takeWhile1 cond = D.toParserK . D.takeWhile1 cond
675+-- takeWhile1 cond f = takeWhileP cond (takeBetween 1 maxBound f)
676+
677+-- | Drain the input as long as the predicate succeeds, running the effects and
678+-- discarding the results.
679+--
680+-- This is also called @skipWhile@ in some parsing libraries.
681+--
682+-- >>> dropWhile p = Parser.takeWhile p Fold.drain
683+--
684+{-# INLINE dropWhile #-}
685+dropWhile :: Monad m => (a -> Bool) -> Parser a m ()
686+dropWhile p = takeWhile p FL.drain
687+
688+-------------------------------------------------------------------------------
689+-- Separators
690+-------------------------------------------------------------------------------
691+
692+-- XXX We can remove Maybe from esc
693+{-# INLINE takeFramedByGeneric #-}
694+takeFramedByGeneric :: Monad m =>
695+ Maybe (a -> Bool)
696+ -> Maybe (a -> Bool)
697+ -> Maybe (a -> Bool)
698+ -> Fold m a b
699+ -> Parser a m b
700+takeFramedByGeneric esc begin end f =
701+ D.toParserK $ D.takeFramedByGeneric esc begin end f
702+
703+-- | @takeEndBy cond parser@ parses a token that ends by a separator chosen by
704+-- the supplied predicate. The separator is also taken with the token.
705+--
706+-- This can be combined with other parsers to implement other interesting
707+-- parsers as follows:
708+--
709+-- >>> takeEndByLE cond n p = Parser.takeEndBy cond (Parser.fromFold $ Fold.take n p)
710+-- >>> takeEndByBetween cond m n p = Parser.takeEndBy cond (Parser.takeBetween m n p)
711+--
712+-- >>> takeEndBy = Parser.takeEndByEsc (const False)
713+--
714+-- See also "Streamly.Data.Fold.takeEndBy". Unlike the fold, the collecting
715+-- parser in the takeEndBy parser can decide whether to fail or not if the
716+-- stream does not end with separator.
717+--
718+-- /Pre-release/
719+--
720+{-# INLINE takeEndBy #-}
721+takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
722+takeEndBy cond = D.toParserK . D.takeEndBy cond . D.fromParserK
723+-- takeEndBy = takeEndByEsc (const False)
724+
725+-- | Like 'takeEndBy' but the separator is dropped.
726+--
727+-- See also "Streamly.Data.Fold.takeEndBy_".
728+--
729+-- /Pre-release/
730+--
731+{-# INLINE takeEndBy_ #-}
732+takeEndBy_ :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
733+takeEndBy_ cond = D.toParserK . D.takeEndBy_ cond . D.fromParserK
734+{-
735+takeEndBy_ isEnd p =
736+ takeFramedByGeneric Nothing Nothing (Just isEnd) (toFold p)
737+-}
738+
739+-- | Take either the separator or the token. Separator is a Left value and
740+-- token is Right value.
741+--
742+-- /Unimplemented/
743+{-# INLINE takeEitherSepBy #-}
744+takeEitherSepBy :: -- Monad m =>
745+ (a -> Bool) -> Fold m (Either a b) c -> Parser a m c
746+takeEitherSepBy _cond = undefined -- D.toParserK . D.takeEitherSepBy cond
747+
748+-- | Parse a token that starts with an element chosen by the predicate. The
749+-- parser fails if the input does not start with the selected element.
750+--
751+-- * Stops - when the predicate succeeds in non-leading position.
752+-- * Fails - when the predicate fails in the leading position.
753+--
754+-- >>> splitWithPrefix p f = Stream.parseMany (Parser.takeStartBy p f)
755+--
756+-- Examples: -
757+--
758+-- >>> p = Parser.takeStartBy (== ',') Fold.toList
759+-- >>> leadingComma = Stream.parse p . Stream.fromList
760+-- >>> leadingComma "a,b"
761+-- Left (ParseError "takeStartBy: missing frame start")
762+-- ...
763+-- >>> leadingComma ",,"
764+-- Right ","
765+-- >>> leadingComma ",a,b"
766+-- Right ",a"
767+-- >>> leadingComma ""
768+-- Right ""
769+--
770+-- /Pre-release/
771+--
772+{-# INLINE takeStartBy #-}
773+takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
774+takeStartBy cond = D.toParserK . D.takeStartBy cond
775+
776+-- | Like 'takeStartBy' but drops the separator.
777+--
778+-- >>> takeStartBy_ isBegin = Parser.takeFramedByGeneric Nothing (Just isBegin) Nothing
779+--
780+{-# INLINE takeStartBy_ #-}
781+takeStartBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
782+takeStartBy_ isBegin = takeFramedByGeneric Nothing (Just isBegin) Nothing
783+
784+-------------------------------------------------------------------------------
785+-- Quoting and Escaping
786+-------------------------------------------------------------------------------
787+
788+-- | Like 'takeEndBy' but the separator elements can be escaped using an
789+-- escape char determined by the first predicate. The escape characters are
790+-- removed.
791+--
792+-- /pre-release/
793+{-# INLINE takeEndByEsc #-}
794+takeEndByEsc :: Monad m =>
795+ (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b
796+takeEndByEsc isEsc isEnd p =
797+ D.toParserK $ D.takeEndByEsc isEsc isEnd (D.fromParserK p)
798+
799+-- | @takeFramedByEsc_ isEsc isBegin isEnd fold@ parses a token framed using a
800+-- begin and end predicate, and an escape character. The frame begin and end
801+-- characters lose their special meaning if preceded by the escape character.
802+--
803+-- Nested frames are allowed if begin and end markers are different, nested
804+-- frames must be balanced unless escaped, nested frame markers are emitted as
805+-- it is.
806+--
807+-- For example,
808+--
809+-- >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList
810+-- >>> Stream.parse p $ Stream.fromList "{hello}"
811+-- Right "hello"
812+-- >>> Stream.parse p $ Stream.fromList "{hello {world}}"
813+-- Right "hello {world}"
814+-- >>> Stream.parse p $ Stream.fromList "{hello \\{world}"
815+-- Right "hello {world"
816+-- >>> Stream.parse p $ Stream.fromList "{hello {world}"
817+-- Left (ParseError "takeFramedByEsc_: missing frame end")
818+--
819+-- /Pre-release/
820+{-# INLINE takeFramedByEsc_ #-}
821+takeFramedByEsc_ :: Monad m =>
822+ (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
823+takeFramedByEsc_ isEsc isBegin isEnd f =
824+ D.toParserK $ D.takeFramedByEsc_ isEsc isBegin isEnd f
825+-- takeEndByEsc_ isEsc isEnd p =
826+-- takeFramedByGeneric (Just isEsc) Nothing (Just isEnd) (toFold p)
827+
828+-- | @takeFramedBy_ isBegin isEnd fold@ parses a token framed by a begin and an
829+-- end predicate.
830+--
831+-- >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)
832+--
833+{-# INLINE takeFramedBy_ #-}
834+takeFramedBy_ :: Monad m =>
835+ (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
836+takeFramedBy_ isBegin isEnd f = D.toParserK $ D.takeFramedBy_ isBegin isEnd f
837+-- takeFramedBy_ isBegin isEnd =
838+-- takeFramedByGeneric (Just (const False)) (Just isBegin) (Just isEnd)
839+
840+-------------------------------------------------------------------------------
841+-- Grouping and words
842+-------------------------------------------------------------------------------
843+
844+-- Note we can also get words using something like:
845+-- sepBy FL.toList (takeWhile (not . p) Fold.toList) (dropWhile p)
846+--
847+-- But that won't be as efficient and ergonomic.
848+--
849+-- | Like 'splitOn' but strips leading, trailing, and repeated separators.
850+-- Therefore, @".a..b."@ having '.' as the separator would be parsed as
851+-- @["a","b"]@. In other words, its like parsing words from whitespace
852+-- separated text.
853+--
854+-- * Stops - when it finds a word separator after a non-word element
855+-- * Fails - never.
856+--
857+-- >>> wordBy = Parser.wordFramedBy (const False) (const False) (const False)
858+--
859+-- @
860+-- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)
861+-- @
862+--
863+{-# INLINE wordBy #-}
864+wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
865+wordBy f = D.toParserK . D.wordBy f
866+
867+-- | Like 'wordBy' but treats anything inside a pair of quotes as a single
868+-- word, the quotes can be escaped by an escape character. Recursive quotes
869+-- are possible if quote begin and end characters are different, quotes must be
870+-- balanced. Outermost quotes are stripped.
871+--
872+-- >>> braces = Parser.wordFramedBy (== '\\') (== '{') (== '}') isSpace Fold.toList
873+-- >>> Stream.parse braces $ Stream.fromList "{ab} cd"
874+-- Right "ab"
875+-- >>> Stream.parse braces $ Stream.fromList "{ab}{cd}"
876+-- Right "abcd"
877+-- >>> Stream.parse braces $ Stream.fromList "a{b} cd"
878+-- Right "ab"
879+-- >>> Stream.parse braces $ Stream.fromList "a{{b}} cd"
880+-- Right "a{b}"
881+--
882+-- >>> quotes = Parser.wordFramedBy (== '\\') (== '"') (== '"') isSpace Fold.toList
883+-- >>> Stream.parse quotes $ Stream.fromList "\"a\"\"b\""
884+-- Right "ab"
885+--
886+{-# INLINE wordFramedBy #-}
887+wordFramedBy :: Monad m =>
888+ (a -> Bool) -- ^ Escape
889+ -> (a -> Bool) -- ^ left quote
890+ -> (a -> Bool) -- ^ right quote
891+ -> (a -> Bool) -- ^ word seperator
892+ -> Fold m a b
893+ -> Parser a m b
894+wordFramedBy isEsc isBegin isEnd isSpc =
895+ D.toParserK . D.wordFramedBy isEsc isBegin isEnd isSpc
896+
897+-- | Like 'wordFramedBy' but the closing quote is determined by the opening
898+-- quote. The first quote begin starts a quote that is closed by its
899+-- corresponding closing quote.
900+--
901+-- 'wordFramedBy' and 'wordQuotedBy' both allow multiple quote characters based
902+-- on the predicates but 'wordQuotedBy' always fixes the quote at the first
903+-- occurrence and then it is closed only by the corresponding closing quote.
904+-- Therefore, other quoting characters can be embedded inside it as normal
905+-- characters. On the other hand, 'wordFramedBy' would close the quote as soon
906+-- as it encounters any of the closing quotes.
907+--
908+-- >>> q = (`elem` ['"', '\''])
909+-- >>> p kQ = Parser.wordQuotedBy kQ (== '\\') q q id isSpace Fold.toList
910+--
911+-- >>> Stream.parse (p False) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
912+-- Right "ab'c;d\"ef"
913+--
914+-- >>> Stream.parse (p True) $ Stream.fromList "a\"b'c\";'d\"e'f ghi"
915+-- Right "a\"b'c\";'d\"e'f"
916+--
917+{-# INLINE wordQuotedBy #-}
918+wordQuotedBy :: (Monad m, Eq a) =>
919+ Bool -- ^ keep the quotes in the output
920+ -> (a -> Bool) -- ^ Escape
921+ -> (a -> Bool) -- ^ left quote
922+ -> (a -> Bool) -- ^ right quote
923+ -> (a -> a) -- ^ get right quote from left quote
924+ -> (a -> Bool) -- ^ word seperator
925+ -> Fold m a b
926+ -> Parser a m b
927+wordQuotedBy keepQuotes isEsc isBegin isEnd toRight isSpc =
928+ D.toParserK . D.wordQuotedBy keepQuotes isEsc isBegin isEnd toRight isSpc
929+
930+-- | Given an input stream @[a,b,c,...]@ and a comparison function @cmp@, the
931+-- parser assigns the element @a@ to the first group, then if @a \`cmp` b@ is
932+-- 'True' @b@ is also assigned to the same group. If @a \`cmp` c@ is 'True'
933+-- then @c@ is also assigned to the same group and so on. When the comparison
934+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
935+-- the result of the fold is the result of the parser.
936+--
937+-- * Stops - when the comparison fails.
938+-- * Fails - never.
939+--
940+-- >>> :{
941+-- runGroupsBy eq =
942+-- Stream.fold Fold.toList
943+-- . Stream.parseMany (Parser.groupBy eq Fold.toList)
944+-- . Stream.fromList
945+-- :}
946+--
947+-- >>> runGroupsBy (<) []
948+-- []
949+--
950+-- >>> runGroupsBy (<) [1]
951+-- [Right [1]]
952+--
953+-- >>> runGroupsBy (<) [3, 5, 4, 1, 2, 0]
954+-- [Right [3,5,4],Right [1,2],Right [0]]
955+--
956+{-# INLINE groupBy #-}
957+groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
958+groupBy eq = D.toParserK . D.groupBy eq
959+
960+-- | Unlike 'groupBy' this combinator performs a rolling comparison of two
961+-- successive elements in the input stream. Assuming the input stream
962+-- is @[a,b,c,...]@ and the comparison function is @cmp@, the parser
963+-- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is
964+-- 'True' @b@ is also assigned to the same group. If @b \`cmp` c@ is 'True'
965+-- then @c@ is also assigned to the same group and so on. When the comparison
966+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
967+-- the result of the fold is the result of the parser.
968+--
969+-- * Stops - when the comparison fails.
970+-- * Fails - never.
971+--
972+-- >>> :{
973+-- runGroupsByRolling eq =
974+-- Stream.fold Fold.toList
975+-- . Stream.parseMany (Parser.groupByRolling eq Fold.toList)
976+-- . Stream.fromList
977+-- :}
978+--
979+-- >>> runGroupsByRolling (<) []
980+-- []
981+--
982+-- >>> runGroupsByRolling (<) [1]
983+-- [Right [1]]
984+--
985+-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]
986+-- [Right [3,5],Right [4],Right [1,2],Right [0]]
987+--
988+-- /Pre-release/
989+--
990+{-# INLINE groupByRolling #-}
991+groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
992+groupByRolling eq = D.toParserK . D.groupByRolling eq
993+
994+-- | Like 'groupByRolling', but if the predicate is 'True' then collects using
995+-- the first fold as long as the predicate holds 'True', if the predicate is
996+-- 'False' collects using the second fold as long as it remains 'False'.
997+-- Returns 'Left' for the first case and 'Right' for the second case.
998+--
999+-- For example, if we want to detect sorted sequences in a stream, both
1000+-- ascending and descending cases we can use 'groupByRollingEither (<=)
1001+-- Fold.toList Fold.toList'.
1002+--
1003+-- /Pre-release/
1004+{-# INLINE groupByRollingEither #-}
1005+groupByRollingEither :: Monad m =>
1006+ (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
1007+groupByRollingEither eq f1 = D.toParserK . D.groupByRollingEither eq f1
1008+
1009+-- | Like 'listEqBy' but uses a stream instead of a list and does not return
1010+-- the stream.
1011+--
1012+-- See also: "Streamly.Data.Stream.streamEqBy"
1013+--
1014+{-# INLINE streamEqBy #-}
1015+streamEqBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Parser a m ()
1016+streamEqBy cmp = D.toParserK . D.streamEqBy cmp
1017+
1018+-- | Match the given sequence of elements using the given comparison function.
1019+-- Returns the original sequence if successful.
1020+--
1021+-- Definition:
1022+--
1023+-- >>> listEqBy cmp xs = Parser.streamEqBy cmp (Stream.fromList xs) *> Parser.fromPure xs
1024+--
1025+-- Examples:
1026+--
1027+-- >>> Stream.parse (Parser.listEqBy (==) "string") $ Stream.fromList "string"
1028+-- Right "string"
1029+--
1030+-- >>> Stream.parse (Parser.listEqBy (==) "mismatch") $ Stream.fromList "match"
1031+-- Left (ParseError "streamEqBy: mismtach occurred")
1032+--
1033+{-# INLINE listEqBy #-}
1034+listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]
1035+-- listEqBy cmp xs = D.toParserK (D.listEqBy cmp xs)
1036+listEqBy cmp xs = streamEqBy cmp (Stream.fromList xs) *> fromPure xs
1037+
1038+-- Rename to "list".
1039+-- | Match the input sequence with the supplied list and return it if
1040+-- successful.
1041+--
1042+-- >>> listEq = Parser.listEqBy (==)
1043+--
1044+{-# INLINE listEq #-}
1045+listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]
1046+listEq = listEqBy (==)
1047+
1048+-- | Match if the input stream is a subsequence of the argument stream i.e. all
1049+-- the elements of the input stream occur, in order, in the argument stream.
1050+-- The elements do not have to occur consecutively. A sequence is considered a
1051+-- subsequence of itself.
1052+{-# INLINE subsequenceBy #-}
1053+subsequenceBy :: -- Monad m =>
1054+ (a -> a -> Bool) -> Stream m a -> Parser a m ()
1055+subsequenceBy = undefined
1056+
1057+{-
1058+-- Should go in Data.Parser.Regex in streamly package so that it can depend on
1059+-- regex backends.
1060+{-# INLINE regexPosix #-}
1061+regexPosix :: -- Monad m =>
1062+ Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
1063+regexPosix = undefined
1064+
1065+{-# INLINE regexPCRE #-}
1066+regexPCRE :: -- Monad m =>
1067+ Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))
1068+regexPCRE = undefined
1069+-}
1070+
1071+-------------------------------------------------------------------------------
1072+-- nested parsers
1073+-------------------------------------------------------------------------------
1074+
1075+-- | Sequential parser application. Apply two parsers sequentially to an input
1076+-- stream. The input is provided to the first parser, when it is done the
1077+-- remaining input is provided to the second parser. If both the parsers
1078+-- succeed their outputs are combined using the supplied function. The
1079+-- operation fails if any of the parsers fail.
1080+--
1081+-- Note: This is a parsing dual of appending streams using
1082+-- 'Streamly.Data.Stream.append', it splits the streams using two parsers and zips
1083+-- the results.
1084+--
1085+-- This implementation is strict in the second argument, therefore, the
1086+-- following will fail:
1087+--
1088+-- >>> Stream.parse (Parser.splitWith const (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]
1089+-- *** Exception: Prelude.undefined
1090+-- ...
1091+--
1092+-- Compare with 'Applicative' instance method '<*>'. This implementation allows
1093+-- stream fusion but has quadratic complexity. This can fuse with other
1094+-- operations and can be faster than 'Applicative' instance for small number
1095+-- (less than 8) of compositions.
1096+--
1097+-- Many combinators can be expressed using @splitWith@ and other parser
1098+-- primitives. Some common idioms are described below,
1099+--
1100+-- @
1101+-- span :: (a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
1102+-- span pred f1 f2 = splitWith (,) ('takeWhile' pred f1) ('fromFold' f2)
1103+-- @
1104+--
1105+-- @
1106+-- spanBy :: (a -> a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
1107+-- spanBy eq f1 f2 = splitWith (,) ('groupBy' eq f1) ('fromFold' f2)
1108+-- @
1109+--
1110+-- @
1111+-- spanByRolling :: (a -> a -> Bool) -> Fold m a b -> Fold m a b -> Parser a m b
1112+-- spanByRolling eq f1 f2 = splitWith (,) ('groupByRolling' eq f1) ('fromFold' f2)
1113+-- @
1114+--
1115+-- /Pre-release/
1116+--
1117+{-# INLINE splitWith #-}
1118+splitWith :: Monad m
1119+ => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
1120+splitWith f p1 p2 =
1121+ D.toParserK $ D.splitWith f (D.fromParserK p1) (D.fromParserK p2)
1122+
1123+-- | Sequential parser application ignoring the output of the first parser.
1124+-- Apply two parsers sequentially to an input stream. The input is provided to
1125+-- the first parser, when it is done the remaining input is provided to the
1126+-- second parser. The output of the parser is the output of the second parser.
1127+-- The operation fails if any of the parsers fail.
1128+--
1129+-- This implementation is strict in the second argument, therefore, the
1130+-- following will fail:
1131+--
1132+-- >>> Stream.parse (Parser.split_ (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]
1133+-- *** Exception: Prelude.undefined
1134+-- ...
1135+--
1136+-- Compare with 'Applicative' instance method '*>'. This implementation allows
1137+-- stream fusion but has quadratic complexity. This can fuse with other
1138+-- operations, and can be faster than 'Applicative' instance for small
1139+-- number (less than 8) of compositions.
1140+--
1141+-- /Pre-release/
1142+--
1143+{-# INLINE split_ #-}
1144+split_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b
1145+split_ p1 p2 = D.toParserK $ D.split_ (D.fromParserK p1) (D.fromParserK p2)
1146+
1147+{-
1148+-- | @teeWith f p1 p2@ distributes its input to both @p1@ and @p2@ until both
1149+-- of them succeed or anyone of them fails and combines their output using @f@.
1150+-- The parser succeeds if both the parsers succeed.
1151+--
1152+-- /Pre-release/
1153+--
1154+{-# INLINE teeWith #-}
1155+teeWith :: Monad m
1156+ => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
1157+teeWith f p1 p2 =
1158+ D.toParserK $ D.teeWith f (D.fromParserK p1) (D.fromParserK p2)
1159+
1160+-- | Like 'teeWith' but ends parsing and zips the results, if available,
1161+-- whenever the first parser ends.
1162+--
1163+-- /Pre-release/
1164+--
1165+{-# INLINE teeWithFst #-}
1166+teeWithFst :: Monad m
1167+ => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
1168+teeWithFst f p1 p2 =
1169+ D.toParserK $ D.teeWithFst f (D.fromParserK p1) (D.fromParserK p2)
1170+
1171+-- | Like 'teeWith' but ends parsing and zips the results, if available,
1172+-- whenever any of the parsers ends or fails.
1173+--
1174+-- /Unimplemented/
1175+--
1176+{-# INLINE teeWithMin #-}
1177+teeWithMin :: Monad m
1178+ => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
1179+teeWithMin f p1 p2 =
1180+ D.toParserK $ D.teeWithMin f (D.fromParserK p1) (D.fromParserK p2)
1181+-}
1182+
1183+-- | Sequential alternative. Apply the input to the first parser and return the
1184+-- result if the parser succeeds. If the first parser fails then backtrack and
1185+-- apply the same input to the second parser and return the result.
1186+--
1187+-- Note: This implementation is not lazy in the second argument. The following
1188+-- will fail:
1189+--
1190+-- >>> Stream.parse (Parser.satisfy (> 0) `Parser.alt` undefined) $ Stream.fromList [1..10]
1191+-- Right 1
1192+--
1193+-- Compare with 'Alternative' instance method '<|>'. This implementation allows
1194+-- stream fusion but has quadratic complexity. This can fuse with other
1195+-- operations and can be much faster than 'Alternative' instance for small
1196+-- number (less than 8) of alternatives.
1197+--
1198+-- /Pre-release/
1199+--
1200+{-# INLINE alt #-}
1201+alt :: Monad m => Parser x m a -> Parser x m a -> Parser x m a
1202+alt p1 p2 = D.toParserK $ D.alt (D.fromParserK p1) (D.fromParserK p2)
1203+
1204+{-
1205+-- | Shortest alternative. Apply both parsers in parallel but choose the result
1206+-- from the one which consumed least input i.e. take the shortest succeeding
1207+-- parse.
1208+--
1209+-- /Pre-release/
1210+--
1211+{-# INLINE shortest #-}
1212+shortest :: Monad m
1213+ => Parser x m a -> Parser x m a -> Parser x m a
1214+shortest p1 p2 = D.toParserK $ D.shortest (D.fromParserK p1) (D.fromParserK p2)
1215+
1216+-- | Longest alternative. Apply both parsers in parallel but choose the result
1217+-- from the one which consumed more input i.e. take the longest succeeding
1218+-- parse.
1219+--
1220+-- /Pre-release/
1221+--
1222+{-# INLINE longest #-}
1223+longest :: Monad m
1224+ => Parser x m a -> Parser x m a -> Parser x m a
1225+longest p1 p2 = D.toParserK $ D.longest (D.fromParserK p1) (D.fromParserK p2)
1226+-}
1227+
1228+-- | Run a parser without consuming the input.
1229+--
1230+{-# INLINE lookAhead #-}
1231+lookAhead :: Monad m => Parser a m b -> Parser a m b
1232+lookAhead p = D.toParserK $ D.lookAhead $ D.fromParserK p
1233+
1234+-- | Takes at-most @n@ input elements.
1235+--
1236+-- * Stops - when the collecting parser stops.
1237+-- * Fails - when the collecting parser fails.
1238+--
1239+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
1240+-- Right [1,2]
1241+--
1242+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 5 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
1243+-- Left (ParseError "takeEQ: Expecting exactly 5 elements, input terminated on 4")
1244+--
1245+-- /Internal/
1246+{-# INLINE takeP #-}
1247+takeP :: Monad m => Int -> Parser a m b -> Parser a m b
1248+takeP i p = D.toParserK $ D.takeP i $ D.fromParserK p
1249+
1250+-------------------------------------------------------------------------------
1251+-- Sequential Collection
1252+-------------------------------------------------------------------------------
1253+--
1254+-- | @concatSequence f p@ collects sequential parses of parsers in a
1255+-- serial stream @p@ using the fold @f@. Fails if the input ends or any
1256+-- of the parsers fail.
1257+--
1258+-- An even more efficient implementation can use ParserD type Parser in
1259+-- the stream.
1260+--
1261+-- /Pre-release/
1262+--
1263+{-# INLINE concatSequence #-}
1264+concatSequence ::
1265+ Monad m =>
1266+ Fold m b c -> Stream m (Parser a m b) -> Parser a m c
1267+concatSequence f p =
1268+ let sp = fmap D.fromParserK p
1269+ in D.toParserK $ D.sequence sp f
1270+
1271+-- | Map a 'Parser' returning function on the result of a 'Parser'.
1272+--
1273+-- Compare with 'Monad' instance method '>>='. This implementation allows
1274+-- stream fusion but has quadratic complexity. This can fuse with other
1275+-- operations and can be much faster than 'Monad' instance for small number
1276+-- (less than 8) of compositions.
1277+--
1278+-- /Pre-release/
1279+--
1280+{-# INLINE concatMap #-}
1281+concatMap :: Monad m
1282+ => (b -> Parser a m c) -> Parser a m b -> Parser a m c
1283+concatMap f p = D.toParserK $ D.concatMap (D.fromParserK . f) (D.fromParserK p)
1284+
1285+{-
1286+-------------------------------------------------------------------------------
1287+-- Alternative Collection
1288+-------------------------------------------------------------------------------
1289+--
1290+-- | @choice parsers@ applies the @parsers@ in order and returns the first
1291+-- successful parse.
1292+--
1293+-- This is same as 'asum' but more efficient.
1294+--
1295+-- /Broken/
1296+--
1297+{-# INLINE choice #-}
1298+choice ::
1299+ (Functor t, Foldable t, Monad m) => t (Parser a m b) -> Parser a m b
1300+choice ps = D.toParserK $ D.choice $ D.fromParserK <$> ps
1301+-}
1302+
1303+-------------------------------------------------------------------------------
1304+-- Sequential Repetition
1305+-------------------------------------------------------------------------------
1306+
1307+-- | Like 'many' but uses a 'Parser' instead of a 'Fold' to collect the
1308+-- results. Parsing stops or fails if the collecting parser stops or fails.
1309+--
1310+-- /Unimplemented/
1311+--
1312+{-# INLINE manyP #-}
1313+manyP :: -- Monad m =>
1314+ Parser a m b -> Parser b m c -> Parser a m c
1315+manyP _p _f = undefined -- D.toParserK $ D.manyP (D.fromParserK p) f
1316+
1317+-- | Collect zero or more parses. Apply the supplied parser repeatedly on the
1318+-- input stream and push the parse results to a downstream fold.
1319+--
1320+-- Stops: when the downstream fold stops or the parser fails.
1321+-- Fails: never, produces zero or more results.
1322+--
1323+-- >>> many = Parser.countBetween 0 maxBound
1324+--
1325+-- Compare with 'Control.Applicative.many'.
1326+--
1327+{-# INLINE many #-}
1328+many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
1329+many p f = D.toParserK $ D.many (D.fromParserK p) f
1330+
1331+-- Note: many1 would perhaps be a better name for this and consistent with
1332+-- other names like takeWhile1. But we retain the name "some" for
1333+-- compatibility.
1334+--
1335+-- | Collect one or more parses. Apply the supplied parser repeatedly on the
1336+-- input stream and push the parse results to a downstream fold.
1337+--
1338+-- Stops: when the downstream fold stops or the parser fails.
1339+-- Fails: if it stops without producing a single result.
1340+--
1341+-- >>> some p f = Parser.manyP p (Parser.takeGE 1 f)
1342+-- >>> some = Parser.countBetween 1 maxBound
1343+--
1344+-- Compare with 'Control.Applicative.some'.
1345+--
1346+{-# INLINE some #-}
1347+some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
1348+some p f = D.toParserK $ D.some (D.fromParserK p) f
1349+-- some p f = manyP p (takeGE 1 f)
1350+-- some = countBetween 1 maxBound
1351+
1352+-- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of
1353+-- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if
1354+-- the input ends or the parser fails before @m@ results are collected.
1355+--
1356+-- >>> countBetween m n p f = Parser.manyP p (Parser.takeBetween m n f)
1357+--
1358+-- /Unimplemented/
1359+--
1360+{-# INLINE countBetween #-}
1361+countBetween :: -- Monad m =>
1362+ Int -> Int -> Parser a m b -> Fold m b c -> Parser a m c
1363+countBetween _m _n _p = undefined
1364+-- countBetween m n p f = manyP p (takeBetween m n f)
1365+
1366+-- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using
1367+-- the fold @f@. Fails if the input ends or the parser fails before @n@
1368+-- results are collected.
1369+--
1370+-- >>> count n = Parser.countBetween n n
1371+-- >>> count n p f = Parser.manyP p (Parser.takeEQ n f)
1372+--
1373+-- /Unimplemented/
1374+--
1375+{-# INLINE count #-}
1376+count :: -- Monad m =>
1377+ Int -> Parser a m b -> Fold m b c -> Parser a m c
1378+count n = countBetween n n
1379+-- count n p f = manyP p (takeEQ n f)
1380+
1381+-- | Like 'manyTill' but uses a 'Parser' to collect the results instead of a
1382+-- 'Fold'. Parsing stops or fails if the collecting parser stops or fails.
1383+--
1384+-- We can implemnent parsers like the following using 'manyTillP':
1385+--
1386+-- @
1387+-- countBetweenTill m n f p = manyTillP (takeBetween m n f) p
1388+-- @
1389+--
1390+-- /Unimplemented/
1391+--
1392+{-# INLINE manyTillP #-}
1393+manyTillP :: -- Monad m =>
1394+ Parser a m b -> Parser a m x -> Parser b m c -> Parser a m c
1395+manyTillP _p1 _p2 _f = undefined
1396+ -- D.toParserK $ D.manyTillP (D.fromParserK p1) (D.fromParserK p2) f
1397+
1398+-- | @manyTill chunking test f@ tries the parser @test@ on the input, if @test@
1399+-- fails it backtracks and tries @chunking@, after @chunking@ succeeds @test@ is
1400+-- tried again and so on. The parser stops when @test@ succeeds. The output of
1401+-- @test@ is discarded and the output of @chunking@ is accumulated by the
1402+-- supplied fold. The parser fails if @chunking@ fails.
1403+--
1404+-- Stops when the fold @f@ stops.
1405+--
1406+{-# INLINE manyTill #-}
1407+manyTill :: Monad m
1408+ => Parser a m b -- ^ Chunking parser. Parses chunks of input.
1409+ -> Parser a m x -- ^ Test parser. Parsing stops when this parser succeeds
1410+ -- else backtract and run the chunking parser.
1411+ -> Fold m b c -- ^ Folds the output of the chunking parser.
1412+ -> Parser a m c
1413+manyTill collect test f =
1414+ D.toParserK $ D.manyTill (D.fromParserK collect) (D.fromParserK test) f
1415+
1416+-- | @manyThen f collect recover@ repeats the parser @collect@ on the input and
1417+-- collects the output in the supplied fold. If the the parser @collect@ fails,
1418+-- parser @recover@ is run until it stops and then we start repeating the
1419+-- parser @collect@ again. The parser fails if the recovery parser fails.
1420+--
1421+-- For example, this can be used to find a key frame in a video stream after an
1422+-- error.
1423+--
1424+-- /Unimplemented/
1425+--
1426+{-# INLINE manyThen #-}
1427+manyThen :: -- (Foldable t, Monad m) =>
1428+ Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
1429+manyThen _parser _recover _f = undefined
1430+
1431+-------------------------------------------------------------------------------
1432+-- Interleaving
1433+-------------------------------------------------------------------------------
1434+--
1435+-- To deinterleave we can chain two parsers one behind the other. The input is
1436+-- given to the first parser and the input definitively rejected by the first
1437+-- parser is given to the second parser.
1438+--
1439+-- We can either have the parsers themselves buffer the input or use the shared
1440+-- global buffer to hold it until none of the parsers need it. When the first
1441+-- parser returns Skip (i.e. rewind) we let the second parser consume the
1442+-- rejected input and when it is done we move the cursor forward to the first
1443+-- parser again. This will require a "move forward" command as well.
1444+--
1445+-- To implement grep we can use three parsers, one to find the pattern, one
1446+-- to store the context behind the pattern and one to store the context in
1447+-- front of the pattern. When a match occurs we need to emit the accumulator of
1448+-- all the three parsers. One parser can count the line numbers to provide the
1449+-- line number info.
1450+
1451+-- XXX rename this to intercalate
1452+-- | Apply two parsers alternately to an input stream. The input stream is
1453+-- considered an interleaving of two patterns. The two parsers represent the
1454+-- two patterns.
1455+--
1456+{-# INLINE deintercalate #-}
1457+deintercalate :: Monad m =>
1458+ Parser a m x
1459+ -> Parser a m y
1460+ -> Fold m (Either x y) z
1461+ -> Parser a m z
1462+deintercalate contentL contentR sink =
1463+ D.toParserK
1464+ $ D.deintercalate
1465+ (D.fromParserK contentL) (D.fromParserK contentR) sink
1466+
1467+-- | Parse items separated by a separator parsed by the supplied parser. At
1468+-- least one item must be present for the parser to succeed.
1469+--
1470+-- Note that this can go in infinite loop if both the parsers fail on some
1471+-- input. Detection of that would make the implementation more complex.
1472+--
1473+{-# INLINE sepBy1 #-}
1474+sepBy1 :: Monad m =>
1475+ Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
1476+sepBy1 p sep sink = do
1477+ x <- p
1478+ f <- fromEffect $ FL.reduce sink
1479+ f1 <- fromEffect $ FL.snoc f x
1480+ many (sep >> p) f1
1481+
1482+-- | Run the content parser first, when it is done, the separator parser is
1483+-- run, when it is done content parser is run again and so on. If none of the
1484+-- parsers consumes an input then parser returns a failure.
1485+--
1486+-- >>> sepBy p1 p2 sink = Parser.deintercalate p1 p2 (Fold.catLefts sink)
1487+-- >>> sepBy content sep sink = Parser.sepBy1 content sep sink <|> return mempty
1488+--
1489+{-# INLINE sepBy #-}
1490+sepBy :: Monad m =>
1491+ Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
1492+sepBy content sep sink =
1493+ D.toParserK $ D.sepBy (D.fromParserK content) (D.fromParserK sep) sink
1494+-- sepBy sink = deintercalate (FL.catLefts sink)
1495+
1496+-------------------------------------------------------------------------------
1497+-- Interleaving a collection of parsers
1498+-------------------------------------------------------------------------------
1499+--
1500+-- | Apply a collection of parsers to an input stream in a round robin fashion.
1501+-- Each parser is applied until it stops and then we repeat starting with the
1502+-- the first parser again.
1503+--
1504+-- /Unimplemented/
1505+--
1506+{-# INLINE roundRobin #-}
1507+roundRobin :: -- (Foldable t, Monad m) =>
1508+ t (Parser a m b) -> Fold m b c -> Parser a m c
1509+roundRobin _ps _f = undefined
1510+
1511+-------------------------------------------------------------------------------
1512+-- Repeated Alternatives
1513+-------------------------------------------------------------------------------
1514+
1515+-- | Keep trying a parser up to a maximum of @n@ failures. When the parser
1516+-- fails the input consumed till now is dropped and the new instance is tried
1517+-- on the fresh input.
1518+--
1519+-- /Unimplemented/
1520+--
1521+{-# INLINE retryMaxTotal #-}
1522+retryMaxTotal :: -- (Monad m) =>
1523+ Int -> Parser a m b -> Fold m b c -> Parser a m c
1524+retryMaxTotal _n _p _f = undefined
1525+
1526+-- | Like 'retryMaxTotal' but aborts after @n@ successive failures.
1527+--
1528+-- /Unimplemented/
1529+--
1530+{-# INLINE retryMaxSuccessive #-}
1531+retryMaxSuccessive :: -- (Monad m) =>
1532+ Int -> Parser a m b -> Fold m b c -> Parser a m c
1533+retryMaxSuccessive _n _p _f = undefined
1534+
1535+-- | Keep trying a parser until it succeeds. When the parser fails the input
1536+-- consumed till now is dropped and the new instance is tried on the fresh
1537+-- input.
1538+--
1539+-- /Unimplemented/
1540+--
1541+{-# INLINE retry #-}
1542+retry :: -- (Monad m) =>
1543+ Parser a m b -> Parser a m b
1544+retry _p = undefined
core/src/Streamly/Internal/Data/Parser/ParserK/Type.hs+4−1
@@ -20,7 +20,8 @@ module Streamly.Internal.Data.Parser.ParserK.Type
20 (
20 (
21 Step (..)
21 Step (..)
22 , Parse (..)
22 , Parse (..)
23− , Parser (..)
23+ , Parser (..) -- XXX Stop exporting this
24+ , ParserK
24 , fromPure
25 , fromPure
25 , fromEffect
26 , fromEffect
26 , die
27 , die
@@ -101,6 +102,8 @@ newtype Parser a m b = MkParser
101 -> m (Step m a r)
102 -> m (Step m a r)
102 }
103 }
103
104
105+type ParserK = Parser
106+
104-------------------------------------------------------------------------------
107-------------------------------------------------------------------------------
105-- Functor
108-- Functor
106-------------------------------------------------------------------------------
109-------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Producer/Source.hs+2−2
@@ -282,7 +282,7 @@ parseManyD parser reader = Producer step return return
282-- /Pre-release/
282-- /Pre-release/
283{-# INLINE parseMany #-}
283{-# INLINE parseMany #-}
284parseMany :: Monad m =>
284parseMany :: Monad m =>
285− ParserK.Parser a m b
285+ ParserD.Parser a m b
286 -> Producer m (Source x a) a
286 -> Producer m (Source x a) a
287 -> Producer m (Source x a) (Either ParseError b)
287 -> Producer m (Source x a) (Either ParseError b)
288−parseMany parser = parseManyD (ParserD.fromParserK parser)
288+parseMany = parseManyD
core/src/Streamly/Internal/Data/Stream/Chunked.hs+2−2
@@ -96,7 +96,7 @@ import qualified Streamly.Internal.Data.Array.Mut.Stream as AS
96import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..))
96import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..))
97import qualified Streamly.Internal.Data.Parser as PR
97import qualified Streamly.Internal.Data.Parser as PR
98import qualified Streamly.Internal.Data.Parser.ParserD as PRD
98import qualified Streamly.Internal.Data.Parser.ParserD as PRD
99− (Parser(..), Initial(..), fromParserK)
99+ (Parser(..), Initial(..))
100import qualified Streamly.Internal.Data.Stream.StreamD as D
100import qualified Streamly.Internal.Data.Stream.StreamD as D
101import qualified Streamly.Internal.Data.Stream.StreamK as K
101import qualified Streamly.Internal.Data.Stream.StreamK as K
102
102
@@ -789,7 +789,7 @@ parseBreak ::
789parseBreak p s =
789parseBreak p s =
790 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
790 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
791-}
791-}
792−parseBreak p = parseBreakK (PRD.fromParserK p)
792+parseBreak = parseBreakK
793
793
794-------------------------------------------------------------------------------
794-------------------------------------------------------------------------------
795-- Elimination - Running Array Folds and parsers
795-- Elimination - Running Array Folds and parsers
core/src/Streamly/Internal/Data/Stream/StreamD/Eliminate.hs+2−2
@@ -181,7 +181,7 @@ parseD parser strm = do
181--
181--
182{-# INLINE [3] parse #-}
182{-# INLINE [3] parse #-}
183parse :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b)
183parse :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b)
184−parse = parseD . PRD.fromParserK
184+parse = parseD
185
185
186-- | Run a 'Parse' over a stream and return rest of the Stream.
186-- | Run a 'Parse' over a stream and return rest of the Stream.
187{-# INLINE_NORMAL parseBreakD #-}
187{-# INLINE_NORMAL parseBreakD #-}
@@ -347,7 +347,7 @@ parseBreakD (PRD.Parser pstep initial extract) stream@(Stream step state) = do
347--
347--
348{-# INLINE parseBreak #-}
348{-# INLINE parseBreak #-}
349parseBreak :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
349parseBreak :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
350−parseBreak p = parseBreakD (PRD.fromParserK p)
350+parseBreak = parseBreakD
351
351
352------------------------------------------------------------------------------
352------------------------------------------------------------------------------
353-- Specialized Folds
353-- Specialized Folds
core/src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+2−2
@@ -1620,7 +1620,7 @@ parseMany
1620 => PR.Parser a m b
1620 => PR.Parser a m b
1621 -> Stream m a
1621 -> Stream m a
1622 -> Stream m (Either ParseError b)
1622 -> Stream m (Either ParseError b)
1623−parseMany p = parseManyD (PRD.fromParserK p)
1623+parseMany = parseManyD
1624
1624
1625-- | Apply a stream of parsers to an input stream and emit the results in the
1625-- | Apply a stream of parsers to an input stream and emit the results in the
1626-- output stream.
1626-- output stream.
@@ -1890,7 +1890,7 @@ parseIterate
1890 -> b
1890 -> b
1891 -> Stream m a
1891 -> Stream m a
1892 -> Stream m (Either ParseError b)
1892 -> Stream m (Either ParseError b)
1893−parseIterate f = parseIterateD (PRD.fromParserK . f)
1893+parseIterate = parseIterateD
1894
1894
1895------------------------------------------------------------------------------
1895------------------------------------------------------------------------------
1896-- Grouping
1896-- Grouping
core/src/Streamly/Internal/Data/Stream/StreamK.hs+1−1
@@ -1231,7 +1231,7 @@ parseBreakD (PR.Parser pstep initial extract) stream = do
1231{-# INLINE parseBreak #-}
1231{-# INLINE parseBreak #-}
1232parseBreak :: Monad m =>
1232parseBreak :: Monad m =>
1233 Parser.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
1233 Parser.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
1234−parseBreak p = parseBreakD (PR.fromParserK p)
1234+parseBreak = parseBreakD
1235
1235
1236{-# INLINE parse #-}
1236{-# INLINE parse #-}
1237parse :: Monad m =>
1237parse :: Monad m =>
core/src/Streamly/Internal/Serialize/FromBytes.hs+7−7
@@ -54,7 +54,7 @@ import qualified Streamly.Internal.Data.Array as A
54import qualified Streamly.Internal.Data.Parser as PR
54import qualified Streamly.Internal.Data.Parser as PR
55 (fromPure, either, satisfy, takeEQ)
55 (fromPure, either, satisfy, takeEQ)
56import qualified Streamly.Internal.Data.Parser.ParserD as PRD
56import qualified Streamly.Internal.Data.Parser.ParserD as PRD
57− (Parser(..), Initial(..), Step(..), toParserK)
57+ (Parser(..), Initial(..), Step(..))
58
58
59-- Note: The () type does not need to have an on-disk representation in theory.
59-- Note: The () type does not need to have an on-disk representation in theory.
60-- But we use a concrete representation for it so that we count how many ()
60-- But we use a concrete representation for it so that we count how many ()
@@ -155,7 +155,7 @@ word16beD = PRD.Parser step initial extract
155--
155--
156{-# INLINE word16be #-}
156{-# INLINE word16be #-}
157word16be :: Monad m => Parser Word8 m Word16
157word16be :: Monad m => Parser Word8 m Word16
158−word16be = PRD.toParserK word16beD
158+word16be = word16beD
159
159
160-- | Little endian (LSB first) Word16
160-- | Little endian (LSB first) Word16
161{-# INLINE word16leD #-}
161{-# INLINE word16leD #-}
@@ -180,7 +180,7 @@ word16leD = PRD.Parser step initial extract
180--
180--
181{-# INLINE word16le #-}
181{-# INLINE word16le #-}
182word16le :: Monad m => Parser Word8 m Word16
182word16le :: Monad m => Parser Word8 m Word16
183−word16le = PRD.toParserK word16leD
183+word16le = word16leD
184
184
185-- | Big endian (MSB first) Word32
185-- | Big endian (MSB first) Word32
186{-# INLINE word32beD #-}
186{-# INLINE word32beD #-}
@@ -207,7 +207,7 @@ word32beD = PRD.Parser step initial extract
207--
207--
208{-# INLINE word32be #-}
208{-# INLINE word32be #-}
209word32be :: Monad m => Parser Word8 m Word32
209word32be :: Monad m => Parser Word8 m Word32
210−word32be = PRD.toParserK word32beD
210+word32be = word32beD
211
211
212-- | Little endian (LSB first) Word32
212-- | Little endian (LSB first) Word32
213{-# INLINE word32leD #-}
213{-# INLINE word32leD #-}
@@ -233,7 +233,7 @@ word32leD = PRD.Parser step initial extract
233--
233--
234{-# INLINE word32le #-}
234{-# INLINE word32le #-}
235word32le :: Monad m => Parser Word8 m Word32
235word32le :: Monad m => Parser Word8 m Word32
236−word32le = PRD.toParserK word32leD
236+word32le = word32leD
237
237
238-- | Big endian (MSB first) Word64
238-- | Big endian (MSB first) Word64
239{-# INLINE word64beD #-}
239{-# INLINE word64beD #-}
@@ -260,7 +260,7 @@ word64beD = PRD.Parser step initial extract
260--
260--
261{-# INLINE word64be #-}
261{-# INLINE word64be #-}
262word64be :: Monad m => Parser Word8 m Word64
262word64be :: Monad m => Parser Word8 m Word64
263−word64be = PRD.toParserK word64beD
263+word64be = word64beD
264
264
265-- | Little endian (LSB first) Word64
265-- | Little endian (LSB first) Word64
266{-# INLINE word64leD #-}
266{-# INLINE word64leD #-}
@@ -286,7 +286,7 @@ word64leD = PRD.Parser step initial extract
286--
286--
287{-# INLINE word64le #-}
287{-# INLINE word64le #-}
288word64le :: Monad m => Parser Word8 m Word64
288word64le :: Monad m => Parser Word8 m Word64
289−word64le = PRD.toParserK word64leD
289+word64le = word64leD
290
290
291{-# INLINE int8 #-}
291{-# INLINE int8 #-}
292int8 :: Monad m => Parser Word8 m Int8
292int8 :: Monad m => Parser Word8 m Int8
core/src/Streamly/Internal/Unicode/Stream.hs+1−1
@@ -540,7 +540,7 @@ writeCharUtf8' = ParserD.toFold (parseCharUtf8WithD ErrorOnCodingFailure)
540{-# INLINE parseCharUtf8With #-}
540{-# INLINE parseCharUtf8With #-}
541parseCharUtf8With ::
541parseCharUtf8With ::
542 Monad m => CodingFailureMode -> Parser.Parser Word8 m Char
542 Monad m => CodingFailureMode -> Parser.Parser Word8 m Char
543−parseCharUtf8With = ParserD.toParserK . parseCharUtf8WithD
543+parseCharUtf8With = parseCharUtf8WithD
544
544
545-- XXX write it as a parser and use parseMany to decode a stream, need to check
545-- XXX write it as a parser and use parseMany to decode a stream, need to check
546-- if that preserves the same performance. Or we can use a resumable parser
546-- if that preserves the same performance. Or we can use a resumable parser
core/streamly-core.cabal+2
@@ -390,6 +390,7 @@ library
390 , Streamly.Data.Array.Mut
390 , Streamly.Data.Array.Mut
391 , Streamly.Data.Fold
391 , Streamly.Data.Fold
392 , Streamly.Data.Parser
392 , Streamly.Data.Parser
393+ , Streamly.Data.Parser.ParserK
393 , Streamly.Data.Stream
394 , Streamly.Data.Stream
394 , Streamly.Data.Stream.StreamK
395 , Streamly.Data.Stream.StreamK
395 , Streamly.Data.Unfold
396 , Streamly.Data.Unfold
@@ -419,6 +420,7 @@ library
419 , Streamly.Internal.Data.Stream.Cross
420 , Streamly.Internal.Data.Stream.Cross
420 , Streamly.Internal.Data.List
421 , Streamly.Internal.Data.List
421 , Streamly.Data.Stream.Zip
422 , Streamly.Data.Stream.Zip
423+ , Streamly.Internal.Data.Parser.ParserDK
422
424
423 build-depends:
425 build-depends:
424 -- streamly-base
426 -- streamly-base
src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs+2−2
@@ -382,7 +382,7 @@ parseD p = D.parseD p . toStreamD
382-- /Internal/
382-- /Internal/
383{-# INLINE parseK #-}
383{-# INLINE parseK #-}
384parseK :: Monad m => PRK.Parser a m b -> SerialT m a -> m (Either PRD.ParseError b)
384parseK :: Monad m => PRK.Parser a m b -> SerialT m a -> m (Either PRD.ParseError b)
385−parseK = parse
385+parseK p = parse (PRD.fromParserK p)
386
386
387-- | Parse a stream using the supplied 'Parser'.
387-- | Parse a stream using the supplied 'Parser'.
388--
388--
@@ -404,7 +404,7 @@ parseK = parse
404--
404--
405{-# INLINE [3] parse #-}
405{-# INLINE [3] parse #-}
406parse :: Monad m => Parser a m b -> SerialT m a -> m (Either PRD.ParseError b)
406parse :: Monad m => Parser a m b -> SerialT m a -> m (Either PRD.ParseError b)
407−parse = parseD . PRD.fromParserK
407+parse = parseD
408
408
409------------------------------------------------------------------------------
409------------------------------------------------------------------------------
410-- Specific Fold Functions
410-- Specific Fold Functions
src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+3−4
@@ -189,8 +189,7 @@ import qualified Streamly.Internal.Data.Array.Type as A
189import qualified Streamly.Internal.Data.Fold as FL
189import qualified Streamly.Internal.Data.Fold as FL
190 (Fold, Step(..), takeEndBy_, takeEndBy, catMaybes, take)
190 (Fold, Step(..), takeEndBy_, takeEndBy, catMaybes, take)
191import qualified Streamly.Internal.Data.IsMap as IsMap
191import qualified Streamly.Internal.Data.IsMap as IsMap
192−import qualified Streamly.Internal.Data.Parser.ParserD as PRD
192+import qualified Streamly.Internal.Data.Parser.ParserD as PRD (Parser(..))
193− (Parser(..), fromParserK)
194import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
193import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
195import qualified Streamly.Internal.Data.Stream.StreamD as D
194import qualified Streamly.Internal.Data.Stream.StreamD as D
196 ( foldMany
195 ( foldMany
@@ -388,7 +387,7 @@ parseMany
388 -> t m a
387 -> t m a
389 -> t m (Either ParseError b)
388 -> t m (Either ParseError b)
390parseMany p m =
389parseMany p m =
391− fromStreamD $ D.parseManyD (PRD.fromParserK p) (toStreamD m)
390+ fromStreamD $ D.parseManyD p (toStreamD m)
392
391
393-- | Same as parseMany but for StreamD streams.
392-- | Same as parseMany but for StreamD streams.
394--
393--
@@ -456,7 +455,7 @@ parseIterate
456 -> t m a
455 -> t m a
457 -> t m (Either ParseError b)
456 -> t m (Either ParseError b)
458parseIterate f i m = fromStreamD $
457parseIterate f i m = fromStreamD $
459− D.parseIterateD (PRD.fromParserK . f) i (toStreamD m)
458+ D.parseIterateD f i (toStreamD m)
460
459
461------------------------------------------------------------------------------
460------------------------------------------------------------------------------
462-- Grouping
461-- Grouping
test/Streamly/Test/Data/Parser.hs+3−2
@@ -13,6 +13,7 @@ import Test.QuickCheck.Monadic (monadicIO, assert, run)
13
13
14import Prelude hiding (sequence)
14import Prelude hiding (sequence)
15
15
16+import qualified Control.Monad.Fail as Fail
16import qualified Data.List as List
17import qualified Data.List as List
17import qualified Prelude
18import qualified Prelude
18import qualified Streamly.Internal.Data.Array as A
19import qualified Streamly.Internal.Data.Array as A
@@ -97,7 +98,7 @@ dieM =
97parserFail :: Property
98parserFail :: Property
98parserFail =
99parserFail =
99 property $
100 property $
100− case runIdentity $ S.parse (fail err) (S.fromList [0 :: Int]) of
101+ case runIdentity $ S.parse (Fail.fail err) (S.fromList [0 :: Int]) of
101 Right _ -> False
102 Right _ -> False
102 Left (ParseError e) -> err == e
103 Left (ParseError e) -> err == e
103 where
104 where
@@ -255,7 +256,7 @@ takeGE =
255 let
256 let
256 list_length = Prelude.length ls
257 list_length = Prelude.length ls
257 in
258 in
258− case runIdentity $S.parse (P.takeGE n FL.toList) (S.fromList ls) of
259+ case runIdentity $ S.parse (P.takeGE n FL.toList) (S.fromList ls) of
259 Right parsed_list ->
260 Right parsed_list ->
260 if n <= list_length
261 if n <= list_length
261 then checkListEqual parsed_list ls
262 then checkListEqual parsed_list ls