on master help sign in
Improve performance of the chunked parser

Pass around the original unchanged array instead of reconstructing the array every time the bounds change. Remove the specialized handling of the Alternative case.

commit4f862664f97e452aab6d52ac8786d0fb686b52b6 authorHarendra Kumar <harendra@composewell.com> dateWed 9 Nov 2022, 06:02 (4 years ago) parentfe28607
3 files changed+235−223
core/src/Streamly/Internal/Data/Parser/Chunked.hs+160−165 core/src/Streamly/Internal/Data/Parser/Chunked/Type.hs+73−56 test/Streamly/Test/Data/Parser/Chunked.hs+2−2
core/src/Streamly/Internal/Data/Parser/Chunked.hs+160−165
@@ -10,7 +10,7 @@
1010
1111module Streamly.Internal.Data.Parser.Chunked
1212 (
13− ParserChunked (..)
13+ ParserChunked (..) -- XXX rename to ChunkParser
1414 , fromParserD
1515 , parseBreak
1616 , K.fromPure
@@ -76,11 +76,10 @@ backTrack = go
7676
7777-- | A continuation to extract the result when a CPS parser is done.
7878{-# INLINE parserDone #-}
79−parserDone :: Monad m => (Int, Int) -> K.Parse b -> m (K.Step m a b)
80−parserDone (0,_) (K.Success n b) = return $ K.Done n b
81−parserDone st (K.Success _ _) =
82− error $ "Bug: fromParserK: inside alternative: " ++ show st
83−parserDone _ (K.Failure e) = return $ K.Error e
79+parserDone :: Monad m =>
80+ K.ParseResult b -> Int -> Maybe (Array a) -> m (K.Step a m b)
81+parserDone (K.Success n b) _ _ = return $ K.Done n b
82+parserDone (K.Failure n e) _ _ = return $ K.Error n e
8483
8584-- | Run a 'Parser' over a stream and return rest of the Stream.
8685{-# INLINE_NORMAL parseBreak #-}
@@ -90,47 +89,11 @@ parseBreak
9089 -> Stream m (Array a)
9190 -> m (b, Stream m (Array a))
9291parseBreak parser input = do
93− pRes <- K.runParser parser 0 (0,0) parserDone
94− case pRes of
95− K.Done n b -> assert (n == 0) (return (b, input))
96− K.Error e -> throwM (D.ParseError e)
97− K.Partial n parserk ->
98− assert (n == 0) (go [] parserk (Stream.toStreamK input))
99− K.Continue n parserk ->
100− assert (n == 0) (go [] parserk (Stream.toStreamK input))
92+ let parserk = \arr -> K.runParser parser 0 0 arr parserDone
93+ in go [] parserk (Stream.toStreamK input)
10194
10295 where
10396
104− -- This is a simplified yieldk
105− extractYieldK backBuf parserk arr stream = do
106− pRes <- parserk (Just arr)
107− case pRes of
108− K.Partial 0 cont1 ->
109− goExtract [] cont1 stream
110− K.Partial n cont1 -> do
111− assertM(n <= sum (map Array.length (arr:backBuf)))
112− goExtract [] cont1 (fst $ backTrack n (arr:backBuf) stream)
113− K.Continue 0 cont1 ->
114− go (arr:backBuf) cont1 stream
115− K.Continue n cont1 -> do
116− assertM(n <= sum (map Array.length (arr:backBuf)))
117− let (s1, backBuf1) = backTrack n (arr:backBuf) stream
118− in go backBuf1 cont1 s1
119− K.Done 0 b ->
120− return (b, Stream.fromStreamK stream)
121− K.Done n b -> do
122− assertM(n <= sum (map Array.length (arr:backBuf)))
123− let (s1, _) = backTrack n (arr:backBuf) stream
124− in return (b, Stream.fromStreamK s1)
125− K.Error err -> throwM $ D.ParseError err
126−
127− goExtract backBuf parserk stream = do
128− let stop = goStop backBuf parserk
129− single a = extractYieldK backBuf parserk a StreamK.nil
130− in StreamK.foldStream
131− defState (extractYieldK backBuf parserk) single stop stream
132−
133− -- This is a simplified goExtract
13497 {-# INLINE goStop #-}
13598 goStop backBuf parserk = do
13699 pRes <- parserk Nothing
@@ -138,49 +101,71 @@ parseBreak parser input = do
138101 -- If we stop in an alternative, it will try calling the next
139102 -- parser, the next parser may call initial returning Partial and
140103 -- then immediately we have to call extract on it.
141− K.Partial 0 cont1 -> do
142− goExtract [] cont1 StreamK.nil
104+ K.Partial 0 cont1 ->
105+ go [] cont1 StreamK.nil
143106 K.Partial n cont1 -> do
144− -- error $ "Bug: parseBreak: Partial in extract, n = " ++ show n
145− assertM(n <= sum (map Array.length backBuf))
146− let (s1, backBuf1) = backTrack n backBuf StreamK.nil
147− in goExtract backBuf1 cont1 s1
148− K.Continue 0 cont1 -> do
149− -- error "parseBreak: extract, Continue 0 creates infinite loop"
107+ let n1 = negate n
108+ assertM(n1 >= 0 && n1 <= sum (map Array.length backBuf))
109+ let (s1, backBuf1) = backTrack n1 backBuf StreamK.nil
110+ in go backBuf1 cont1 s1
111+ K.Continue 0 cont1 ->
150112 go backBuf cont1 StreamK.nil
151113 K.Continue n cont1 -> do
152− assertM(n <= sum (map Array.length backBuf))
153− let (s1, backBuf1) = backTrack n backBuf StreamK.nil
154− in goExtract backBuf1 cont1 s1
155− K.Done 0 b -> return (b, Stream.nil)
114+ let n1 = negate n
115+ assertM(n1 >= 0 && n1 <= sum (map Array.length backBuf))
116+ let (s1, backBuf1) = backTrack n1 backBuf StreamK.nil
117+ in go backBuf1 cont1 s1
118+ K.Done 0 b ->
119+ return (b, Stream.nil)
156120 K.Done n b -> do
157− assertM(n <= sum (map Array.length backBuf))
158− let (s1, _) = backTrack n backBuf StreamK.nil
121+ let n1 = negate n
122+ assertM(n1 >= 0 && n1 <= sum (map Array.length backBuf))
123+ let (s1, _) = backTrack n1 backBuf StreamK.nil
159124 in return (b, Stream.fromStreamK s1)
160− K.Error err -> throwM $ D.ParseError err
125+ K.Error _ err -> throwM $ D.ParseError err
161126
162− -- SPECIALIZE this on backBuf?
163− yieldk backBuf !parserk arr stream = do
127+ seekErr n len =
128+ error $ "parseBreak: Partial: forward seek not implemented n = "
129+ ++ show n ++ " len = " ++ show len
130+
131+ yieldk backBuf parserk arr stream = do
164132 pRes <- parserk (Just arr)
133+ let len = Array.length arr
165134 case pRes of
166− K.Partial 0 cont1 ->
167− go [] cont1 stream
168− K.Partial n cont1 -> do
169− assertM(n <= sum (map Array.length (arr:backBuf)))
170− go [] cont1 (fst $ backTrack n (arr:backBuf) stream)
171− K.Continue 0 cont1 ->
172− go (arr:backBuf) cont1 stream
173− K.Continue n cont1 -> do
174− assertM(n <= sum (map Array.length (arr:backBuf)))
175− let (s1, backBuf1) = backTrack n (arr:backBuf) stream
176− in go backBuf1 cont1 s1
177− K.Done 0 b ->
178− return (b, Stream.fromStreamK stream)
135+ K.Partial n cont1 ->
136+ case compare n len of
137+ EQ -> go [] cont1 stream
138+ LT -> do
139+ if n >= 0
140+ then yieldk [] cont1 arr stream
141+ else do
142+ let n1 = negate n
143+ bufLen = sum (map Array.length backBuf)
144+ s = StreamK.cons arr stream
145+ assertM(n1 >= 0 && n1 <= bufLen)
146+ let (s1, _) = backTrack n1 backBuf s
147+ go [] cont1 s1
148+ GT -> seekErr n len
149+ K.Continue n cont1 ->
150+ case compare n len of
151+ EQ -> go (arr:backBuf) cont1 stream
152+ LT -> do
153+ if n >= 0
154+ then yieldk backBuf cont1 arr stream
155+ else do
156+ let n1 = negate n
157+ bufLen = sum (map Array.length backBuf)
158+ s = StreamK.cons arr stream
159+ assertM(n1 >= 0 && n1 <= bufLen)
160+ let (s1, backBuf1) = backTrack n1 backBuf s
161+ go backBuf1 cont1 s1
162+ GT -> seekErr n len
179163 K.Done n b -> do
180− assertM(n <= sum (map Array.length (arr:backBuf)))
181− let (s1, _) = backTrack n (arr:backBuf) stream
164+ let n1 = len - n
165+ assertM(n1 <= sum (map Array.length (arr:backBuf)))
166+ let (s1, _) = backTrack n1 (arr:backBuf) stream
182167 in return (b, Stream.fromStreamK s1)
183− K.Error err -> throwM $ D.ParseError err
168+ K.Error _ err -> throwM $ D.ParseError err
184169
185170 go backBuf parserk stream = do
186171 let stop = goStop backBuf parserk
@@ -188,6 +173,12 @@ parseBreak parser input = do
188173 in StreamK.foldStream
189174 defState (yieldk backBuf parserk) single stop stream
190175
176+data ChunkResult s b =
177+ ChunkDone !Int !b
178+ | ChunkPartial !Int !s
179+ | ChunkContinue !Int !s
180+ | ChunkError !Int String
181+
191182-- This is very similar to fromParserD in the Array/Unboxed/Fold module.
192183{-# INLINE parseChunk #-}
193184parseChunk
@@ -195,122 +186,126 @@ parseChunk
195186 => (s -> a -> m (Step s b))
196187 -> s
197188 -> Array a
198− -> m (Step s b)
199−parseChunk pstep state (Array contents start end) = do
200− go SPEC start state
189+ -> Int
190+ -> m (ChunkResult s b)
191+parseChunk pstep state (Array contents start end) offset = do
192+ if offset >= 0
193+ then go SPEC (start + offset * SIZE_OF(a)) state
194+ else return $ ChunkContinue offset state
201195
202196 where
203197
204− {-# INLINE partial #-}
205− partial arrRem cur next elemSize st n fs1 = do
206− let next1 = next - (n * elemSize)
207− if next1 >= start && cur < end
208− then go SPEC next1 fs1
209− else return $ st (arrRem + n) fs1
198+ {-# INLINE onBack #-}
199+ onBack offset1 elemSize constr pst = do
200+ let pos = offset1 - start
201+ in if pos >= 0
202+ then go SPEC offset1 pst
203+ else return $ constr (pos `div` elemSize) pst
210204
211− go !_ !cur !pst | cur >= end = return $ Continue 0 pst
205+ -- Note: div may be expensive but the alternative is to maintain an element
206+ -- offset in addition to a byte offset or just the element offset and use
207+ -- multiplication to get the byte offset every time, both these options
208+ -- turned out to be more expensive than using div.
209+ go !_ !cur !pst | cur >= end =
210+ return $ ChunkContinue ((end - start) `div` SIZE_OF(a)) pst
212211 go !_ !cur !pst = do
213212 x <- liftIO $ peekWith contents cur
214213 pRes <- pstep pst x
215214 let elemSize = SIZE_OF(a)
216215 next = INDEX_NEXT(cur,a)
217− arrRem = (end - next) `div` elemSize
216+ back n = next - n * elemSize
217+ curOff = (cur - start) `div` elemSize
218+ nextOff = (next - start) `div` elemSize
218219 case pRes of
219− Done n b -> do
220− return $ Done (arrRem + n) b
220+ Done 0 b ->
221+ return $ ChunkDone nextOff b
222+ Done 1 b ->
223+ return $ ChunkDone curOff b
224+ Done n b ->
225+ return $ ChunkDone ((back n - start) `div` elemSize) b
226+ Partial 0 pst1 ->
227+ go SPEC next pst1
228+ Partial 1 pst1 ->
229+ go SPEC cur pst1
221230 Partial n pst1 ->
222− partial arrRem cur next elemSize Partial n pst1
223− Continue n pst1 -> do
224− partial arrRem cur next elemSize Continue n pst1
225− Error err -> return $ Error err
231+ onBack (back n) elemSize ChunkPartial pst1
232+ Continue 0 pst1 ->
233+ go SPEC next pst1
234+ Continue 1 pst1 ->
235+ go SPEC cur pst1
236+ Continue n pst1 ->
237+ onBack (back n) elemSize ChunkContinue pst1
238+ Error err ->
239+ return $ ChunkError curOff err
226240
227−{-# INLINE_NORMAL parseDToK #-}
241+{-# INLINE parseDToK #-}
228242parseDToK
229− :: (MonadIO m, Unbox a)
243+ :: forall m a s b r. (MonadIO m, Unbox a)
230244 => (s -> a -> m (Step s b))
231245 -> m (Initial s b)
232246 -> (s -> m (Step s b))
233247 -> Int
234− -> (Int, Int)
235− -> ((Int, Int) -> K.Parse b -> m (K.Step m a r))
236− -> m (K.Step m a r)
237−-- Non 'Alternative' case.
238−parseDToK pstep initial extract leftover (0, _) cont = do
248+ -> Int
249+ -> Maybe (Array a)
250+ -> (K.ParseResult b -> Int -> Maybe (Array a) -> m (K.Step a m r))
251+ -> m (K.Step a m r)
252+parseDToK pstep initial extract offset usedCount input cont = do
239253 res <- initial
240254 case res of
241− IPartial r -> return $ K.Partial leftover (parseCont (return r))
242− IDone b -> cont state (K.Success 0 b)
243− IError err -> cont state (K.Failure err)
255+ IPartial pst -> do
256+ case input of
257+ Just arr -> parseContJust usedCount offset pst arr
258+ Nothing -> parseContNothing usedCount pst
259+ IDone b -> cont (K.Success offset b) usedCount input
260+ IError err -> cont (K.Failure offset err) usedCount input
244261
245262 where
246263
247− -- The continuation is called with (0,0) state i.e. Alternative level
248− -- is 0 and the used count is 0. Alternative level is 0 because the level
249− -- passed in the argument above is 0, the "used" count is 0 because it is
250− -- not useful when Alternative level is 0. We should probably use a Maybe
251− -- type for the state but that might impact performance, need to measure.
252− state = (0,0)
253−
254− -- We could pass a stream or array here and drive the ParserD with fusion.
255− parseCont pst (Just arr) = do
256− r <- pst
257− pRes <- parseChunk pstep r arr
264+ -- XXX We can maintain an absolute position instead of relative that will
265+ -- help in reporting of error location in the stream.
266+ {-# NOINLINE parseContJust #-}
267+ parseContJust count off pst arr = do
268+ pRes <- parseChunk pstep pst arr off
269+ -- The "n" here is stream position index wrt the array start, and not
270+ -- the backtrack count as returned by byte stream parsers.
258271 case pRes of
259− Done n b -> cont state (K.Success n b)
260− Error err -> cont state (K.Failure err)
261− Partial n pst1 -> return $ K.Partial n (parseCont (return pst1))
262− Continue n pst1 -> return $ K.Continue n (parseCont (return pst1))
272+ ChunkDone n b ->
273+ assert (n <= Array.length arr)
274+ (cont (K.Success n b) (count + n - off) (Just arr))
275+ ChunkPartial n pst1 ->
276+ assert (n < 0 || n >= Array.length arr)
277+ (return $ K.Partial n (parseCont (count + n - off) pst1))
278+ ChunkContinue n pst1 ->
279+ assert (n < 0 || n >= Array.length arr)
280+ (return $ K.Continue n (parseCont (count + n - off) pst1))
281+ ChunkError n err ->
282+ cont (K.Failure n err) (count + n - off) (Just arr)
263283
264− parseCont acc Nothing = do
265− pst <- acc
284+ {-# NOINLINE parseContNothing #-}
285+ parseContNothing count pst = do
266286 r <- extract pst
267287 case r of
268− Done n b -> cont state (K.Success n b)
269− Error err -> cont state (K.Failure err)
270− Partial _ _ -> error "Bug: parseDToK Partial unreachable"
271− Continue n pst1 -> return $ K.Continue n (parseCont (return pst1))
272−
273−-- 'Alternative' case. Used count needs to be maintained when inside an
274−-- Alternative.
275−parseDToK pstep initial extract leftover (level, count) cont = do
276− res <- initial
277− case res of
278− IPartial r -> return $ K.Partial leftover (parseCont count (return r))
279− IDone b -> cont (level,count) (K.Success 0 b)
280− IError err -> cont (level,count) (K.Failure err)
281−
282− where
283−
284− parseCont !cnt pst (Just arr) = do
285− let !cnt1 = cnt + Array.length arr
286− r <- pst
287− pRes <- parseChunk pstep r arr
288− case pRes of
289− Done n b -> do
290− assertM(n <= cnt1)
291− cont (level, cnt1 - n) (K.Success n b)
288+ -- IMPORTANT: the n here is from the byte stream parser, that means
289+ -- it is the backtrack element count and not the stream position
290+ -- index into the current input array.
291+ Done n b ->
292+ assert (n >= 0)
293+ (cont (K.Success (- n) b) (count - n) Nothing)
294+ Continue n pst1 ->
295+ assert (n >= 0)
296+ (return $ K.Continue (- n) (parseCont (count - n) pst1))
292297 Error err ->
293− cont (level, cnt1) (K.Failure err)
294− Partial n pst1 -> do
295− assertM(n <= cnt1)
296− return $ K.Partial n (parseCont (cnt1 - n) (return pst1))
297− Continue n pst1 -> do
298− assertM(n <= cnt1)
299− return $ K.Continue n (parseCont (cnt1 - n) (return pst1))
300− parseCont cnt acc Nothing = do
301− pst <- acc
302− r <- extract pst
303− let s = (level, cnt)
304− case r of
305− Done n b -> do
306− assertM(n <= cnt)
307− cont s (K.Success n b)
298+ -- XXX It is called only when there is no input arr. So using 0
299+ -- as the position is correct?
300+ cont (K.Failure 0 err) count Nothing
308301 Partial _ _ -> error "Bug: parseDToK Partial unreachable"
309− Error e ->
310− cont s (K.Failure e)
311− Continue n pst1 -> do
312− assertM(n <= cnt)
313− return $ K.Continue n (parseCont (cnt - n) (return pst1))
302+
303+ -- XXX Maybe we can use two separate continuations instead of using
304+ -- Just/Nothing cases here. That may help in avoiding the parseContJust
305+ -- function call.
306+ {-# INLINE parseCont #-}
307+ parseCont cnt pst (Just arr) = parseContJust cnt 0 pst arr
308+ parseCont cnt pst Nothing = parseContNothing cnt pst
314309
315310-- | Convert a raw byte 'Parser' to a chunked parser.
316311--
core/src/Streamly/Internal/Data/Parser/Chunked/Type.hs+73−56
@@ -25,7 +25,7 @@
2525module Streamly.Internal.Data.Parser.Chunked.Type
2626 (
2727 Step (..)
28− , Parse (..)
28+ , ParseResult (..)
2929 , ParserChunked (..)
3030 , fromPure
3131 , fromEffect
@@ -52,34 +52,47 @@ import qualified Control.Monad.Fail as Fail
5252--
5353-- /Pre-release/
5454--
55−data Step m a r =
55+data Step a m r =
56+ -- The Int is the current stream position index wrt to the start of the
57+ -- array.
5658 Done !Int r
5759 -- XXX we can use a "resume" and a "stop" continuations instead of Maybe.
5860 -- measure if that works any better.
61+ -- Array a -> m (Step a m r), m (Step a m r)
5962 -- XXX The Array is the only difference from element parser, we can pass
6063 -- this as parameter?
61− | Partial !Int (Maybe (Array a) -> m (Step m a r))
62− | Continue !Int (Maybe (Array a) -> m (Step m a r))
63− | Error String
64+ | Partial !Int (Maybe (Array a) -> m (Step a m r))
65+ | Continue !Int (Maybe (Array a) -> m (Step a m r))
66+ | Error !Int String
6467
65−instance Functor m => Functor (Step m a) where
68+instance Functor m => Functor (Step a m) where
6669 fmap f (Done n r) = Done n (f r)
6770 fmap f (Partial n k) = Partial n (fmap (fmap f) . k)
6871 fmap f (Continue n k) = Continue n (fmap (fmap f) . k)
69− fmap _ (Error e) = Error e
72+ fmap _ (Error n e) = Error n e
7073
7174-- | The parser's result.
7275--
76+-- Int is the position index into the current input array. Could be negative.
77+-- Cannot be beyond the input array max bound.
78+--
7379-- /Pre-release/
7480--
75−data Parse b =
76− Success !Int !b -- Leftover count, result
77− | Failure !String -- Error
81+data ParseResult b =
82+ Success !Int !b -- Position index, result
83+ | Failure !Int !String -- Position index, error
7884
7985-- | Map a function over 'Success'.
80−instance Functor Parse where
86+instance Functor ParseResult where
8187 fmap f (Success n b) = Success n (f b)
82− fmap _ (Failure e) = Failure e
88+ fmap _ (Failure n e) = Failure n e
89+
90+-- XXX Change the type to the shape (a -> m r -> m r) -> (m r -> m r) -> m r
91+--
92+-- The parse continuation would be: Array a -> m (Step a m r) -> m (Step a m r)
93+-- The extract continuation would be: m (Step a m r) -> m (Step a m r)
94+--
95+-- Use Step itself in place of ParseResult.
8396
8497-- | A continuation passing style parser representation. A continuation of
8598-- 'Step's, each step passes a state and a parse result to the next 'Step'. The
@@ -90,24 +103,27 @@ instance Functor Parse where
90103--
91104newtype ParserChunked a m b = MkParser
92105 { runParser :: forall r.
93− -- leftover: the number of elements that were not used by the
94− -- previous consumer and should be carried forward.
106+ -- XXX Maintain and pass the original position in the stream. that
107+ -- way we can also report better errors. Use a Context structure for
108+ -- passing the state.
109+
110+ -- Stream position index wrt to the current input array start. If
111+ -- negative then backtracking is required before using the array.
112+ -- The parser should use "Continue -n" in this case if it needs to
113+ -- consume input. Negative value cannot be beyond the current
114+ -- backtrack buffer. Positive value cannot be beyond array length.
115+ -- If the parser needs to advance beyond the array length it should
116+ -- use "Continue +n".
95117 Int
96− -- (alt nesting level, alt used elem count). Nesting level is
97− -- increased whenever we enter an Alternative composition and
98− -- decreased when it is done. The used element count is a count of
99− -- elements consumed by the Alternative. If the Alternative fails we
100− -- need to backtrack by this amount.
101− --
102− -- The nesting level is used in parseDToK to optimize the case when
103− -- we are not in an alternative, in that case we do not need to
104− -- maintain the element count for backtracking.
105− -> (Int, Int)
106− -- The first argument is the (nest level, used count) tuple as
107− -- described above. The leftover element count is carried as part of
108− -- 'Success' constructor of 'Parse'.
109− -> ((Int, Int) -> Parse b -> m (Step m a r))
110− -> m (Step m a r)
118+ -- used elem count, a count of elements consumed by the parser. If
119+ -- an Alternative fails we need to backtrack by this amount.
120+ -> Int
121+ -- The second argument is the used count as described above. The
122+ -- current input position is carried as part of 'Success'
123+ -- constructor of 'ParseResult'.
124+ -> Maybe (Array a)
125+ -> (ParseResult b -> Int -> Maybe (Array a) -> m (Step a m r))
126+ -> m (Step a m r)
111127 }
112128
113129-------------------------------------------------------------------------------
@@ -119,9 +135,9 @@ newtype ParserChunked a m b = MkParser
119135--
120136instance Functor m => Functor (ParserChunked a m) where
121137 {-# INLINE fmap #-}
122− fmap f parser = MkParser $ \n st k ->
123− let k1 s res = k s (fmap f res)
124− in runParser parser n st k1
138+ fmap f parser = MkParser $ \n st arr pk ->
139+ let pk1 res = pk (fmap f res)
140+ in runParser parser n st arr pk1
125141
126142-------------------------------------------------------------------------------
127143-- Sequential applicative
@@ -135,7 +151,7 @@ instance Functor m => Functor (ParserChunked a m) where
135151--
136152{-# INLINE fromPure #-}
137153fromPure :: b -> ParserChunked a m b
138−fromPure b = MkParser $ \n st k -> k st (Success n b)
154+fromPure b = MkParser $ \n st arr pk -> pk (Success n b) st arr
139155
140156-- | See 'Streamly.Internal.Data.Parser.fromEffect'.
141157--
@@ -143,7 +159,8 @@ fromPure b = MkParser $ \n st k -> k st (Success n b)
143159--
144160{-# INLINE fromEffect #-}
145161fromEffect :: Monad m => m b -> ParserChunked a m b
146−fromEffect eff = MkParser $ \n st k -> eff >>= \b -> k st (Success n b)
162+fromEffect eff =
163+ MkParser $ \n st arr pk -> eff >>= \b -> pk (Success n b) st arr
147164
148165-- | 'Applicative' form of 'Streamly.Internal.Data.Parser.serialWith'. Note that
149166-- this operation does not fuse, use 'Streamly.Internal.Data.Parser.serialWith'
@@ -157,19 +174,19 @@ instance Monad m => Applicative (ParserChunked a m) where
157174 (<*>) = ap
158175
159176 {-# INLINE (*>) #-}
160− p1 *> p2 = MkParser $ \n st k ->
161− let k1 s (Success n1 _) = runParser p2 n1 s k
162− k1 s (Failure e) = k s (Failure e)
163− in runParser p1 n st k1
177+ p1 *> p2 = MkParser $ \n st arr k ->
178+ let k1 (Success n1 _) s input = runParser p2 n1 s input k
179+ k1 (Failure n1 e) s input = k (Failure n1 e) s input
180+ in runParser p1 n st arr k1
164181
165182 {-# INLINE (<*) #-}
166− p1 <* p2 = MkParser $ \n st k ->
167− let k1 s1 (Success n1 b) =
168− let k2 s2 (Success n2 _) = k s2 (Success n2 b)
169− k2 s2 (Failure e) = k s2 (Failure e)
170− in runParser p2 n1 s1 k2
171− k1 s1 (Failure e) = k s1 (Failure e)
172− in runParser p1 n st k1
183+ p1 <* p2 = MkParser $ \n st arr k ->
184+ let k1 (Success n1 b) s1 input =
185+ let k2 (Success n2 _) = k (Success n2 b)
186+ k2 (Failure n2 e) = k (Failure n2 e)
187+ in runParser p2 n1 s1 input k2
188+ k1 (Failure n1 e) s1 input = k (Failure n1 e) s1 input
189+ in runParser p1 n st arr k1
173190
174191 {-# INLINE liftA2 #-}
175192 liftA2 f p = (<*>) (fmap f p)
@@ -187,7 +204,7 @@ instance Monad m => Applicative (ParserChunked a m) where
187204--
188205{-# INLINE die #-}
189206die :: String -> ParserChunked a m b
190−die err = MkParser (\_ st k -> k st (Failure err))
207+die err = MkParser (\n st arr pk -> pk (Failure n err) st arr)
191208
192209-- | Monad composition can be used for lookbehind parsers, we can make the
193210-- future parses depend on the previously parsed values.
@@ -236,10 +253,10 @@ instance Monad m => Monad (ParserChunked a m) where
236253 return = pure
237254
238255 {-# INLINE (>>=) #-}
239− p >>= f = MkParser $ \n st k ->
240− let k1 s1 (Success n1 b) = runParser (f b) n1 s1 k
241− k1 s1 (Failure e) = k s1 (Failure e)
242− in runParser p n st k1
256+ p >>= f = MkParser $ \n st arr pk ->
257+ let pk1 (Success n1 b) s1 inp = runParser (f b) n1 s1 inp pk
258+ pk1 (Failure n1 e) s1 inp = pk (Failure n1 e) s1 inp
259+ in runParser p n st arr pk1
243260
244261 {-# INLINE (>>) #-}
245262 (>>) = (*>)
@@ -261,7 +278,7 @@ instance (MonadThrow m, MonadReader r m, MonadCatch m) =>
261278 ask = fromEffect ask
262279
263280 {-# INLINE local #-}
264− local f p = MkParser $ \n st k -> local f $ runParser p n st k
281+ local f p = MkParser $ \n st arr k -> local f $ runParser p n st arr k
265282
266283instance (MonadThrow m, MonadState s m) => MonadState s (ParserChunked a m) where
267284 {-# INLINE get #-}
@@ -296,11 +313,11 @@ instance Monad m => Alternative (ParserChunked a m) where
296313 empty = die "empty"
297314
298315 {-# INLINE (<|>) #-}
299− p1 <|> p2 = MkParser $ \n (level, _) k ->
300− let k1 (0, _) _ = error "Bug: 0 nest level in Alternative"
301− k1 (l1, n1) (Failure _) = runParser p2 n1 (l1 - 1, 0) k
302− k1 (l1, _) success = k (l1 - 1, 0) success
303− in runParser p1 n (level + 1, 0) k1
316+ p1 <|> p2 = MkParser $ \n _ arr k ->
317+ let
318+ k1 (Failure pos _) used input = runParser p2 (pos - used) 0 input k
319+ k1 success _ input = k success 0 input
320+ in runParser p1 n 0 arr k1
304321
305322 -- some and many are implemented here instead of using default definitions
306323 -- so that we can use INLINE on them. It gives 50% performance improvement.
test/Streamly/Test/Data/Parser/Chunked.hs+2−2
@@ -700,7 +700,7 @@ applicative =
700700 in monadicIO $ do
701701 let arrays = [A.fromList list1, A.fromList list2]
702702 (olist1, olist2) <-
703− run $ parse (P.fromRaw parser) (S.fromList arrays)
703+ run $ parse (P.fromParserD parser) (S.fromList arrays)
704704 listEquals (==) olist1 list1
705705 listEquals (==) olist2 list2
706706
@@ -728,7 +728,7 @@ monad =
728728 in monadicIO $ do
729729 let arrays = [A.fromList list1, A.fromList list2]
730730 (olist1, olist2) <-
731− run $ parse (P.fromRaw parser) (S.fromList arrays)
731+ run $ parse (P.fromParserD parser) (S.fromList arrays)
732732 listEquals (==) olist1 list1
733733 listEquals (==) olist2 list2
734734