on master help sign in
Use parserK by default and rewrite to ParserD
commitdde90418338cd3c12860f7b66b67f638ee57ac38 authorHarendra Kumar <harendra@composewell.com> dateTue 14 Apr 2020, 22:49 (7 years ago) parenta03e287
14 files changed+1428−388
benchmark/Chart.hs+7 benchmark/Streamly/Benchmark/Data/Parser.hs+229 benchmark/Streamly/Benchmark/Data/ParserD.hs+28−22 benchmark/Streamly/Benchmark/Data/ParserK.hs+2−5 benchmark/streamly-benchmarks.cabal+12 src/Streamly/Internal/Data/Parser.hs+871 src/Streamly/Internal/Data/Parser/ParserD.hs+73−219 src/Streamly/Internal/Data/Parser/ParserD/Tee.hs+5−13 src/Streamly/Internal/Data/Parser/ParserD/Types.hs+100−112 src/Streamly/Internal/Data/Parser/ParserK/Types.hs+75−5 src/Streamly/Internal/Data/Stream/Prelude.hs+1−1 src/Streamly/Internal/Data/Stream/StreamD.hs+5−4 src/Streamly/Internal/Prelude.hs+19−7 streamly.cabal+1
benchmark/Chart.hs+7
@@ -30,6 +30,7 @@ data BenchType
3030 | NestedConcurrent
3131 | ParserD
3232 | ParserK
33+ | Parser
3334 | Base
3435 | FileIO
3536 | Array
@@ -84,6 +85,7 @@ parseBench = do
8485 Just "nested-concurrent" -> setBenchType NestedConcurrent
8586 Just "parserD" -> setBenchType ParserD
8687 Just "parserK" -> setBenchType ParserK
88+ Just "parser" -> setBenchType Parser
8789 Just "base" -> setBenchType Base
8890 Just "fileio" -> setBenchType FileIO
8991 Just "array-cmp" -> setBenchType ArrayCmp
@@ -374,6 +376,11 @@ main = do
374376 (makeGraphs "parserK")
375377 "charts/parserK/results.csv"
376378 "charts/parserK"
379+ Parser -> benchShow opts cfg
380+ { title = Just "Parser" }
381+ (makeGraphs "parser")
382+ "charts/parser/results.csv"
383+ "charts/parser"
377384 FileIO -> benchShow opts cfg
378385 { title = Just "File IO" }
379386 (makeGraphs "fileIO")
benchmark/Streamly/Benchmark/Data/Parser.hsnew file+229
@@ -0,0 +1,229 @@
1+-- |
2+-- Module : Streamly.Benchmark.Data.Parser
3+-- Copyright : (c) 2020 Composewell Technologies
4+--
5+-- License : MIT
6+-- Maintainer : streamly@composewell.com
7+
8+{-# LANGUAGE FlexibleContexts #-}
9+{-# OPTIONS_GHC -fspec-constr-recursive=4 #-}
10+
11+module Main
12+ (
13+ main
14+ ) where
15+
16+import Control.DeepSeq (NFData(..))
17+import Control.Monad.Catch (MonadCatch)
18+import Data.Foldable (asum)
19+import System.Random (randomRIO)
20+import Prelude hiding (any, all, take, sequence, sequenceA, takeWhile)
21+
22+import qualified Data.Traversable as TR
23+import qualified Control.Applicative as AP
24+import qualified Streamly as S hiding (runStream)
25+import qualified Streamly.Prelude as S
26+import qualified Streamly.Internal.Data.Fold as FL
27+import qualified Streamly.Internal.Data.Parser as PR
28+import qualified Streamly.Internal.Prelude as IP
29+
30+import Gauge
31+import Streamly hiding (runStream)
32+import Streamly.Benchmark.Common
33+
34+-------------------------------------------------------------------------------
35+-- Utilities
36+-------------------------------------------------------------------------------
37+
38+-- XXX these can be moved to the common module
39+
40+{-# INLINE sourceUnfoldrM #-}
41+sourceUnfoldrM :: (S.IsStream t, S.MonadAsync m) => Int -> Int -> t m Int
42+sourceUnfoldrM value n = S.unfoldrM step n
43+ where
44+ step cnt =
45+ if cnt > n + value
46+ then return Nothing
47+ else return (Just (cnt, cnt + 1))
48+
49+-- | Takes a fold method, and uses it with a default source.
50+{-# INLINE benchIOSink #-}
51+benchIOSink
52+ :: (IsStream t, NFData b)
53+ => Int -> String -> (t IO Int -> IO b) -> Benchmark
54+benchIOSink value name f =
55+ bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldrM value
56+
57+-------------------------------------------------------------------------------
58+-- Parsers
59+-------------------------------------------------------------------------------
60+
61+{-# INLINE any #-}
62+any :: (MonadCatch m, Ord a) => a -> SerialT m a -> m Bool
63+any value = IP.parse (PR.any (> value))
64+
65+{-# INLINE all #-}
66+all :: (MonadCatch m, Ord a) => a -> SerialT m a -> m Bool
67+all value = IP.parse (PR.all (<= value))
68+
69+{-# INLINE take #-}
70+take :: MonadCatch m => Int -> SerialT m a -> m ()
71+take value = IP.parse (PR.take value FL.drain)
72+
73+{-# INLINE takeWhile #-}
74+takeWhile :: MonadCatch m => Int -> SerialT m Int -> m ()
75+takeWhile value = IP.parse (PR.takeWhile (<= value) FL.drain)
76+
77+{-# INLINE many #-}
78+many :: MonadCatch m => SerialT m Int -> m Int
79+many = IP.parse (PR.many FL.length (PR.satisfy (> 0)))
80+
81+{-# INLINE manyAlt #-}
82+manyAlt :: MonadCatch m => SerialT m Int -> m Int
83+manyAlt xs = do
84+ x <- IP.parse (AP.many (PR.satisfy (> 0))) xs
85+ return $ Prelude.length x
86+
87+{-# INLINE some #-}
88+some :: MonadCatch m => SerialT m Int -> m Int
89+some = IP.parse (PR.some FL.length (PR.satisfy (> 0)))
90+
91+{-# INLINE someAlt #-}
92+someAlt :: MonadCatch m => SerialT m Int -> m Int
93+someAlt xs = do
94+ x <- IP.parse (AP.some (PR.satisfy (> 0))) xs
95+ return $ Prelude.length x
96+
97+{-# INLINE manyTill #-}
98+manyTill :: MonadCatch m => Int -> SerialT m Int -> m Int
99+manyTill value =
100+ IP.parse (PR.manyTill FL.length (PR.satisfy (> 0)) (PR.satisfy (== value)))
101+
102+{-# INLINE splitAllAny #-}
103+splitAllAny :: MonadCatch m
104+ => Int -> SerialT m Int -> m (Bool, Bool)
105+splitAllAny value =
106+ IP.parse ((,) <$> PR.all (<= (value `div` 2)) <*> PR.any (> value))
107+
108+{-# INLINE splitWithAllAny #-}
109+splitWithAllAny :: MonadCatch m
110+ => Int -> SerialT m Int -> m (Bool, Bool)
111+splitWithAllAny value =
112+ IP.parse (PR.splitWith (,) (PR.all (<= (value `div` 2))) (PR.any (> value)))
113+
114+{-# INLINE teeAllAny #-}
115+teeAllAny :: (MonadCatch m, Ord a)
116+ => a -> SerialT m a -> m (Bool, Bool)
117+teeAllAny value =
118+ IP.parse (PR.teeWith (,) (PR.all (<= value)) (PR.any (> value)))
119+
120+{-# INLINE teeFstAllAny #-}
121+teeFstAllAny :: (MonadCatch m, Ord a)
122+ => a -> SerialT m a -> m (Bool, Bool)
123+teeFstAllAny value =
124+ IP.parse (PR.teeWithFst (,) (PR.all (<= value)) (PR.any (> value)))
125+
126+{-# INLINE shortestAllAny #-}
127+shortestAllAny :: (MonadCatch m, Ord a)
128+ => a -> SerialT m a -> m Bool
129+shortestAllAny value =
130+ IP.parse (PR.shortest (PR.all (<= value)) (PR.any (> value)))
131+
132+{-# INLINE longestAllAny #-}
133+longestAllAny :: (MonadCatch m, Ord a)
134+ => a -> SerialT m a -> m Bool
135+longestAllAny value =
136+ IP.parse (PR.longest (PR.all (<= value)) (PR.any (> value)))
137+
138+-------------------------------------------------------------------------------
139+-- Parsers in which -fspec-constr-recursive=16 is problematic
140+-------------------------------------------------------------------------------
141+
142+-- XXX -fspec-constr-recursive=16 makes GHC go beserk when compiling these.
143+-- We need to fix GHC so that we can have better control over that option or do
144+-- not have to rely on it.
145+--
146+{-# INLINE lookAhead #-}
147+lookAhead :: MonadCatch m => Int -> SerialT m Int -> m ()
148+lookAhead value =
149+ IP.parse (PR.lookAhead (PR.takeWhile (<= value) FL.drain) *> pure ())
150+
151+-- quadratic complexity
152+{-# INLINE sequenceA #-}
153+sequenceA :: MonadCatch m => Int -> SerialT m Int -> m Int
154+sequenceA value xs = do
155+ x <- IP.parse (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
156+ return $ length x
157+
158+-- quadratic complexity
159+{-# INLINE sequence #-}
160+sequence :: MonadCatch m => Int -> SerialT m Int -> m Int
161+sequence value xs = do
162+ x <- IP.parse (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
163+ return $ length x
164+
165+-- choice using the "Alternative" instance with direct style parser type has
166+-- quadratic performance complexity.
167+--
168+{-# INLINE choice #-}
169+choice :: MonadCatch m => Int -> SerialT m Int -> m Int
170+choice value =
171+ IP.parse (asum (replicate value (PR.satisfy (< 0)))
172+ AP.<|> PR.satisfy (> 0))
173+
174+-------------------------------------------------------------------------------
175+-- Benchmarks
176+-------------------------------------------------------------------------------
177+
178+o_1_space_serial_parse :: Int -> [Benchmark]
179+o_1_space_serial_parse value =
180+ [ benchIOSink value "any" $ any value
181+ , benchIOSink value "all" $ all value
182+ , benchIOSink value "take" $ take value
183+ , benchIOSink value "takeWhile" $ takeWhile value
184+ , benchIOSink value "split (all,any)" $ splitAllAny value
185+ , benchIOSink value "splitWith (all,any)" $ splitWithAllAny value
186+ , benchIOSink value "many" many
187+ , benchIOSink value "some" some
188+ , benchIOSink value "choice" $ choice value
189+ , benchIOSink value "tee (all,any)" $ teeAllAny value
190+ , benchIOSink value "teeFst (all,any)" $ teeFstAllAny value
191+ , benchIOSink value "shortest (all,any)" $ shortestAllAny value
192+ , benchIOSink value "longest (all,any)" $ longestAllAny value
193+ , benchIOSink value "sequenceA" $ sequenceA value
194+ , benchIOSink value "sequence" $ sequence value
195+ ]
196+
197+o_1_heap_serial_parse :: Int -> [Benchmark]
198+o_1_heap_serial_parse value =
199+ [ benchIOSink value "lookAhead" $ lookAhead value
200+ , benchIOSink value "manyAlt" manyAlt
201+ , benchIOSink value "someAlt" someAlt
202+ , benchIOSink value "manyTill" $ manyTill value
203+ ]
204+
205+-------------------------------------------------------------------------------
206+-- Driver
207+-------------------------------------------------------------------------------
208+
209+main :: IO ()
210+main = do
211+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
212+ value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
213+
214+ where
215+
216+ allBenchmarks value =
217+ [ bgroup "o-1-space"
218+ [ bgroup "parser" $ concat
219+ [
220+ o_1_space_serial_parse value
221+ ]
222+ ]
223+ , bgroup "o-n-heap"
224+ [ bgroup "parser" $ concat
225+ [
226+ o_1_heap_serial_parse value
227+ ]
228+ ]
229+ ]
benchmark/Streamly/Benchmark/Data/ParserD.hs+28−22
@@ -20,6 +20,7 @@ import System.Random (randomRIO)
2020import Prelude hiding (any, all, take, sequence, sequenceA, takeWhile)
2121
2222import qualified Data.Traversable as TR
23+import qualified Data.Foldable as F
2324import qualified Control.Applicative as AP
2425import qualified Streamly as S hiding (runStream)
2526import qualified Streamly.Prelude as S
@@ -37,9 +38,6 @@ import Streamly.Benchmark.Common
3738
3839-- XXX these can be moved to the common module
3940
40−-- We need a monadic bind here to make sure that the function f does not get
41−-- completely optimized out by the compiler in some cases.
42−
4341{-# INLINE sourceUnfoldrM #-}
4442sourceUnfoldrM :: (S.IsStream t, S.MonadAsync m) => Int -> Int -> t m Int
4543sourceUnfoldrM value n = S.unfoldrM step n
@@ -63,74 +61,76 @@ benchIOSink value name f =
6361
6462{-# INLINE any #-}
6563any :: (MonadThrow m, Ord a) => a -> SerialT m a -> m Bool
66−any value = IP.parse (PR.any (> value))
64+any value = IP.parseD (PR.any (> value))
6765
6866{-# INLINE all #-}
6967all :: (MonadThrow m, Ord a) => a -> SerialT m a -> m Bool
70−all value = IP.parse (PR.all (<= value))
68+all value = IP.parseD (PR.all (<= value))
7169
7270{-# INLINE take #-}
7371take :: MonadThrow m => Int -> SerialT m a -> m ()
74−take value = IP.parse (PR.take value FL.drain)
72+take value = IP.parseD (PR.take value FL.drain)
7573
7674{-# INLINE takeWhile #-}
7775takeWhile :: MonadThrow m => Int -> SerialT m Int -> m ()
78−takeWhile value = IP.parse (PR.takeWhile (<= value) FL.drain)
76+takeWhile value = IP.parseD (PR.takeWhile (<= value) FL.drain)
7977
8078{-# INLINE many #-}
8179many :: MonadCatch m => SerialT m Int -> m Int
82−many = IP.parse (PR.many FL.length (PR.satisfy (> 0)))
80+many = IP.parseD (PR.many FL.length (PR.satisfy (> 0)))
8381
8482{-# INLINE manyAlt #-}
8583manyAlt :: MonadCatch m => SerialT m Int -> m Int
8684manyAlt xs = do
87− x <- IP.parse (AP.many (PR.satisfy (> 0))) xs
85+ x <- IP.parseD (AP.many (PR.satisfy (> 0))) xs
8886 return $ Prelude.length x
8987
9088{-# INLINE some #-}
9189some :: MonadCatch m => SerialT m Int -> m Int
92−some = IP.parse (PR.some FL.length (PR.satisfy (> 0)))
90+some = IP.parseD (PR.some FL.length (PR.satisfy (> 0)))
9391
9492{-# INLINE someAlt #-}
9593someAlt :: MonadCatch m => SerialT m Int -> m Int
9694someAlt xs = do
97− x <- IP.parse (AP.some (PR.satisfy (> 0))) xs
95+ x <- IP.parseD (AP.some (PR.satisfy (> 0))) xs
9896 return $ Prelude.length x
9997
10098{-# INLINE manyTill #-}
10199manyTill :: MonadCatch m => Int -> SerialT m Int -> m Int
102100manyTill value =
103− IP.parse (PR.manyTill FL.length (PR.satisfy (> 0)) (PR.satisfy (== value)))
101+ let p = PR.satisfy (> 0)
102+ pcond = PR.satisfy (== value)
103+ in IP.parseD (PR.manyTill FL.length p pcond)
104104
105105{-# INLINE splitAllAny #-}
106106splitAllAny :: MonadThrow m
107107 => Int -> SerialT m Int -> m (Bool, Bool)
108108splitAllAny value =
109− IP.parse ((,) <$> PR.all (<= (value `div` 2)) <*> PR.any (> value))
109+ IP.parseD ((,) <$> PR.all (<= (value `div` 2)) <*> PR.any (> value))
110110
111111{-# INLINE teeAllAny #-}
112112teeAllAny :: (MonadThrow m, Ord a)
113113 => a -> SerialT m a -> m (Bool, Bool)
114114teeAllAny value =
115− IP.parse (PR.teeWith (,) (PR.all (<= value)) (PR.any (> value)))
115+ IP.parseD (PR.teeWith (,) (PR.all (<= value)) (PR.any (> value)))
116116
117117{-# INLINE teeFstAllAny #-}
118118teeFstAllAny :: (MonadThrow m, Ord a)
119119 => a -> SerialT m a -> m (Bool, Bool)
120120teeFstAllAny value =
121− IP.parse (PR.teeWithFst (,) (PR.all (<= value)) (PR.any (> value)))
121+ IP.parseD (PR.teeWithFst (,) (PR.all (<= value)) (PR.any (> value)))
122122
123123{-# INLINE shortestAllAny #-}
124124shortestAllAny :: (MonadThrow m, Ord a)
125125 => a -> SerialT m a -> m Bool
126126shortestAllAny value =
127− IP.parse (PR.shortest (PR.all (<= value)) (PR.any (> value)))
127+ IP.parseD (PR.shortest (PR.all (<= value)) (PR.any (> value)))
128128
129129{-# INLINE longestAllAny #-}
130130longestAllAny :: (MonadCatch m, Ord a)
131131 => a -> SerialT m a -> m Bool
132132longestAllAny value =
133− IP.parse (PR.longest (PR.all (<= value)) (PR.any (> value)))
133+ IP.parseD (PR.longest (PR.all (<= value)) (PR.any (> value)))
134134
135135-------------------------------------------------------------------------------
136136-- Parsers in which -fspec-constr-recursive=16 is problematic
@@ -143,20 +143,25 @@ longestAllAny value =
143143{-# INLINE lookAhead #-}
144144lookAhead :: MonadThrow m => Int -> SerialT m Int -> m ()
145145lookAhead value =
146− IP.parse (PR.lookAhead (PR.takeWhile (<= value) FL.drain) *> pure ())
146+ IP.parseD (PR.lookAhead (PR.takeWhile (<= value) FL.drain) *> pure ())
147+
148+{-# INLINE sequenceA_ #-}
149+sequenceA_ :: MonadThrow m => Int -> SerialT m Int -> m ()
150+sequenceA_ value =
151+ IP.parseD (F.sequenceA_ $ replicate value (PR.satisfy (> 0)))
147152
148153-- quadratic complexity
149154{-# INLINE sequenceA #-}
150155sequenceA :: MonadThrow m => Int -> SerialT m Int -> m Int
151156sequenceA value xs = do
152− x <- IP.parse (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
157+ x <- IP.parseD (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
153158 return $ length x
154159
155160-- quadratic complexity
156161{-# INLINE sequence #-}
157162sequence :: MonadThrow m => Int -> SerialT m Int -> m Int
158163sequence value xs = do
159− x <- IP.parse (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
164+ x <- IP.parseD (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
160165 return $ length x
161166
162167-- choice using the "Alternative" instance with direct style parser type has
@@ -164,8 +169,8 @@ sequence value xs = do
164169--
165170{-# INLINE choice #-}
166171choice :: MonadCatch m => Int -> SerialT m Int -> m Int
167−choice value = do
168− IP.parse (asum (replicate value (PR.satisfy (< 0)))
172+choice value =
173+ IP.parseD (asum (replicate value (PR.satisfy (< 0)))
169174 AP.<|> PR.satisfy (> 0))
170175
171176-------------------------------------------------------------------------------
@@ -187,6 +192,7 @@ o_1_space_serial_parse value =
187192 , benchIOSink value "shortest (all,any)" $ shortestAllAny value
188193 , benchIOSink value "longest (all,any)" $ longestAllAny value
189194 , benchIOSink value "sequenceA/100" $ sequenceA (value `div` 100)
195+ , benchIOSink value "sequenceA_/100" $ sequenceA_ (value `div` 100)
190196 , benchIOSink value "sequence/100" $ sequence (value `div` 100)
191197 ]
192198
benchmark/Streamly/Benchmark/Data/ParserK.hs+2−5
@@ -18,8 +18,8 @@ import Data.Foldable (asum)
1818import System.Random (randomRIO)
1919import Prelude hiding (any, all, take, sequence, sequenceA, takeWhile)
2020
21−import qualified Data.Traversable as TR
2221import qualified Control.Applicative as AP
22+import qualified Data.Traversable as TR
2323import qualified Streamly as S hiding (runStream)
2424import qualified Streamly.Prelude as S
2525import qualified Streamly.Internal.Data.Parser.ParserK.Types as PR
@@ -36,9 +36,6 @@ import Streamly.Benchmark.Common
3636
3737-- XXX these can be moved to the common module
3838
39−-- We need a monadic bind here to make sure that the function f does not get
40−-- completely optimized out by the compiler in some cases.
41−
4239{-# INLINE sourceUnfoldrM #-}
4340sourceUnfoldrM :: (S.IsStream t, S.MonadAsync m) => Int -> Int -> t m Int
4441sourceUnfoldrM value n = S.unfoldrM step n
@@ -108,7 +105,7 @@ someAlt xs = do
108105
109106{-# INLINE choice #-}
110107choice :: MonadCatch m => Int -> SerialT m Int -> m Int
111−choice value = do
108+choice value =
112109 IP.parseK (asum (replicate value (satisfy (< 0)))
113110 AP.<|> satisfy (> 0))
114111
benchmark/streamly-benchmarks.cabal+12
@@ -271,6 +271,18 @@ benchmark parserK
271271 buildable: True
272272 build-depends: exceptions >= 0.8 && < 0.11
273273
274+benchmark parser
275+ import: bench-options
276+ type: exitcode-stdio-1.0
277+ ghc-options: -with-rtsopts "-T -K36K -M32M"
278+ hs-source-dirs: Streamly/Benchmark/Data
279+ main-is: Parser.hs
280+ if impl(ghcjs)
281+ buildable: False
282+ else
283+ buildable: True
284+ build-depends: exceptions >= 0.8 && < 0.11
285+
274286-------------------------------------------------------------------------------
275287-- Raw Streams
276288-------------------------------------------------------------------------------
src/Streamly/Internal/Data/Parser.hsnew file+871
@@ -0,0 +1,871 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE RankNTypes #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+
5+#if __GLASGOW_HASKELL__ >= 800
6+{-# OPTIONS_GHC -Wno-orphans #-}
7+#endif
8+
9+-- |
10+-- Module : Streamly.Internal.Data.Parser
11+-- Copyright : (c) 2020 Composewell Technologies
12+-- License : BSD3
13+-- Maintainer : streamly@composewell.com
14+-- Stability : experimental
15+-- Portability : GHC
16+--
17+-- Fast backtracking parsers with stream fusion and native streaming
18+-- capability.
19+--
20+-- 'Applicative' and 'Control.Applicative.Alternative' type class based
21+-- combinators from the
22+-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>
23+-- package can also be used with the 'Parser' type. However, there are two
24+-- important differences between @parser-combinators@ and the equivalent ones
25+-- provided in this module in terms of performance:
26+--
27+-- 1) @parser-combinators@ use plain Haskell lists to collect the results, in a
28+-- strict Monad like IO, the results are necessarily buffered before they can
29+-- be consumed. This may not perform optimally in streaming applications
30+-- processing large amounts of data. Equivalent combinators in this module can
31+-- consume the results of parsing using a 'Fold', thus providing a scalability
32+-- and a composable consumer.
33+--
34+-- 2) Several combinators in this module can be many times faster because of
35+-- stream fusion. For example, 'Streamly.Internal.Data.Parser.many' combinator
36+-- in this module is much faster than the 'Control.Applicative.many' combinator
37+-- of 'Control.Applicative.Alternative' type class.
38+--
39+-- = Errors
40+--
41+-- Failing parsers in this module throw the 'D.ParseError' exception.
42+--
43+-- = Naming
44+--
45+-- As far as possible, try that the names of the combinators in this module are
46+-- consistent with:
47+--
48+-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>
49+-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>
50+-- * <http://hackage.haskell.org/package/megaparsec megaparsec>
51+-- * <http://hackage.haskell.org/package/attoparsec attoparsec>
52+-- * <http://hackage.haskell.org/package/parsec parsec>
53+
54+module Streamly.Internal.Data.Parser
55+ (
56+ K.Parser (..)
57+ , D.ParseError (..)
58+ , D.Step (..)
59+
60+ -- First order parsers
61+ -- * Accumulators
62+ , fromFold
63+ , any
64+ , all
65+ , yield
66+ , yieldM
67+ , die
68+ , dieM
69+
70+ -- * Element parsers
71+ , peek
72+ , eof
73+ , satisfy
74+
75+ -- * Sequence parsers
76+ --
77+ -- | Parsers chained in series, if one parser terminates the composition
78+ -- terminates.
79+ --
80+ -- TODO: Currently we are using folds to collect the output of the parsers
81+ -- but we can use Parsers instead of folds to make the composition more
82+ -- powerful. For example, we can do:
83+ --
84+ -- @
85+ -- sliceSepByMax cond n p = sliceBy cond (take n p)
86+ -- sliceSepByBetween cond m n p = sliceBy cond (takeBetween m n p)
87+ -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
88+ -- @
89+ --
90+ -- TODO: Like toList fold we can have toNonEmpty Parser to fold to a
91+ -- nonempty list. If we cannot collect even one element the parser will
92+ -- fail. toNonEmpty can be used in takeWhile to implement takeWhile1.
93+
94+ -- | Grab a sequence of input elements without inspecting them
95+ , take
96+ -- $take
97+ -- | Unimplemented
98+ --
99+ -- @
100+ -- , takeBetween
101+ -- , takeLE -- take -- takeBetween 0 n
102+ -- , takeLE1 -- take1 -- takeBetween 1 n
103+ -- @
104+ , takeEQ -- takeBetween n n
105+ , takeGE -- takeBetween n maxBound
106+
107+ -- Grab a sequence of input elements by inspecting them
108+ , lookAhead
109+ , takeWhile
110+ -- $takeWhile
111+ , takeWhile1
112+ , sliceSepBy
113+ , sliceSepByMax
114+ -- | Unimplemented
115+ --
116+ -- @
117+ -- , sliceSepByBetween
118+ -- @
119+ , sliceEndWith
120+ , sliceBeginWith
121+ -- | Unimplemented
122+ --
123+ -- @
124+ -- , sliceSepWith
125+ --
126+ -- , frameSepBy -- parse frames escaped by an escape char/sequence
127+ -- , frameEndWith
128+ -- @
129+ , wordBy
130+ , groupBy
131+ , eqBy
132+ -- | Unimplemented
133+ --
134+ -- @
135+ -- , prefixOf -- match any prefix of a given string
136+ -- , suffixOf -- match any suffix of a given string
137+ -- , infixOf -- match any substring of a given string
138+ -- @
139+
140+ -- Second order parsers (parsers using parsers)
141+ -- * Binary Combinators
142+
143+ -- ** Sequential Applicative
144+ , splitWith
145+ , split_
146+
147+ -- ** Parallel Applicatives
148+ , teeWith
149+ , teeWithFst
150+ , teeWithMin
151+ -- , teeTill -- like manyTill but parallel
152+
153+ -- ** Sequential Interleaving
154+ -- Use two folds, run a primary parser, its rejected values go to the
155+ -- secondary parser.
156+ , deintercalate
157+
158+ -- ** Sequential Alternative
159+ , alt
160+
161+ -- ** Parallel Alternatives
162+ , shortest
163+ , longest
164+ -- , fastest
165+
166+ -- * N-ary Combinators
167+ -- ** Sequential Collection
168+ , sequence
169+ , concatMap
170+
171+ -- ** Sequential Repetition
172+ , count
173+ , countBetween
174+ -- , countBetweenTill
175+
176+ , many
177+ , some
178+ , manyTill
179+
180+ -- ** Special cases
181+ -- | TODO: traditional implmentations of these may be of limited use. For
182+ -- example, consider parsing lines separated by @\\r\\n@. The main parser
183+ -- will have to detect and exclude the sequence @\\r\\n@ anyway so that we
184+ -- can apply the "sep" parser.
185+ --
186+ -- We can instead implement these as special cases of deintercalate.
187+ --
188+ -- @
189+ -- , endBy
190+ -- , sepBy
191+ -- , sepEndBy
192+ -- , beginBy
193+ -- , sepBeginBy
194+ -- , sepAroundBy
195+ -- @
196+
197+ -- * Distribution
198+ --
199+ -- | A simple and stupid impl would be to just convert the stream to an
200+ -- array and give the array reference to all consumers. The array can be
201+ -- grown on demand by any consumer and truncated when nonbody needs it.
202+
203+ -- ** Distribute to collection
204+ -- ** Distribute to repetition
205+
206+ -- ** Interleaved collection
207+ -- |
208+ --
209+ -- 1. Round robin
210+ -- 2. Priority based
211+
212+ -- ** Interleaved repetition
213+ -- | repeat one parser and when it fails run an error recovery parser
214+ -- e.g. to find a key frame in the stream after an error
215+
216+ -- ** Collection of Alternatives
217+ -- | Unimplemented
218+ --
219+ -- @
220+ -- , shortestN
221+ -- , longestN
222+ -- , fastestN -- first N successful in time
223+ -- , choiceN -- first N successful in position
224+ -- @
225+ , choice -- first successful in position
226+
227+ -- ** Repeated Alternatives
228+ -- |
229+ --
230+ -- @
231+ -- , retryMax -- try N times
232+ -- , retryUntil -- try until successful
233+ -- , retryUntilN -- try until successful n times
234+ -- @
235+ )
236+where
237+
238+import Control.Monad.Catch (MonadCatch)
239+import Prelude
240+ hiding (any, all, take, takeWhile, sequence, concatMap)
241+
242+import Streamly.Internal.Data.Fold.Types (Fold(..))
243+import Streamly.Internal.Data.Parser.ParserK.Types (Parser)
244+
245+import qualified Streamly.Internal.Data.Parser.ParserD as D
246+import qualified Streamly.Internal.Data.Parser.ParserK.Types as K
247+
248+-------------------------------------------------------------------------------
249+-- Upgrade folds to parses
250+-------------------------------------------------------------------------------
251+--
252+-- | Make a 'Parser' from a 'Fold'.
253+--
254+-- /Internal/
255+--
256+{-# INLINE fromFold #-}
257+fromFold :: MonadCatch m => Fold m a b -> Parser m a b
258+fromFold = D.toParserK . D.fromFold
259+
260+-------------------------------------------------------------------------------
261+-- Terminating but not failing folds
262+-------------------------------------------------------------------------------
263+--
264+-- |
265+-- >>> S.parse (PR.any (== 0)) $ S.fromList [1,0,1]
266+-- > True
267+--
268+{-# INLINE any #-}
269+any :: MonadCatch m => (a -> Bool) -> Parser m a Bool
270+any = D.toParserK . D.any
271+
272+-- |
273+-- >>> S.parse (PR.all (== 0)) $ S.fromList [1,0,1]
274+-- > False
275+--
276+{-# INLINE all #-}
277+all :: MonadCatch m => (a -> Bool) -> Parser m a Bool
278+all = D.toParserK . D.all
279+
280+-- This is the dual of stream "yield".
281+--
282+-- | A parser that always yields a pure value without consuming any input.
283+--
284+-- /Internal/
285+--
286+{-# INLINE [3] yield #-}
287+yield :: MonadCatch m => b -> Parser m a b
288+yield = D.toParserK . D.yield
289+{-# RULES "yield fallback to CPS" [2]
290+ forall a. D.toParserK (D.yield a) = K.yield a #-}
291+
292+-- This is the dual of stream "yieldM".
293+--
294+-- | A parser that always yields the result of an effectful action without
295+-- consuming any input.
296+--
297+-- /Internal/
298+--
299+{-# INLINE yieldM #-}
300+yieldM :: MonadCatch m => m b -> Parser m a b
301+yieldM = D.toParserK . D.yieldM
302+
303+-- This is the dual of "nil".
304+--
305+-- | A parser that always fails with an error message without consuming
306+-- any input.
307+--
308+-- /Internal/
309+--
310+{-# INLINE [3] die #-}
311+die :: MonadCatch m => String -> Parser m a b
312+die = D.toParserK . D.die
313+{-# RULES "die fallback to CPS" [2]
314+ forall a. D.toParserK (D.die a) = K.die a #-}
315+
316+-- This is the dual of "nilM".
317+--
318+-- | A parser that always fails with an effectful error message and without
319+-- consuming any input.
320+--
321+-- /Internal/
322+--
323+{-# INLINE dieM #-}
324+dieM :: MonadCatch m => m String -> Parser m a b
325+dieM = D.toParserK . D.dieM
326+
327+-------------------------------------------------------------------------------
328+-- Failing Parsers
329+-------------------------------------------------------------------------------
330+
331+-- | Peek the head element of a stream, without consuming it. Fails if it
332+-- encounters end of input.
333+--
334+-- >>> S.parse ((,) <$> PR.peek <*> PR.satisfy (> 0)) $ S.fromList [1]
335+-- (1,1)
336+--
337+-- @
338+-- peek = lookAhead (satisfy True)
339+-- @
340+--
341+-- /Internal/
342+--
343+{-# INLINE peek #-}
344+peek :: MonadCatch m => Parser m a a
345+peek = D.toParserK D.peek
346+
347+-- | Succeeds if we are at the end of input, fails otherwise.
348+--
349+-- >>> S.parse ((,) <$> PR.satisfy (> 0) <*> PR.eof) $ S.fromList [1]
350+-- > (1,())
351+--
352+-- /Internal/
353+--
354+{-# INLINE eof #-}
355+eof :: MonadCatch m => Parser m a ()
356+eof = D.toParserK D.eof
357+
358+-- | Returns the next element if it passes the predicate, fails otherwise.
359+--
360+-- >>> S.parse (PR.satisfy (== 1)) $ S.fromList [1,0,1]
361+-- > 1
362+--
363+-- /Internal/
364+--
365+{-# INLINE satisfy #-}
366+satisfy :: MonadCatch m => (a -> Bool) -> Parser m a a
367+satisfy = D.toParserK . D.satisfy
368+
369+-------------------------------------------------------------------------------
370+-- Taking elements
371+-------------------------------------------------------------------------------
372+--
373+-- $take
374+-- Note: this is called takeP in some parser libraries.
375+--
376+-- TODO Once we have terminating folds, this Parse should get replaced by Fold.
377+-- Alternatively, we can name it "chunkOf" and the corresponding time domain
378+-- combinator as "intervalOf" or even "chunk" and "interval".
379+
380+-- | Take at most @n@ input elements and fold them using the supplied fold.
381+--
382+-- Stops after @n@ elements.
383+-- Never fails.
384+--
385+-- >>> S.parse (PR.take 1 FL.toList) $ S.fromList [1]
386+-- [1]
387+--
388+-- @
389+-- S.chunksOf n f = S.splitParse (FL.take n f)
390+-- @
391+--
392+-- /Internal/
393+--
394+{-# INLINE take #-}
395+take :: MonadCatch m => Int -> Fold m a b -> Parser m a b
396+take n = D.toParserK . D.take n
397+
398+-- | Stops after taking exactly @n@ input elements.
399+--
400+-- * Stops - after @n@ elements.
401+-- * Fails - if the stream ends before it can collect @n@ elements.
402+--
403+-- >>> S.parse (PR.takeEQ 4 FL.toList) $ S.fromList [1,0,1]
404+-- > "takeEQ: Expecting exactly 4 elements, got 3"
405+--
406+-- /Internal/
407+--
408+{-# INLINE takeEQ #-}
409+takeEQ :: MonadCatch m => Int -> Fold m a b -> Parser m a b
410+takeEQ n = D.toParserK . D.takeEQ n
411+
412+-- | Take at least @n@ input elements, but can collect more.
413+--
414+-- * Stops - never.
415+-- * Fails - if the stream end before producing @n@ elements.
416+--
417+-- >>> S.parse (PR.takeGE 4 FL.toList) $ S.fromList [1,0,1]
418+-- > "takeGE: Expecting at least 4 elements, got only 3"
419+--
420+-- >>> S.parse (PR.takeGE 4 FL.toList) $ S.fromList [1,0,1,0,1]
421+-- > [1,0,1,0,1]
422+--
423+-- /Internal/
424+--
425+{-# INLINE takeGE #-}
426+takeGE :: MonadCatch m => Int -> Fold m a b -> Parser m a b
427+takeGE n = D.toParserK . D.takeGE n
428+
429+-- $takeWhile
430+-- Note: This is called @takeWhileP@ and @munch@ in some parser libraries.
431+
432+-- | Collect stream elements until an element fails the predicate. The element
433+-- on which the predicate fails is returned back to the input stream.
434+--
435+-- * Stops - when the predicate fails.
436+-- * Fails - never.
437+--
438+-- >>> S.parse (PR.takeWhile (== 0) FL.toList) $ S.fromList [0,0,1,0,1]
439+-- > [0,0]
440+--
441+-- We can implement a @breakOn@ using 'takeWhile':
442+--
443+-- @
444+-- breakOn p = takeWhile (not p)
445+-- @
446+--
447+-- /Internal/
448+--
449+{-# INLINE takeWhile #-}
450+takeWhile :: MonadCatch m => (a -> Bool) -> Fold m a b -> Parser m a b
451+takeWhile cond = D.toParserK . D.takeWhile cond
452+
453+-- | Like 'takeWhile' but takes at least one element otherwise fails.
454+--
455+-- /Internal/
456+--
457+{-# INLINE takeWhile1 #-}
458+takeWhile1 :: MonadCatch m => (a -> Bool) -> Fold m a b -> Parser m a b
459+takeWhile1 cond = D.toParserK . D.takeWhile1 cond
460+
461+-- | Collect stream elements until an element succeeds the predicate. Drop the
462+-- element on which the predicate succeeded. The succeeding element is treated
463+-- as an infix separator which is dropped from the output.
464+--
465+-- * Stops - when the predicate succeeds.
466+-- * Fails - never.
467+--
468+-- >>> S.parse (PR.sliceSepBy (== 1) FL.toList) $ S.fromList [0,0,1,0,1]
469+-- > [0,0]
470+--
471+-- S.splitOn pred f = S.splitParse (PR.sliceSepBy pred f)
472+--
473+-- >>> S.toList $ S.splitParse (PR.sliceSepBy (== 1) FL.toList) $ S.fromList [0,0,1,0,1]
474+-- > [[0,0],[0],[]]
475+--
476+-- /Internal/
477+--
478+{-# INLINABLE sliceSepBy #-}
479+sliceSepBy :: MonadCatch m => (a -> Bool) -> Fold m a b -> Parser m a b
480+sliceSepBy cond = D.toParserK . D.sliceSepBy cond
481+
482+-- | Collect stream elements until an element succeeds the predicate. Also take
483+-- the element on which the predicate succeeded. The succeeding element is
484+-- treated as a suffix separator which is kept in the output segement.
485+--
486+-- * Stops - when the predicate succeeds.
487+-- * Fails - never.
488+--
489+-- S.splitWithSuffix pred f = S.splitParse (PR.sliceEndWith pred f)
490+--
491+-- /Unimplemented/
492+--
493+{-# INLINABLE sliceEndWith #-}
494+sliceEndWith ::
495+ -- Monad m =>
496+ (a -> Bool) -> Fold m a b -> Parser m a b
497+sliceEndWith = undefined
498+
499+-- | Collect stream elements until an elements passes the predicate, return the
500+-- last element on which the predicate succeeded back to the input stream. If
501+-- the predicate succeeds on the first element itself then it is kept in the
502+-- stream and we continue collecting. The succeeding element is treated as a
503+-- prefix separator which is kept in the output segement.
504+--
505+-- * Stops - when the predicate succeeds in non-leading position.
506+-- * Fails - never.
507+--
508+-- S.splitWithPrefix pred f = S.splitParse (PR.sliceBeginWith pred f)
509+--
510+-- /Unimplemented/
511+--
512+{-# INLINABLE sliceBeginWith #-}
513+sliceBeginWith ::
514+ -- Monad m =>
515+ (a -> Bool) -> Fold m a b -> Parser m a b
516+sliceBeginWith = undefined
517+
518+-- | Split using a condition or a count whichever occurs first. This is a
519+-- hybrid of 'splitOn' and 'take'. The element on which the condition succeeds
520+-- is dropped.
521+--
522+-- /Internal/
523+--
524+{-# INLINABLE sliceSepByMax #-}
525+sliceSepByMax :: MonadCatch m
526+ => (a -> Bool) -> Int -> Fold m a b -> Parser m a b
527+sliceSepByMax cond cnt = D.toParserK . D.sliceSepByMax cond cnt
528+
529+-- | Like 'splitOn' but strips leading, trailing, and repeated separators.
530+-- Therefore, @".a..b."@ having '.' as the separator would be parsed as
531+-- @["a","b"]@. In other words, its like parsing words from whitespace
532+-- separated text.
533+--
534+-- * Stops - when it finds a word separator after a non-word element
535+-- * Fails - never.
536+--
537+-- @
538+-- S.wordsBy pred f = S.splitParse (PR.wordBy pred f)
539+-- @
540+--
541+-- /Unimplemented/
542+--
543+{-# INLINABLE wordBy #-}
544+wordBy ::
545+ -- Monad m =>
546+ (a -> Bool) -> Fold m a b -> Parser m a b
547+wordBy = undefined
548+
549+-- | @groupBy cmp f $ S.fromList [a,b,c,...]@ assigns the element @a@ to the
550+-- first group, then if @a \`cmp` b@ is 'True' @b@ is also assigned to the same
551+-- group. If @a \`cmp` c@ is 'True' then @c@ is also assigned to the same
552+-- group and so on. When the comparison fails a new group is started. Each
553+-- group is folded using the 'Fold' @f@ and the result of the fold is emitted
554+-- in the output stream.
555+--
556+-- * Stops - when the comparison fails.
557+-- * Fails - never.
558+--
559+-- @
560+-- S.groupsBy cmp f = S.splitParse (PR.groupBy cmp f)
561+-- @
562+--
563+-- /Unimplemented/
564+--
565+{-# INLINABLE groupBy #-}
566+groupBy ::
567+ -- Monad m =>
568+ (a -> a -> Bool) -> Fold m a b -> Parser m a b
569+groupBy = undefined
570+
571+-- | Match the given sequence of elements using the given comparison function.
572+--
573+-- /Internal/
574+--
575+{-# INLINE eqBy #-}
576+eqBy :: MonadCatch m => (a -> a -> Bool) -> [a] -> Parser m a ()
577+eqBy cmp = D.toParserK . D.eqBy cmp
578+
579+-------------------------------------------------------------------------------
580+-- nested parsers
581+-------------------------------------------------------------------------------
582+
583+-- | Sequential parser application. Apply two parsers sequentially to an input
584+-- stream. The input is provided to the first parser, when it is done the
585+-- remaining input is provided to the second parser. If both the parsers
586+-- succeed their outputs are combined using the supplied function. The
587+-- operation fails if any of the parsers fail.
588+--
589+-- Note: This is a parsing dual of appending streams using
590+-- 'Streamly.Prelude.serial', it splits the streams using two parsers and zips
591+-- the results.
592+--
593+-- This implementation is strict in the second argument, therefore, the
594+-- following will fail:
595+--
596+-- >>> S.parse (PR.splitWith const (PR.satisfy (> 0)) undefined) $ S.fromList [1]
597+--
598+-- Compare with 'Applicative' instance method '<*>'. This implementation allows
599+-- stream fusion but has quadratic complexity. This can fuse with other
600+-- operations and can be faster than 'Applicative' instance for small number
601+-- (less than 8) of compositions.
602+--
603+-- /Internal/
604+--
605+{-# INLINE splitWith #-}
606+splitWith :: MonadCatch m
607+ => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
608+splitWith f p1 p2 =
609+ D.toParserK $ D.splitWith f (D.fromParserK p1) (D.fromParserK p2)
610+
611+-- | Sequential parser application ignoring the output of the first parser.
612+-- Apply two parsers sequentially to an input stream. The input is provided to
613+-- the first parser, when it is done the remaining input is provided to the
614+-- second parser. The output of the parser is the output of the second parser.
615+-- The operation fails if any of the parsers fail.
616+--
617+-- This implementation is strict in the second argument, therefore, the
618+-- following will fail:
619+--
620+-- >>> S.parse (split_ (PR.satisfy (> 0)) undefined) $ S.fromList [1]
621+--
622+-- Compare with 'Applicative' instance method '*>'. This implementation allows
623+-- stream fusion but has quadratic complexity. This can fuse with other
624+-- operations, and can be faster than 'Applicative' instance for small
625+-- number (less than 8) of compositions.
626+--
627+-- /Internal/
628+--
629+{-# INLINE split_ #-}
630+split_ :: MonadCatch m => Parser m x a -> Parser m x b -> Parser m x b
631+split_ p1 p2 = D.toParserK $ D.split_ (D.fromParserK p1) (D.fromParserK p2)
632+
633+-- | @teeWith f p1 p2@ distributes its input to both @p1@ and @p2@ until both
634+-- of them succeed or anyone of them fails and combines their output using @f@.
635+-- The parser succeeds if both the parsers succeed.
636+--
637+-- /Internal/
638+--
639+{-# INLINE teeWith #-}
640+teeWith :: MonadCatch m
641+ => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
642+teeWith f p1 p2 =
643+ D.toParserK $ D.teeWith f (D.fromParserK p1) (D.fromParserK p2)
644+
645+-- | Like 'teeWith' but ends parsing and zips the results, if available,
646+-- whenever the first parser ends.
647+--
648+-- /Internal/
649+--
650+{-# INLINE teeWithFst #-}
651+teeWithFst :: MonadCatch m
652+ => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
653+teeWithFst f p1 p2 =
654+ D.toParserK $ D.teeWithFst f (D.fromParserK p1) (D.fromParserK p2)
655+
656+-- | Like 'teeWith' but ends parsing and zips the results, if available,
657+-- whenever any of the parsers ends or fails.
658+--
659+-- /Unimplemented/
660+--
661+{-# INLINE teeWithMin #-}
662+teeWithMin :: MonadCatch m
663+ => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
664+teeWithMin f p1 p2 =
665+ D.toParserK $ D.teeWithMin f (D.fromParserK p1) (D.fromParserK p2)
666+
667+-- | Sequential alternative. Apply the input to the first parser and return the
668+-- result if the parser succeeds. If the first parser fails then backtrack and
669+-- apply the same input to the second parser and return the result.
670+--
671+-- Note: This implementation is not lazy in the second argument. The following
672+-- will fail:
673+--
674+-- >>> S.parse (PR.satisfy (> 0) `PR.alt` undefined) $ S.fromList [1..10]
675+--
676+-- Compare with 'Alternative' instance method '<|>'. This implementation allows
677+-- stream fusion but has quadratic complexity. This can fuse with other
678+-- operations and can be much faster than 'Alternative' instance for small
679+-- number (less than 8) of alternatives.
680+--
681+-- /Internal/
682+--
683+{-# INLINE alt #-}
684+alt :: MonadCatch m => Parser m x a -> Parser m x a -> Parser m x a
685+alt p1 p2 = D.toParserK $ D.alt (D.fromParserK p1) (D.fromParserK p2)
686+
687+-- | Shortest alternative. Apply both parsers in parallel but choose the result
688+-- from the one which consumed least input i.e. take the shortest succeeding
689+-- parse.
690+--
691+-- /Internal/
692+--
693+{-# INLINE shortest #-}
694+shortest :: MonadCatch m
695+ => Parser m x a -> Parser m x a -> Parser m x a
696+shortest p1 p2 = D.toParserK $ D.shortest (D.fromParserK p1) (D.fromParserK p2)
697+
698+-- | Longest alternative. Apply both parsers in parallel but choose the result
699+-- from the one which consumed more input i.e. take the longest succeeding
700+-- parse.
701+--
702+-- /Internal/
703+--
704+{-# INLINE longest #-}
705+longest :: MonadCatch m
706+ => Parser m x a -> Parser m x a -> Parser m x a
707+longest p1 p2 = D.toParserK $ D.longest (D.fromParserK p1) (D.fromParserK p2)
708+
709+-- | Run a parser without consuming the input.
710+--
711+-- /Internal/
712+--
713+{-# INLINE lookAhead #-}
714+lookAhead :: MonadCatch m => Parser m a b -> Parser m a b
715+lookAhead p = D.toParserK $ D.lookAhead $ D.fromParserK p
716+
717+-------------------------------------------------------------------------------
718+-- Interleaving
719+-------------------------------------------------------------------------------
720+--
721+-- To deinterleave we can chain two parsers one behind the other. The input is
722+-- given to the first parser and the input definitively rejected by the first
723+-- parser is given to the second parser.
724+--
725+-- We can either have the parsers themselves buffer the input or use the shared
726+-- global buffer to hold it until none of the parsers need it. When the first
727+-- parser returns Skip (i.e. rewind) we let the second parser consume the
728+-- rejected input and when it is done we move the cursor forward to the first
729+-- parser again. This will require a "move forward" command as well.
730+--
731+-- To implement grep we can use three parsers, one to find the pattern, one
732+-- to store the context behind the pattern and one to store the context in
733+-- front of the pattern. When a match occurs we need to emit the accumulator of
734+-- all the three parsers. One parser can count the line numbers to provide the
735+-- line number info.
736+--
737+-- | Apply two parsers alternately to an input stream. The input stream is
738+-- considered an interleaving of two patterns. The two parsers represent the
739+-- two patterns.
740+--
741+-- This undoes a "gintercalate" of two streams.
742+--
743+-- /Unimplemented/
744+--
745+{-# INLINE deintercalate #-}
746+deintercalate ::
747+ -- Monad m =>
748+ Fold m a y -> Parser m x a
749+ -> Fold m b z -> Parser m x b
750+ -> Parser m x (y, z)
751+deintercalate = undefined
752+
753+-------------------------------------------------------------------------------
754+-- Sequential Collection
755+-------------------------------------------------------------------------------
756+--
757+-- | @sequence f t@ collects sequential parses of parsers in the container @t@
758+-- using the fold @f@. Fails if the input ends or any of the parsers fail.
759+--
760+-- /Unimplemented/
761+--
762+{-# INLINE sequence #-}
763+sequence ::
764+ -- Foldable t =>
765+ Fold m b c -> t (Parser m a b) -> Parser m a c
766+sequence _f _p = undefined
767+
768+-- | Map a 'Parser' returning function on the result of a 'Parser'.
769+--
770+-- Compare with 'Monad' instance method '>>='. This implementation allows
771+-- stream fusion but has quadratic complexity. This can fuse with other
772+-- operations and can be much faster than 'Monad' instance for small number
773+-- (less than 8) of compositions.
774+--
775+-- /Internal/
776+--
777+{-# INLINE concatMap #-}
778+concatMap :: MonadCatch m
779+ => (b -> Parser m a c) -> Parser m a b -> Parser m a c
780+concatMap f p = D.toParserK $ D.concatMap (D.fromParserK . f) (D.fromParserK p)
781+
782+-------------------------------------------------------------------------------
783+-- Alternative Collection
784+-------------------------------------------------------------------------------
785+--
786+-- | @choice parsers@ applies the @parsers@ in order and returns the first
787+-- successful parse.
788+--
789+{-# INLINE choice #-}
790+choice ::
791+ -- Foldable t =>
792+ t (Parser m a b) -> Parser m a b
793+choice _ps = undefined
794+
795+-------------------------------------------------------------------------------
796+-- Sequential Repetition
797+-------------------------------------------------------------------------------
798+--
799+-- $many
800+-- TODO "many" is essentially a Fold because it cannot fail. So it can be
801+-- downgraded to a Fold. Or we can make the return type a Fold instead and
802+-- upgrade that to a parser when needed.
803+
804+-- | Collect zero or more parses. Apply the parser repeatedly on the input
805+-- stream, stop when the parser fails, accumulate zero or more parse results
806+-- using the supplied 'Fold'. This parser never fails, in case the first
807+-- application of parser fails it returns an empty result.
808+--
809+-- Compare with 'Control.Applicative.many'.
810+--
811+-- /Internal/
812+--
813+{-# INLINE many #-}
814+many :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
815+many f p = D.toParserK $ D.many f (D.fromParserK p)
816+-- many = countBetween 0 maxBound
817+
818+-- | Collect one or more parses. Apply the supplied parser repeatedly on the
819+-- input stream and accumulate the parse results as long as the parser
820+-- succeeds, stop when it fails. This parser fails if not even one result is
821+-- collected.
822+--
823+-- Compare with 'Control.Applicative.some'.
824+--
825+-- /Internal/
826+--
827+{-# INLINE some #-}
828+some :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
829+some f p = D.toParserK $ D.some f (D.fromParserK p)
830+-- some f p = many (takeGE 1 f) p
831+-- many = countBetween 1 maxBound
832+
833+-- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of
834+-- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if
835+-- the input ends or the parser fails before @m@ results are collected.
836+--
837+-- /Unimplemented/
838+--
839+{-# INLINE countBetween #-}
840+countBetween ::
841+ -- MonadCatch m =>
842+ Int -> Int -> Fold m b c -> Parser m a b -> Parser m a c
843+countBetween _m _n _f = undefined
844+-- countBetween m n f p = many (takeBetween m n f) p
845+
846+-- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using
847+-- the fold @f@. Fails if the input ends or the parser fails before @n@
848+-- results are collected.
849+--
850+-- /Unimplemented/
851+--
852+{-# INLINE count #-}
853+count ::
854+ -- MonadCatch m =>
855+ Int -> Fold m b c -> Parser m a b -> Parser m a c
856+count n = countBetween n n
857+-- count n f p = many (takeEQ n f) p
858+
859+-- | @manyTill f collect test@ tries the parser @test@ on the input, if @test@
860+-- fails it backtracks and tries @collect@, after @collect@ succeeds @test@ is
861+-- tried again and so on. The parser stops when @test@ succeeds. The output of
862+-- @test@ is discarded and the output of @collect@ is accumulated by the
863+-- supplied fold. The parser fails if @collect@ fails.
864+--
865+-- /Internal/
866+--
867+{-# INLINE manyTill #-}
868+manyTill :: MonadCatch m
869+ => Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
870+manyTill f p1 p2 =
871+ D.toParserK $ D.manyTill f (D.fromParserK p1) (D.fromParserK p2)
src/Streamly/Internal/Data/Parser/ParserD.hs+73−219
@@ -2,6 +2,8 @@
22{-# LANGUAGE RankNTypes #-}
33{-# LANGUAGE ScopedTypeVariables #-}
44
5+#include "inline.hs"
6+
57-- |
68-- Module : Streamly.Internal.Data.Parser.ParserD
79-- Copyright : (c) 2020 Composewell Technologies
@@ -10,40 +12,19 @@
1012-- Stability : experimental
1113-- Portability : GHC
1214--
13−-- Fast streaming parsers.
14−--
15−-- 'Applicative' and 'Alternative' type class based 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 generic 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 'Alternative' type class.
32−--
33−-- Failing parsers in this module throw the 'ParseError' exception.
34−
35−-- XXX As far as possible, try that the combinators in this module and in
36−-- "Text.ParserCombinators.ReadP/parser-combinators/parsec/megaparsec/attoparsec"
37−-- have consistent names. takeP/takeWhileP/munch?
15+-- Direct style parser implementation with stream fusion.
3816
3917module Streamly.Internal.Data.Parser.ParserD
4018 (
4119 Parser (..)
20+ , ParseError (..)
21+ , Step (..)
4222
4323 -- First order parsers
4424 -- * Accumulators
4525 , fromFold
4626 , toParserK
27+ , fromParserK
4728 , any
4829 , all
4930 , yield
@@ -101,6 +82,7 @@ module Streamly.Internal.Data.Parser.ParserD
10182
10283 -- ** Sequential Applicative
10384 , splitWith
85+ , split_
10486
10587 -- ** Parallel Applicatives
10688 , teeWith
@@ -113,6 +95,9 @@ module Streamly.Internal.Data.Parser.ParserD
11395 -- secondary parser.
11496 , deintercalate
11597
98+ -- ** Sequential Alternative
99+ , alt
100+
116101 -- ** Parallel Alternatives
117102 , shortest
118103 , longest
@@ -121,6 +106,7 @@ module Streamly.Internal.Data.Parser.ParserD
121106 -- * N-ary Combinators
122107 -- ** Sequential Collection
123108 , sequence
109+ , concatMap
124110
125111 -- ** Sequential Repetition
126112 , count
@@ -179,7 +165,7 @@ where
179165import Control.Exception (assert)
180166import Control.Monad.Catch (MonadCatch, MonadThrow(..))
181167import Prelude
182− hiding (any, all, take, takeWhile, sequence)
168+ hiding (any, all, take, takeWhile, sequence, concatMap)
183169
184170import Streamly.Internal.Data.Fold.Types (Fold(..))
185171
@@ -194,7 +180,9 @@ import Streamly.Internal.Data.Strict
194180-- Upgrade folds to parses
195181-------------------------------------------------------------------------------
196182--
197−-- | The resulting parse never terminates and never errors out.
183+-- | See 'Streamly.Internal.Data.Parser.fromFold'.
184+--
185+-- /Internal/
198186--
199187{-# INLINE fromFold #-}
200188fromFold :: Monad m => Fold m a b -> Parser m a b
@@ -205,24 +193,45 @@ fromFold (Fold fstep finitial fextract) = Parser step finitial fextract
205193 step s a = Yield 0 <$> fstep s a
206194
207195-------------------------------------------------------------------------------
208−-- Convert to CPS style parser representation
196+-- Convert to and from CPS style parser representation
209197-------------------------------------------------------------------------------
210198
211−{-# INLINE toParserK #-}
199+-- | Convert a direct style 'Parser' to a CPS style 'K.Parser'.
200+--
201+-- /Internal/
202+--
203+{-# INLINE_LATE toParserK #-}
212204toParserK :: MonadCatch m => Parser m a b -> K.Parser m a b
213205toParserK (Parser step initial extract) =
214206 K.MkParser $ \inp yieldk ->
215207 Z.parse step initial extract inp >>= yieldk
216208
209+-- | Convert a CPS style 'K.Parser' to a direct style 'Parser'.
210+--
211+-- /Unimplemented/
212+--
213+{-# NOINLINE fromParserK #-}
214+fromParserK :: Monad m => K.Parser m a b -> Parser m a b
215+fromParserK _ = Parser step initial extract
216+
217+ where
218+
219+ initial = return ()
220+ step () _ = error "fromParserK: unimplemented"
221+ extract () = error "fromParserK: unimplemented"
222+
223+#ifndef DISABLE_FUSION
224+{-# RULES "fromParserK/toParserK fusion" [2]
225+ forall s. toParserK (fromParserK s) = s #-}
226+{-# RULES "toParserK/fromParserK fusion" [2]
227+ forall s. fromParserK (toParserK s) = s #-}
228+#endif
229+
217230-------------------------------------------------------------------------------
218231-- Terminating but not failing folds
219232-------------------------------------------------------------------------------
220233--
221−-- |
222−-- >>> S.parse (PR.any (== 0)) $ S.fromList [1,0,1]
223−-- > Right True
224−--
225−{-# INLINABLE any #-}
234+{-# INLINE any #-}
226235any :: Monad m => (a -> Bool) -> Parser m a Bool
227236any predicate = Parser step initial return
228237
@@ -238,10 +247,6 @@ any predicate = Parser step initial return
238247 then Stop 0 True
239248 else Yield 0 False
240249
241−-- |
242−-- >>> S.parse (PR.all (== 0)) $ S.fromList [1,0,1]
243−-- > Right False
244−--
245250{-# INLINABLE all #-}
246251all :: Monad m => (a -> Bool) -> Parser m a Bool
247252all predicate = Parser step initial return
@@ -262,15 +267,7 @@ all predicate = Parser step initial return
262267-- Failing Parsers
263268-------------------------------------------------------------------------------
264269
265−-- | Peek the head element of a stream, without consuming it. Fails if it
266−-- encounters end of input.
267−--
268−-- >>> S.parse ((,) <$> PR.peek <*> PR.satisfy (> 0)) $ S.fromList [1]
269−-- (1,1)
270−--
271−-- @
272−-- peek = lookAhead (satisfy True)
273−-- @
270+-- | See 'Streamly.Internal.Data.Parser.peek'.
274271--
275272-- /Internal/
276273--
@@ -286,10 +283,7 @@ peek = Parser step initial extract
286283
287284 extract () = throwM $ ParseError "peek: end of input"
288285
289−-- | Succeeds if we are at the end of input, fails otherwise.
290−--
291−-- >>> S.parse ((,) <$> PR.satisfy (> 0) <*> PR.eof) $ S.fromList [1]
292−-- > (1,())
286+-- | See 'Streamly.Internal.Data.Parser.eof'.
293287--
294288-- /Internal/
295289--
@@ -303,10 +297,7 @@ eof = Parser step initial return
303297
304298 step () _ = return $ Error "eof: not at end of input"
305299
306−-- | Returns the next element if it passes the predicate, fails otherwise.
307−--
308−-- >>> S.parse (PR.satisfy (== 1)) $ S.fromList [1,0,1]
309−-- > 1
300+-- | See 'Streamly.Internal.Data.Parser.satisfy'.
310301--
311302-- /Internal/
312303--
@@ -329,21 +320,7 @@ satisfy predicate = Parser step initial extract
329320-- Taking elements
330321-------------------------------------------------------------------------------
331322--
332−-- XXX Once we have terminating folds, this Parse should get replaced by Fold.
333−-- Alternatively, we can name it "chunkOf" and the corresponding time domain
334−-- combinator as "intervalOf" or even "chunk" and "interval".
335−--
336−-- | Take at most @n@ input elements and fold them using the supplied fold.
337−--
338−-- Stops after @n@ elements.
339−-- Never fails.
340−--
341−-- >>> S.parse (PR.take 1 FL.toList) $ S.fromList [1]
342−-- [1]
343−--
344−-- @
345−-- S.chunksOf n f = S.splitParse (FL.take n f)
346−-- @
323+-- | See 'Streamly.Internal.Data.Parser.take'.
347324--
348325-- /Internal/
349326--
@@ -365,16 +342,7 @@ take n (Fold fstep finitial fextract) = Parser step initial extract
365342
366343 extract (Tuple' _ r) = fextract r
367344
368−--
369−-- XXX can we use a "cmp" operation in a common implementation?
370−--
371−-- | Stops after taking exactly @n@ input elements.
372−--
373−-- * Stops - after @n@ elements.
374−-- * Fails - if the stream ends before it can collect @n@ elements.
375−--
376−-- >>> S.parse (PR.takeEQ 4 FL.toList) $ S.fromList [1,0,1]
377−-- > "takeEQ: Expecting exactly 4 elements, got 3"
345+-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
378346--
379347-- /Internal/
380348--
@@ -403,16 +371,7 @@ takeEQ n (Fold fstep finitial fextract) = Parser step initial extract
403371 "takeEQ: Expecting exactly " ++ show n
404372 ++ " elements, got " ++ show i
405373
406−-- | Take at least @n@ input elements, but can collect more.
407−--
408−-- * Stops - never.
409−-- * Fails - if the stream ends before producing @n@ elements.
410−--
411−-- >>> S.parse (PR.takeGE 4 FL.toList) $ S.fromList [1,0,1]
412−-- > "takeGE: Expecting at least 4 elements, got only 3"
413−--
414−-- >>> S.parse (PR.takeGE 4 FL.toList) $ S.fromList [1,0,1,0,1]
415−-- > [1,0,1,0,1]
374+-- | See 'Streamly.Internal.Data.Parser.takeGE'.
416375--
417376-- /Internal/
418377--
@@ -446,20 +405,7 @@ takeGE n (Fold fstep finitial fextract) = Parser step initial extract
446405 then return x
447406 else throwM $ ParseError err
448407
449−-- | Collect stream elements until an element fails the predicate. The element
450−-- on which the predicate fails is returned back to the input stream.
451−--
452−-- * Stops - when the predicate fails.
453−-- * Fails - never.
454−--
455−-- >>> S.parse (PR.takeWhile (== 0) FL.toList) $ S.fromList [0,0,1,0,1]
456−-- > [0,0]
457−--
458−-- We can implement a @breakOn@ using 'takeWhile':
459−--
460−-- @
461−-- breakOn p = takeWhile (not p)
462−-- @
408+-- | See 'Streamly.Internal.Data.Parser.takeWhile'.
463409--
464410-- /Internal/
465411--
@@ -477,7 +423,7 @@ takeWhile predicate (Fold fstep finitial fextract) =
477423 then Yield 0 <$> fstep s a
478424 else Stop 1 <$> fextract s
479425
480−-- | Like 'takeWhile' but takes at least one element otherwise fails.
426+-- | See 'Streamly.Internal.Data.Parser.takeWhile1'.
481427--
482428-- /Internal/
483429--
@@ -509,20 +455,7 @@ takeWhile1 predicate (Fold fstep finitial fextract) =
509455 extract Nothing = throwM $ ParseError "takeWhile1: end of input"
510456 extract (Just s) = fextract s
511457
512−-- | Collect stream elements until an element succeeds the predicate. Drop the
513−-- element on which the predicate succeeded. The succeeding element is treated
514−-- as an infix separator which is dropped from the output.
515−--
516−-- * Stops - when the predicate succeeds.
517−-- * Fails - never.
518−--
519−-- >>> S.parse (PR.sliceSepBy (== 1) FL.toList) $ S.fromList [0,0,1,0,1]
520−-- > [0,0]
521−--
522−-- S.splitOn pred f = S.splitParse (PR.sliceSepBy pred f)
523−--
524−-- >>> S.toList $ S.splitParse (PR.sliceSepBy (== 1) FL.toList) $ S.fromList [0,0,1,0,1]
525−-- > [[0,0],[0],[]]
458+-- | See 'Streamly.Internal.Data.Parser.sliceSepBy'.
526459--
527460-- /Internal/
528461--
@@ -539,14 +472,7 @@ sliceSepBy predicate (Fold fstep finitial fextract) =
539472 then Yield 0 <$> fstep s a
540473 else Stop 0 <$> fextract s
541474
542−-- | Collect stream elements until an element succeeds the predicate. Also take
543−-- the element on which the predicate succeeded. The succeeding element is
544−-- treated as a suffix separator which is kept in the output segement.
545−--
546−-- * Stops - when the predicate succeeds.
547−-- * Fails - never.
548−--
549−-- S.splitWithSuffix pred f = S.splitParse (PR.sliceEndWith pred f)
475+-- | See 'Streamly.Internal.Data.Parser.sliceEndWith'.
550476--
551477-- /Unimplemented/
552478--
@@ -556,16 +482,7 @@ sliceEndWith ::
556482 (a -> Bool) -> Fold m a b -> Parser m a b
557483sliceEndWith = undefined
558484
559−-- | Collect stream elements until an elements passes the predicate, return the
560−-- last element on which the predicate succeeded back to the input stream. If
561−-- the predicate succeeds on the first element itself then it is kept in the
562−-- stream and we continue collecting. The succeeding element is treated as a
563−-- prefix separator which is kept in the output segement.
564−--
565−-- * Stops - when the predicate succeeds in non-leading position.
566−-- * Fails - never.
567−--
568−-- S.splitWithPrefix pred f = S.splitParse (PR.sliceBeginWith pred f)
485+-- | See 'Streamly.Internal.Data.Parser.sliceBeginWith'.
569486--
570487-- /Unimplemented/
571488--
@@ -575,9 +492,7 @@ sliceBeginWith ::
575492 (a -> Bool) -> Fold m a b -> Parser m a b
576493sliceBeginWith = undefined
577494
578−-- | Split using a condition or a count whichever occurs first. This is a
579−-- hybrid of 'splitOn' and 'take'. The element on which the condition succeeds
580−-- is dropped.
495+-- | See 'Streamly.Internal.Data.Parser.sliceSepByMax'.
581496--
582497-- /Internal/
583498--
@@ -601,17 +516,7 @@ sliceSepByMax predicate cnt (Fold fstep finitial fextract) =
601516 return $ Stop 0 b
602517 extract (Tuple' _ r) = fextract r
603518
604−-- | Like 'splitOn' but strips leading, trailing, and repeated separators.
605−-- Therefore, @".a..b."@ having '.' as the separator would be parsed as
606−-- @["a","b"]@. In other words, its like parsing words from whitespace
607−-- separated text.
608−--
609−-- * Stops - when it finds a word separator after a non-word element
610−-- * Fails - never.
611−--
612−-- @
613−-- S.wordsBy pred f = S.splitParse (PR.wordBy pred f)
614−-- @
519+-- | See 'Streamly.Internal.Data.Parser.wordBy'.
615520--
616521-- /Unimplemented/
617522--
@@ -621,19 +526,7 @@ wordBy ::
621526 (a -> Bool) -> Fold m a b -> Parser m a b
622527wordBy = undefined
623528
624−-- | @groupBy cmp f $ S.fromList [a,b,c,...]@ assigns the element @a@ to the
625−-- first group, then if @a \`cmp` b@ is 'True' @b@ is also assigned to the same
626−-- group. If @a \`cmp` c@ is 'True' then @c@ is also assigned to the same
627−-- group and so on. When the comparison fails a new group is started. Each
628−-- group is folded using the 'Fold' @f@ and the result of the fold is emitted
629−-- in the output stream.
630−--
631−-- * Stops - when the comparison fails.
632−-- * Fails - never.
633−--
634−-- @
635−-- S.groupsBy cmp f = S.splitParse (PR.groupBy cmp f)
636−-- @
529+-- | See 'Streamly.Internal.Data.Parser.groupBy'.
637530--
638531-- /Unimplemented/
639532--
@@ -646,7 +539,7 @@ groupBy = undefined
646539-- XXX use an Unfold instead of a list?
647540-- XXX custom combinators for matching list, array and stream?
648541--
649−-- | Match the given sequence of elements using the given comparison function.
542+-- | See 'Streamly.Internal.Data.Parser.eqBy'.
650543--
651544-- /Internal/
652545--
@@ -662,8 +555,7 @@ eqBy cmp str = Parser step initial extract
662555 step [x] a = return $
663556 if x `cmp` a
664557 then Stop 0 ()
665− else Error $
666− "eqBy: failed, at the last element"
558+ else Error "eqBy: failed, at the last element"
667559 step (x:xs) a = return $
668560 if x `cmp` a
669561 then Skip 0 xs
@@ -677,6 +569,10 @@ eqBy cmp str = Parser step initial extract
677569-- nested parsers
678570-------------------------------------------------------------------------------
679571
572+-- | See 'Streamly.Internal.Data.Parser.lookahead'.
573+--
574+-- /Internal/
575+--
680576{-# INLINE lookAhead #-}
681577lookAhead :: MonadThrow m => Parser m a b -> Parser m a b
682578lookAhead (Parser step1 initial1 _) =
@@ -705,27 +601,7 @@ lookAhead (Parser step1 initial1 _) =
705601-- Interleaving
706602-------------------------------------------------------------------------------
707603--
708−-- To deinterleave we can chain two parsers one behind the other. The input is
709−-- given to the first parser and the input definitively rejected by the first
710−-- parser is given to the second parser.
711−--
712−-- We can either have the parsers themselves buffer the input or use the shared
713−-- global buffer to hold it until none of the parsers need it. When the first
714−-- parser returns Skip (i.e. rewind) we let the second parser consume the
715−-- rejected input and when it is done we move the cursor forward to the first
716−-- parser again. This will require a "move forward" command as well.
717−--
718−-- To implement grep we can use three parsers, one to find the pattern, one
719−-- to store the context behind the pattern and one to store the context in
720−-- front of the pattern. When a match occurs we need to emit the accumulator of
721−-- all the three parsers. One parser can count the line numbers to provide the
722−-- line number info.
723−--
724−-- | Apply two parsers alternately to an input stream. The input stream is
725−-- considered an interleaving of two patterns. The two parsers represent the
726−-- two patterns.
727−--
728−-- This undoes a "gintercalate" of two streams.
604+-- | See 'Streamly.Internal.Data.Parser.deintercalate'.
729605--
730606-- /Unimplemented/
731607--
@@ -741,8 +617,7 @@ deintercalate = undefined
741617-- Sequential Collection
742618-------------------------------------------------------------------------------
743619--
744−-- | @sequence f t@ collects sequential parses of parsers in the container @t@
745−-- using the fold @f@. Fails if the input ends or any of the parsers fail.
620+-- | See 'Streamly.Internal.Data.Parser.sequence'.
746621--
747622-- /Unimplemented/
748623--
@@ -756,8 +631,9 @@ sequence _f _p = undefined
756631-- Alternative Collection
757632-------------------------------------------------------------------------------
758633--
759−-- | @choice parsers@ applies the @parsers@ in order and returns the first
760−-- successful parse.
634+-- | See 'Streamly.Internal.Data.Parser.choice'.
635+--
636+-- /Unimplemented/
761637--
762638{-# INLINE choice #-}
763639choice ::
@@ -769,16 +645,7 @@ choice _ps = undefined
769645-- Sequential Repetition
770646-------------------------------------------------------------------------------
771647--
772−-- XXX "many" is essentially a Fold because it cannot fail. So it can be
773−-- downgraded to a Fold. Or we can make the return type a Fold instead and
774−-- upgrade that to a parser when needed.
775−--
776−-- | Collect zero or more parses. Apply the parser repeatedly on the input
777−-- stream, stop when the parser fails, accumulate zero or more parse results
778−-- using the supplied 'Fold'. This parser never fails, in case the first
779−-- application of parser fails it returns an empty result.
780−--
781−-- Compare with 'Control.Applicative.many'.
648+-- | See 'Streamly.Internal.Data.Parser.many'.
782649--
783650-- /Internal/
784651--
@@ -787,12 +654,7 @@ many :: MonadCatch m => Fold m b c -> Parser m a b -> Parser m a c
787654many = splitMany
788655-- many = countBetween 0 maxBound
789656
790−-- | Collect one or more parses. Apply the supplied parser repeatedly on the
791−-- input stream and accumulate the parse results as long as the parser
792−-- succeeds, stop when it fails. This parser fails if not even one result is
793−-- collected.
794−--
795−-- Compare with 'Control.Applicative.some'.
657+-- | See 'Streamly.Internal.Data.Parser.some'.
796658--
797659-- /Internal/
798660--
@@ -802,9 +664,7 @@ some = splitSome
802664-- some f p = many (takeGE 1 f) p
803665-- many = countBetween 1 maxBound
804666
805−-- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of
806−-- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if
807−-- the input ends or the parser fails before @m@ results are collected.
667+-- | See 'Streamly.Internal.Data.Parser.countBetween'.
808668--
809669-- /Unimplemented/
810670--
@@ -815,9 +675,7 @@ countBetween ::
815675countBetween _m _n _f = undefined
816676-- countBetween m n f p = many (takeBetween m n f) p
817677
818−-- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using
819−-- the fold @f@. Fails if the input ends or the parser fails before @n@
820−-- results are collected.
678+-- | See 'Streamly.Internal.Data.Parser.count'.
821679--
822680-- /Unimplemented/
823681--
@@ -830,11 +688,7 @@ count n = countBetween n n
830688
831689data ManyTillState fs sr sl = ManyTillR Int fs sr | ManyTillL fs sl
832690
833−-- | @manyTill f collect test@ tries the parser @test@ on the input, if @test@
834−-- fails it backtracks and tries @collect@, after @collect@ succeeds @test@ is
835−-- tried again and so on. The parser stops when @test@ succeeds. The output of
836−-- @test@ is discarded and the output of @collect@ is accumulated by the
837−-- supplied fold. The parser fails if @collect@ fails.
691+-- | See 'Streamly.Internal.Data.Parser.manyTill'.
838692--
839693-- /Internal/
840694--
src/Streamly/Internal/Data/Parser/ParserD/Tee.hs+5−13
@@ -80,9 +80,7 @@ data Res = Yld Int | Stp Int | Skp | Err String
8080-- line/column fold running in parallel with the main parser, whenever an error
8181-- occurs we can zip the error with the context fold.
8282--
83−-- | @teeWith f p1 p2@ distributes its input to both @p1@ and @p2@ until both
84−-- of them succeed or fail and combines their output using @f@. The parser
85−-- succeeds if both the parsers succeed.
83+-- | See 'Streamly.Internal.Data.Parser.teeWith'.
8684--
8785-- /Internal/
8886--
@@ -200,8 +198,7 @@ teeWith zf (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
200198 TeePair (_, StepResult rL, _, _) (_, StepResult rR, _, _) ->
201199 return $ zf rL rR
202200
203−-- | Like 'teeWith' but ends parsing and zips the results, if available,
204−-- whenever the first parser ends.
201+-- | See 'Streamly.Internal.Data.Parser.teeWithFst'.
205202--
206203-- /Internal/
207204--
@@ -310,8 +307,7 @@ teeWithFst zf (Parser stepL initialL extractL)
310307 return $ zf rL rR
311308 _ -> error "unreachable"
312309
313−-- | Like 'teeWith' but ends parsing and zips the results, if available,
314−-- whenever any of the parsers ends or fails.
310+-- | See 'Streamly.Internal.Data.Parser.teeWithMin'.
315311--
316312-- /Unimplemented/
317313--
@@ -325,9 +321,7 @@ teeWithMin = undefined
325321-- Distribute input to two parsers and choose one result
326322-------------------------------------------------------------------------------
327323
328−-- | Shortest alternative. Apply both parsers in parallel but choose the result
329−-- from the one which consumed least input i.e. take the shortest succeeding
330−-- parse.
324+-- | See 'Streamly.Internal.Data.Parser.shortest'.
331325--
332326-- /Internal/
333327--
@@ -403,9 +397,7 @@ shortest (Parser stepL initialL extractL) (Parser stepR initialR _) =
403397 TeePair (_, StepState sL, _, _) _ -> extractL sL
404398 _ -> error "unreachable"
405399
406−-- | Longest alternative. Apply both parsers in parallel but choose the result
407−-- from the one which consumed more input i.e. take the longest succeeding
408−-- parse.
400+-- | See 'Streamly.Internal.Data.Parser.longest'.
409401--
410402-- /Internal/
411403--
src/Streamly/Internal/Data/Parser/ParserD/Types.hs+100−112
@@ -1,6 +1,9 @@
1−{-# LANGUAGE ExistentialQuantification #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ExistentialQuantification #-}
23{-# LANGUAGE ScopedTypeVariables #-}
34
5+#include "inline.hs"
6+
47-- |
58-- Module : Streamly.Internal.Data.Parser.ParserD.Types
69-- Copyright : (c) 2020 Composewell Technologies
@@ -116,12 +119,14 @@ module Streamly.Internal.Data.Parser.ParserD.Types
116119 , yield
117120 , yieldM
118121 , splitWith
122+ , split_
119123
120124 , die
121125 , dieM
122126 , splitSome
123127 , splitMany
124128 , alt
129+ , concatMap
125130 )
126131where
127132
@@ -129,6 +134,7 @@ import Control.Applicative (Alternative(..))
129134import Control.Exception (assert, Exception(..))
130135import Control.Monad (MonadPlus(..))
131136import Control.Monad.Catch (MonadCatch, try, throwM, MonadThrow)
137+import Prelude hiding (concatMap)
132138
133139import Fusion.Plugin.Types (Fuse(..))
134140import Streamly.Internal.Data.Fold (Fold(..), toList)
@@ -222,22 +228,17 @@ instance Functor m => Functor (Parser m a) where
222228 step s b = fmap2 f (step1 s b)
223229 fmap2 g = fmap (fmap g)
224230
225−-- This is the dual of stream "yield".
226−--
227−-- | A parser that always yields a pure value without consuming any input.
231+-- | See 'Streamly.Internal.Data.Parser.yield'.
228232--
229233-- /Internal/
230234--
231−{-# INLINE yield #-}
235+{-# INLINE_NORMAL yield #-}
232236yield :: Monad m => b -> Parser m a b
233237yield b = Parser (\_ _ -> pure $ Stop 1 b) -- step
234238 (pure ()) -- initial
235239 (\_ -> pure b) -- extract
236240
237−-- This is the dual of stream "yieldM".
238−--
239−-- | A parser that always yields the result of an effectful action without
240−-- consuming any input.
241+-- | See 'Streamly.Internal.Data.Parser.yieldM'.
241242--
242243-- /Internal/
243244--
@@ -261,19 +262,7 @@ data SeqParseState sl f sr = SeqParseL sl | SeqParseR f sr
261262-- compositions the performance starts dipping rapidly beyond a CPS style
262263-- unfused implementation.
263264--
264−-- | Sequential application. Apply two parsers sequentially to an input stream.
265−-- The input is provided to the first parser, when it is done the remaining
266−-- input is provided to the second parser. If both the parsers succeed their
267−-- outputs are combined using the supplied function. The operation fails if any
268−-- of the parsers fail.
269−--
270−-- This undoes an "append" of two streams, it splits the streams using two
271−-- parsers and zips the results.
272−--
273−-- This implementation is strict in the second argument, therefore, the
274−-- following will fail:
275−--
276−-- >>> S.parse (PR.satisfy (> 0) *> undefined) $ S.fromList [1]
265+-- | See 'Streamly.Internal.Data.Parser.splitWith'.
277266--
278267-- /Internal/
279268--
@@ -315,6 +304,47 @@ splitWith func (Parser stepL initialL extractL)
315304 rR <- extractR sR
316305 return $ func rL rR
317306
307+{-# ANN type SeqAState Fuse #-}
308+data SeqAState sl sr = SeqAL sl | SeqAR sr
309+
310+-- This turns out to be slightly faster than splitWith
311+-- | See 'Streamly.Internal.Data.Parser.split_'.
312+--
313+-- /Internal/
314+--
315+{-# INLINE split_ #-}
316+split_ :: Monad m => Parser m x a -> Parser m x b -> Parser m x b
317+split_ (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
318+ Parser step initial extract
319+
320+ where
321+
322+ initial = SeqAL <$> initialL
323+
324+ -- Note: For the composed parse to terminate, the left parser has to be
325+ -- a terminating parser returning a Stop at some point.
326+ step (SeqAL st) a = do
327+ (\r i -> case r of
328+ -- Note: this leads to buffering even if we are not in an
329+ -- Alternative composition.
330+ Yield _ s -> Skip 0 (SeqAL s)
331+ Skip n s -> Skip n (SeqAL s)
332+ Stop n _ -> Skip n (SeqAR i)
333+ -- XXX should we sequence initialR monadically?
334+ Error err -> Error err) <$> stepL st a <*> initialR
335+
336+ step (SeqAR st) a = do
337+ (\r -> case r of
338+ Yield n s -> Yield n (SeqAR s)
339+ Skip n s -> Skip n (SeqAR s)
340+ Stop n b -> Stop n b
341+ Error err -> Error err) <$> stepR st a
342+
343+ extract (SeqAR sR) = extractR sR
344+ extract (SeqAL sL) = do
345+ _ <- extractL sL
346+ initialR >>= extractR
347+
318348-- | 'Applicative' form of 'splitWith'.
319349instance Monad m => Applicative (Parser m a) where
320350 {-# INLINE pure #-}
@@ -323,6 +353,9 @@ instance Monad m => Applicative (Parser m a) where
323353 {-# INLINE (<*>) #-}
324354 (<*>) = splitWith id
325355
356+ {-# INLINE (*>) #-}
357+ (*>) = split_
358+
326359-------------------------------------------------------------------------------
327360-- Sequential Alternative
328361-------------------------------------------------------------------------------
@@ -335,14 +368,7 @@ data AltParseState sl sr = AltParseL Int sl | AltParseR sr
335368-- each subsequent alternative's input element has to go through, therefore, it
336369-- cannot scale to a large number of compositions
337370--
338−-- | Sequential alternative. Apply the input to the first parser and return the
339−-- result if the parser succeeds. If the first parser fails then backtrack and
340−-- apply the same input to the second parser and return the result.
341−--
342−-- Note: This implementation is not lazy in the second argument. The following
343−-- will fail:
344−--
345−-- >>> S.parse (PR.satisfy (> 0) `PR.alt` undefined) $ S.fromList [1..10]
371+-- | See 'Streamly.Internal.Data.Parser.alt'.
346372--
347373-- /Internal/
348374--
@@ -480,24 +506,18 @@ splitSome (Fold fstep finitial fextract) (Parser step1 initial1 extract1) =
480506 Left (_ :: ParseError) -> fextract fs
481507 Right b -> fstep fs b >>= fextract
482508
483−-- This is the dual of "nil".
484−--
485−-- | A parser that always fails with an error message without consuming
486−-- any input.
509+-- | See 'Streamly.Internal.Data.Parser.die'.
487510--
488511-- /Internal/
489512--
490−{-# INLINE die #-}
513+{-# INLINE_NORMAL die #-}
491514die :: MonadThrow m => String -> Parser m a b
492515die err =
493516 Parser (\_ _ -> pure $ Error err) -- step
494517 (pure ()) -- initial
495518 (\_ -> throwM $ ParseError err) -- extract
496519
497−-- This is the dual of "nilM".
498−--
499−-- | A parser that always fails with an effectful error message and without
500−-- consuming any input.
520+-- | See 'Streamly.Internal.Data.Parser.dieM'.
501521--
502522-- /Internal/
503523--
@@ -539,92 +559,60 @@ instance MonadCatch m => Alternative (Parser m a) where
539559{-# ANN type ConcatParseState Fuse #-}
540560data ConcatParseState sl p = ConcatParseL sl | ConcatParseR p
541561
542−-- Note: The monad instance has quadratic performance complexity. It works fine
543−-- for small number of compositions but for a scalable implementation we need a
544−-- CPS version.
545−
546−-- | Monad composition can be used for lookbehind parsers, we can make the
547−-- future parses depend on the previously parsed values.
562+-- | See 'Streamly.Internal.Data.Parser.concatMap'.
548563--
549−-- If we have to parse "a9" or "9a" but not "99" or "aa" we can use the
550−-- following parser:
551−--
552−-- @
553−-- backtracking :: MonadCatch m => PR.Parser m Char String
554−-- backtracking =
555−-- sequence [PR.satisfy isDigit, PR.satisfy isAlpha]
556−-- '<|>'
557−-- sequence [PR.satisfy isAlpha, PR.satisfy isDigit]
558−-- @
559−--
560−-- We know that if the first parse resulted in a digit at the first place then
561−-- the second parse is going to fail. However, we waste that information and
562−-- parse the first character again in the second parse only to know that it is
563−-- not an alphabetic char. By using lookbehind in a 'Monad' composition we can
564−-- avoid redundant work:
565−--
566−-- @
567−-- data DigitOrAlpha = Digit Char | Alpha Char
568−--
569−-- lookbehind :: MonadCatch m => PR.Parser m Char String
570−-- lookbehind = do
571−-- x1 \<- Digit '<$>' PR.satisfy isDigit
572−-- '<|>' Alpha '<$>' PR.satisfy isAlpha
573−--
574−-- -- Note: the parse depends on what we parsed already
575−-- x2 <- case x1 of
576−-- Digit _ -> PR.satisfy isAlpha
577−-- Alpha _ -> PR.satisfy isDigit
578−--
579−-- return $ case x1 of
580−-- Digit x -> [x,x2]
581−-- Alpha x -> [x,x2]
582−-- @
564+-- /Internal/
583565--
584−instance Monad m => Monad (Parser m a) where
585− {-# INLINE return #-}
586− return = pure
566+{-# INLINE concatMap #-}
567+concatMap :: Monad m => (b -> Parser m a c) -> Parser m a b -> Parser m a c
568+concatMap func (Parser stepL initialL extractL) = Parser step initial extract
587569
588− -- (>>=) :: Parser m a b -> (b -> Parser m a c) -> Parser m a c
589− {-# INLINE (>>=) #-}
590− (Parser stepL initialL extractL) >>= func = Parser step initial extract
570+ where
591571
592− where
572+ initial = ConcatParseL <$> initialL
593573
594− initial = ConcatParseL <$> initialL
574+ step (ConcatParseL st) a = do
575+ r <- stepL st a
576+ return $ case r of
577+ Yield _ s -> Skip 0 (ConcatParseL s)
578+ Skip n s -> Skip n (ConcatParseL s)
579+ Stop n b -> Skip n (ConcatParseR (func b))
580+ Error err -> Error err
595581
596− step (ConcatParseL st) a = do
597− r <- stepL st a
598− return $ case r of
599− Yield _ s -> Skip 0 (ConcatParseL s)
600− Skip n s -> Skip n (ConcatParseL s)
601− Stop n b -> Skip n (ConcatParseR (func b))
602− Error err -> Error err
582+ step (ConcatParseR (Parser stepR initialR extractR)) a = do
583+ st <- initialR
584+ r <- stepR st a
585+ return $ case r of
586+ Yield n s ->
587+ Yield n (ConcatParseR (Parser stepR (return s) extractR))
588+ Skip n s ->
589+ Skip n (ConcatParseR (Parser stepR (return s) extractR))
590+ Stop n b -> Stop n b
591+ Error err -> Error err
603592
604− step (ConcatParseR (Parser stepR initialR extractR)) a = do
605− st <- initialR
606− r <- stepR st a
607− return $ case r of
608− Yield n s ->
609− Yield n (ConcatParseR (Parser stepR (return s) extractR))
610− Skip n s ->
611− Skip n (ConcatParseR (Parser stepR (return s) extractR))
612− Stop n b -> Stop n b
613− Error err -> Error err
593+ extract (ConcatParseR (Parser _ initialR extractR)) =
594+ initialR >>= extractR
614595
615− extract (ConcatParseR (Parser _ initialR extractR)) =
616− initialR >>= extractR
596+ extract (ConcatParseL sL) = extractL sL >>= f . func
617597
618− extract (ConcatParseL sL) = extractL sL >>= f . func
598+ where
619599
620− where
600+ f (Parser _ initialR extractR) = initialR >>= extractR
621601
622− f (Parser _ initialR extractR) = initialR >>= extractR
602+-- Note: The monad instance has quadratic performance complexity. It works fine
603+-- for small number of compositions but for a scalable implementation we need a
604+-- CPS version.
623605
624−-- | 'mzero' is same as 'empty', it aborts the parser. 'mplus' is same as
625−-- '<|>', it selects the first succeeding parser.
606+-- | See documentation of 'Streamly.Internal.Data.Parser.ParserK.Types.Parser'.
626607--
627−-- /Internal/
608+instance Monad m => Monad (Parser m a) where
609+ {-# INLINE return #-}
610+ return = pure
611+
612+ {-# INLINE (>>=) #-}
613+ (>>=) = flip concatMap
614+
615+-- | See documentation of 'Streamly.Internal.Data.Parser.ParserK.Types.Parser'.
628616--
629617instance MonadCatch m => MonadPlus (Parser m a) where
630618 {-# INLINE mzero #-}
src/Streamly/Internal/Data/Parser/ParserK/Types.hs+75−5
@@ -14,10 +14,20 @@
1414-- Portability : GHC
1515--
1616-- CPS style implementation of parsers.
17+--
18+-- The CPS representation allows linear performance for Applicative, sequenceA,
19+-- Monad, sequence, and Alternative, choice operations compared to the
20+-- quadratic complexity of the corresponding direct style operations. However,
21+-- direct style operations allow fusion with ~10x better performance than CPS.
22+--
23+-- The direct style representation does not allow for recursive definitions of
24+-- "some" and "many" whereas CPS allows that.
1725
1826module Streamly.Internal.Data.Parser.ParserK.Types
1927 (
2028 Parser (..)
29+ , yield
30+ , die
2131 )
2232where
2333
@@ -37,6 +47,10 @@ instance Functor m => Functor (Parser m a) where
3747 fmap f parser =
3848 MkParser $ \inp yieldk -> runParser parser inp (yieldk . fmap (fmap f))
3949
50+-- | See 'Streamly.Internal.Data.Parser.yield'.
51+--
52+-- /Internal/
53+--
4054{-# INLINE yield #-}
4155yield :: b -> Parser m a b
4256yield b = MkParser (\_ yieldk -> yieldk (Z.nil, Right b))
@@ -45,7 +59,10 @@ yield b = MkParser (\_ yieldk -> yieldk (Z.nil, Right b))
4559-- Sequential applicative
4660-------------------------------------------------------------------------------
4761--
48−-- | 'Applicative' form of 'splitWith'.
62+-- | 'Applicative' form of 'Streamly.Internal.Data.Parser.splitWith'. Note that
63+-- this operation does not fuse, use 'Streamly.Internal.Data.Parser.splitWith'
64+-- when fusion is important.
65+--
4966instance Monad m => Applicative (Parser m a) where
5067 {-# INLINE pure #-}
5168 pure = yield
@@ -53,10 +70,56 @@ instance Monad m => Applicative (Parser m a) where
5370 {-# INLINE (<*>) #-}
5471 (<*>) = ap
5572
73+-- | See 'Streamly.Internal.Data.Parser.die'.
74+--
75+-- /Internal/
76+--
5677{-# INLINE die #-}
5778die :: String -> Parser m a b
5879die err = MkParser (\z yieldk -> yieldk (z, Left err))
5980
81+-- | Monad composition can be used for lookbehind parsers, we can make the
82+-- future parses depend on the previously parsed values.
83+--
84+-- If we have to parse "a9" or "9a" but not "99" or "aa" we can use the
85+-- following parser:
86+--
87+-- @
88+-- backtracking :: MonadCatch m => PR.Parser m Char String
89+-- backtracking =
90+-- sequence [PR.satisfy isDigit, PR.satisfy isAlpha]
91+-- '<|>'
92+-- sequence [PR.satisfy isAlpha, PR.satisfy isDigit]
93+-- @
94+--
95+-- We know that if the first parse resulted in a digit at the first place then
96+-- the second parse is going to fail. However, we waste that information and
97+-- parse the first character again in the second parse only to know that it is
98+-- not an alphabetic char. By using lookbehind in a 'Monad' composition we can
99+-- avoid redundant work:
100+--
101+-- @
102+-- data DigitOrAlpha = Digit Char | Alpha Char
103+--
104+-- lookbehind :: MonadCatch m => PR.Parser m Char String
105+-- lookbehind = do
106+-- x1 \<- Digit '<$>' PR.satisfy isDigit
107+-- '<|>' Alpha '<$>' PR.satisfy isAlpha
108+--
109+-- -- Note: the parse depends on what we parsed already
110+-- x2 <- case x1 of
111+-- Digit _ -> PR.satisfy isAlpha
112+-- Alpha _ -> PR.satisfy isDigit
113+--
114+-- return $ case x1 of
115+-- Digit x -> [x,x2]
116+-- Alpha x -> [x,x2]
117+-- @
118+--
119+-- Seel also 'Streamly.Internal.Data.Parser.concatMap'. This monad instance
120+-- does not fuse, use 'Streamly.Internal.Data.Parser.concatMap' when you need
121+-- fusion.
122+--
60123instance Monad m => Monad (Parser m a) where
61124 {-# INLINE return #-}
62125 return = pure
@@ -68,11 +131,18 @@ instance Monad m => Monad (Parser m a) where
68131 Left err -> runParser (die err) z yieldk
69132 in runParser m inp yield1
70133
71−-- | 'Alternative' instance using 'alt'.
134+-- | 'Alternative' form of 'Streamly.Internal.Data.Parser.alt'. Backtrack and
135+-- run the second parser if the first one fails.
136+--
137+-- The "some" and "many" operations of alternative accumulate results in a pure
138+-- list which is not scalable and streaming. Instead use
139+-- 'Streamly.Internal.Data.Parser.some' and
140+-- 'Streamly.Internal.Data.Parser.many' for fusible operations with composable
141+-- accumulation of results.
72142--
73−-- The "some" and "many" operations of alternative collect results in a pure
74−-- list which is not scalable and streaming. Instead use the "splitParse"
75−-- operation to apply a 'Parser' repeatedly on a stream in a scalable manner.
143+-- See also 'Streamly.Internal.Data.Parser.alt'. This 'Alternative' instance
144+-- does not fuse, use 'Streamly.Internal.Data.Parser.alt' when you need
145+-- fusion.
76146--
77147instance Monad m => Alternative (Parser m a) where
78148 {-# INLINE empty #-}
src/Streamly/Internal/Data/Stream/Prelude.hs+1−1
@@ -74,7 +74,7 @@ import Prelude hiding (foldr, minimum, maximum)
7474import qualified Prelude
7575
7676import Streamly.Internal.Data.Fold.Types (Fold (..))
77−import Streamly.Internal.Data.Parser.ParserD.Types (Step)
77+import Streamly.Internal.Data.Parser (Step)
7878
7979#ifdef USE_STREAMK_ONLY
8080import qualified Streamly.Internal.Data.Stream.StreamK as S
src/Streamly/Internal/Data/Stream/StreamD.hs+5−4
@@ -362,7 +362,7 @@ import Streamly.Internal.Data.Time.Units
362362import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
363363import Streamly.Internal.Memory.Array.Types (Array(..))
364364import Streamly.Internal.Data.Fold.Types (Fold(..))
365−import Streamly.Internal.Data.Parser.ParserD.Types (Parser(..), ParseError(..))
365+import Streamly.Internal.Data.Parser (ParseError(..))
366366import Streamly.Internal.Data.Pipe.Types (Pipe(..), PipeState(..))
367367import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
368368import Streamly.Internal.Data.Time.Units
@@ -379,7 +379,8 @@ import qualified Streamly.Internal.Memory.Array.Types as A
379379import qualified Streamly.Internal.Data.Fold as FL
380380import qualified Streamly.Memory.Ring as RB
381381import qualified Streamly.Internal.Data.Stream.StreamK as K
382−import qualified Streamly.Internal.Data.Parser.ParserD.Types as PR
382+import qualified Streamly.Internal.Data.Parser as PR
383+import qualified Streamly.Internal.Data.Parser.ParserD as PRD
383384
384385------------------------------------------------------------------------------
385386-- Construction
@@ -1039,10 +1040,10 @@ data ParseChunksState x inpBuf st pst =
10391040{-# INLINE_NORMAL splitParse #-}
10401041splitParse
10411042 :: MonadThrow m
1042− => Parser m a b
1043+ => PRD.Parser m a b
10431044 -> Stream m a
10441045 -> Stream m b
1045−splitParse (Parser pstep initial extract) (Stream step state) =
1046+splitParse (PRD.Parser pstep initial extract) (Stream step state) =
10461047 Stream stepOuter (ParseChunksInit [] state)
10471048
10481049 where
src/Streamly/Internal/Prelude.hs+19−7
@@ -94,6 +94,7 @@ module Streamly.Internal.Prelude
9494 , fold
9595 , parse
9696 , parseK
97+ , parseD
9798
9899 -- ** Concurrent Folds
99100 , foldAsync
@@ -540,7 +541,7 @@ import qualified System.IO as IO
540541
541542import Streamly.Internal.Data.Stream.Enumeration (Enumerable(..), enumerate, enumerateTo)
542543import Streamly.Internal.Data.Fold.Types (Fold (..), Fold2 (..))
543−import Streamly.Internal.Data.Parser.ParserD.Types (Parser (..))
544+import Streamly.Internal.Data.Parser (Parser (..))
544545import Streamly.Internal.Data.Unfold.Types (Unfold)
545546import Streamly.Internal.Memory.Array.Types (Array, writeNUnsafe)
546547-- import Streamly.Memory.Ring (Ring)
@@ -578,6 +579,7 @@ import qualified Streamly.Internal.Data.Stream.Serial as Serial
578579import qualified Streamly.Internal.Data.Stream.Parallel as Par
579580import qualified Streamly.Internal.Data.Stream.Zip as Z
580581import qualified Streamly.Internal.Data.Parser.ParserK.Types as PRK
582+import qualified Streamly.Internal.Data.Parser.ParserD as PRD
581583import qualified Streamly.Internal.Data.Zipper as ZR
582584
583585------------------------------------------------------------------------------
@@ -1197,16 +1199,16 @@ runSink = fold . toFold
11971199-}
11981200
11991201------------------------------------------------------------------------------
1200−-- Running a Parse
1202+-- Running a Parser
12011203------------------------------------------------------------------------------
12021204
1203−-- | Parse a stream using the supplied 'Parse'.
1205+-- | Parse a stream using the supplied 'Parser'.
12041206--
12051207-- /Internal/
12061208--
1207−{-# INLINE parse #-}
1208−parse :: MonadThrow m => Parser m a b -> SerialT m a -> m b
1209−parse (Parser step initial extract) = P.parselMx' step initial extract
1209+{-# INLINE_NORMAL parseD #-}
1210+parseD :: MonadThrow m => PRD.Parser m a b -> SerialT m a -> m b
1211+parseD (PRD.Parser step initial extract) = P.parselMx' step initial extract
12101212
12111213{-# INLINE parseK #-}
12121214parseK :: Monad m => PRK.Parser m a b -> SerialT m a -> m b
@@ -1217,6 +1219,16 @@ parseK parser xs = do
12171219 Left err -> error err
12181220 Right b -> return b
12191221
1222+-- | Parse a stream using the supplied 'Parser'.
1223+--
1224+-- /Internal/
1225+--
1226+{-# INLINE [3] parse #-}
1227+parse :: MonadThrow m => Parser m a b -> SerialT m a -> m b
1228+parse = parseD . PRD.fromParserK
1229+{-# RULES "parse fallback to CPS" [2]
1230+ forall a. parseD (PRD.fromParserK a) = parseK a #-}
1231+
12201232------------------------------------------------------------------------------
12211233-- Specialized folds
12221234------------------------------------------------------------------------------
@@ -3141,7 +3153,7 @@ concatMapTreeYieldLeavesWith combine f = concatMapLoopWith combine f yield
31413153{-# INLINE splitParse #-}
31423154splitParse
31433155 :: (IsStream t, MonadThrow m)
3144− => Parser m a b
3156+ => PRD.Parser m a b
31453157 -> t m a
31463158 -> t m b
31473159splitParse f m = D.fromStreamD $ D.splitParse f (D.toStreamD m)
streamly.cabal+1
@@ -341,6 +341,7 @@ library
341341 , Streamly.Internal.Data.Parser.ParserD.Types
342342 , Streamly.Internal.Data.Parser.ParserD.Tee
343343 , Streamly.Internal.Data.Parser.ParserD
344+ , Streamly.Internal.Data.Parser
344345
345346 -- Base streams
346347 , Streamly.Internal.Data.Stream.StreamK.Type