1{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
6-- Module : Streamly.Internal.Data.Parser.ParserD.Tee
7-- Copyright : (c) 2020 Composewell Technologies
8-- License : BSD-3-Clause
9-- Maintainer : streamly@composewell.com
10-- Stability : experimental
13-- Parallel parsers. Distributing the input to multiple parsers at the same
16-- For simplicity, we are using code where a particular state is unreachable
17-- but it is not prevented by types. Somehow uni-pattern match using "let"
18-- produces better optimized code compared to using @case@ match and using
19-- explicit error messages in unreachable cases.
21-- There seem to be no way to silence individual warnings so we use a global
22-- incomplete uni-pattern match warning suppression option for the file.
23-- Disabling the warning for other code as well has the potential to mask off
24-- some legit warnings, therefore, we have segregated only the code that uses
25-- uni-pattern matches in this module.
27module Streamly.Internal.Data.Parser.ParserD.Tee
35 -- Parallel alternatives
43import Control.Exception (assert)
44import Control.Monad.Catch (MonadCatch, try)
46 hiding (any, all, takeWhile)
48import Fusion.Plugin.Types (Fuse(..))
49import Streamly.Internal.Data.Parser.ParserD.Type
50 (Initial(..), Parser(..), Step(..), ParseError)
52-------------------------------------------------------------------------------
53-- Distribute input to two parsers and collect both results
54-------------------------------------------------------------------------------
56-- When the input stream is distributed to two parsers, both the parsers can
57-- backtrack independently. Therefore, we need separate buffer state for each
62-- We can keep the state of each parser in the zipper and pass around that
63-- zipper to the parsers. Each parser can consume from the zipper and then pass
64-- around the zipper to the other parser.
68-- In the approach we have taken here, the driver pushes one element at a time
69-- to the tee and each of the parsers in the tee may buffer it independently
70-- for backtracking. So they do not need to depend on the original stream
71-- source for individual parser backtracking. Problem arises when both the
72-- parsers backtrack and they do not need any input from the driver rather they
73-- must consume from their buffers. For such situation we may need a
74-- "Continue" style driver command from the tee so that the driver runs
75-- the tee without providing it any input. Or we may need a local driver loop
76-- until new input is to be demanded from the input stream.
78-- When the tee errors out or stops, the tee driver may have to backtrack by
79-- the specified amount (or the tee must return the leftover input). Therefore,
80-- the tee driver also has to buffer, this leads to triple buffering.
82-- When the tee stops we need to determine the backtracking amount from the
83-- leftover of both the parsers. Since both the parsers may have consumed
84-- different lengths of the stream we consider the maximum of the two as
87 -- XXX We can use Initial instead of StepState
88{-# ANN type StepState Fuse #-}
89data StepState s a = StepState s | StepResult a
91-- | State of the pair of parsers in a tee composition
92-- Note: strictness annotation is important for fusing the constructors
93{-# ANN type TeeState Fuse #-}
94data TeeState sL sR x a b =
95-- @TeePair (past buffer, parser state, future-buffer1, future-buffer2) ...@
96 TeePair !([x], StepState sL a, [x], [x]) !([x], StepState sR b, [x], [x])
98{-# ANN type Res Fuse #-}
99data Res = Yld Int | Stp Int | Skp | Err String
101-- | See 'Streamly.Internal.Data.Parser.teeWith'.
105{-# INLINE teeWith #-}
107 => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
108teeWith zf (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
109 Parser step initial extract
113 {-# INLINE_LATE initial #-}
117 return $ case resL of
120 IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])
121 ([], StepState sr, [], [])
122 IDone br -> IPartial $ TeePair ([], StepState sl, [], [])
123 ([], StepResult br, [], [])
124 IError err -> IError err
128 IPartial $ TeePair ([], StepResult bl, [], [])
129 ([], StepState sr, [], [])
130 IDone br -> IDone $ zf bl br
131 IError err -> IError err
132 IError err -> IError err
134 {-# INLINE consume #-}
135 consume buf inp1 inp2 stp st y = do
136 let (x, inp11, inp21) =
139 z : [] -> (z, reverse (x:inp2), [])
140 z : zs -> (z, zs, x:inp2)
143 return (buf1, r, inp11, inp21)
145 -- XXX This is currently broken, even though both the parsers need to
146 -- consume from their buffers after backtracking the driver would still be
147 -- pushing more input to the buffers.
149 -- consume one input item and return the next state of the fold
150 {-# INLINE useStream #-}
151 useStream buf inp1 inp2 stp st y = do
152 (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y
155 let state = ([], StepState s, inp11, inp21)
156 in return (state, Yld 0)
158 let src0 = Prelude.take n buf1
159 src = Prelude.reverse src0
160 state = ([], StepState s, src ++ inp11, inp21)
161 in assert (n <= length buf1) (return (state, Yld n))
163 let state = (Prelude.take n buf1, StepResult b, inp11, inp21)
164 in assert (n <= length buf1) (return (state, Stp n))
165 -- Continue 0 s -> (buf1, Right s, inp11, inp21)
167 let (src0, buf2) = splitAt n buf1
168 src = Prelude.reverse src0
169 state = (buf2, StepState s, src ++ inp11, inp21)
170 in assert (n <= length buf1) (return (state, Skp))
171 Error err -> return (undefined, Err err)
173 {-# INLINE_LATE step #-}
174 step (TeePair (bufL, StepState sL, inpL1, inpL2)
175 (bufR, StepState sR, inpR1, inpR2)) x = do
176 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
177 (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x
178 let next = TeePair l r
179 return $ case (stL,stR) of
180 (Yld n1, Yld n2) -> Partial (min n1 n2) next
181 (Yld n1, Stp n2) -> Partial (min n1 n2) next
182 (Stp n1, Yld n2) -> Partial (min n1 n2) next
184 -- Uni-pattern match results in better optimized code compared
186 let (_, StepResult rL, _, _) = l
187 (_, StepResult rR, _, _) = r
188 in Done (min n1 n2) (zf rL rR)
189 (Err err, _) -> Error err
190 (_, Err err) -> Error err
193 step (TeePair (bufL, StepState sL, inpL1, inpL2)
194 r@(_, StepResult rR, _, _)) x = do
195 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
196 let next = TeePair l r
197 -- XXX If the unused count of this stream is lower than the unused
198 -- count of the stopped stream, only then this will be correct. We need
199 -- to fix the other case. We need to keep incrementing the unused count
200 -- of the stopped stream and take the min of the two.
202 Yld n -> Partial n next
204 let (_, StepResult rL, _, _) = l
206 Skp -> Continue 0 next
209 step (TeePair l@(_, StepResult rL, _, _)
210 (bufR, StepState sR, inpR1, inpR2)) x = do
211 (r, stR) <- useStream bufR inpR1 inpR2 stepR sR x
212 let next = TeePair l r
213 -- XXX If the unused count of this stream is lower than the unused
214 -- count of the stopped stream, only then this will be correct. We need
215 -- to fix the other case. We need to keep incrementing the unused count
216 -- of the stopped stream and take the min of the two.
218 Yld n -> Partial n next
220 let (_, StepResult rR, _, _) = r
222 Skp -> Continue 0 next
227 {-# INLINE_LATE extract #-}
230 TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do
234 TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do
237 TeePair (_, StepResult rL, _, _) (_, StepState sR, _, _) -> do
240 TeePair (_, StepResult rL, _, _) (_, StepResult rR, _, _) ->
243-- | See 'Streamly.Internal.Data.Parser.teeWithFst'.
247{-# INLINE teeWithFst #-}
249 => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
250teeWithFst zf (Parser stepL initialL extractL)
251 (Parser stepR initialR extractR) =
252 Parser step initial extract
256 {-# INLINE_LATE initial #-}
262 return $ case resR of
263 IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])
264 ([], StepState sr, [], [])
265 IDone br -> IPartial $ TeePair ([], StepState sl, [], [])
266 ([], StepResult br, [], [])
267 IError err -> IError err
270 IPartial sr -> IDone . zf bl <$> extractR sr
271 IDone br -> return $ IDone $ zf bl br
272 IError err -> return $ IError err
273 IError err -> return $ IError err
275 {-# INLINE consume #-}
276 consume buf inp1 inp2 stp st y = do
277 let (x, inp11, inp21) =
280 z : [] -> (z, reverse (x:inp2), [])
281 z : zs -> (z, zs, x:inp2)
284 return (buf1, r, inp11, inp21)
286 -- consume one input item and return the next state of the fold
287 {-# INLINE useStream #-}
288 useStream buf inp1 inp2 stp st y = do
289 (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y
292 let state = ([], StepState s, inp11, inp21)
293 in return (state, Yld 0)
294 Partial n _ -> return (undefined, Yld n) -- Not implemented
296 let state = (Prelude.take n buf1, StepResult b, inp11, inp21)
297 in assert (n <= length buf1) (return (state, Stp n))
298 -- Continue 0 s -> (buf1, Right s, inp11, inp21)
300 let (src0, buf2) = splitAt n buf1
301 src = Prelude.reverse src0
302 state = (buf2, StepState s, src ++ inp11, inp21)
303 in assert (n <= length buf1) (return (state, Skp))
304 Error err -> return (undefined, Err err)
306 {-# INLINE_LATE step #-}
307 step (TeePair (bufL, StepState sL, inpL1, inpL2)
308 (bufR, StepState sR, inpR1, inpR2)) x = do
309 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
310 (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x
311 let next = TeePair l r
313 -- XXX what if the first parser returns an unused count which is
314 -- more than the second parser's unused count? It does not make
315 -- sense for the second parser to consume more than the first
316 -- parser. We reset the input cursor based on the first parser.
317 -- Error out if the second one has consumed more then the first?
319 -- Uni-pattern match results in better optimized code compared
321 let (_, StepResult rL, _, _) = l
322 (_, StepResult rR, _, _) = r
323 in return $ Done n1 (zf rL rR)
325 let (_, StepResult rL, _, _) = l
326 (_, StepState ssR, _, _) = r
329 return $ Done n1 (zf rL rR)
330 (Yld n1, Yld n2) -> return $ Partial (min n1 n2) next
331 (Yld n1, Stp n2) -> return $ Partial (min n1 n2) next
332 (Err err, _) -> return $ Error err
333 (_, Err err) -> return $ Error err
334 _ -> return $ Continue 0 next
336 step (TeePair (bufL, StepState sL, inpL1, inpL2)
337 r@(_, StepResult rR, _, _)) x = do
338 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
339 let next = TeePair l r
340 -- XXX If the unused count of this stream is lower than the unused
341 -- count of the stopped stream, only then this will be correct. We need
342 -- to fix the other case. We need to keep incrementing the unused count
343 -- of the stopped stream and take the min of the two.
345 Yld n -> Partial n next
347 let (_, StepResult rL, _, _) = l
349 Skp -> Continue 0 next
354 {-# INLINE_LATE extract #-}
357 TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do
361 TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do
364 _ -> error "unreachable"
366-- | See 'Streamly.Internal.Data.Parser.teeWithMin'.
370{-# INLINE teeWithMin #-}
373 (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
374teeWithMin = undefined
376-------------------------------------------------------------------------------
377-- Distribute input to two parsers and choose one result
378-------------------------------------------------------------------------------
380-- | See 'Streamly.Internal.Data.Parser.shortest'.
384{-# INLINE shortest #-}
385shortest :: Monad m => Parser m x a -> Parser m x a -> Parser m x a
386shortest (Parser stepL initialL extractL) (Parser stepR initialR _) =
387 Parser step initial extract
391 {-# INLINE_LATE initial #-}
395 return $ case resL of
398 IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])
399 ([], StepState sr, [], [])
401 IError err -> IError err
405 IPartial _ -> IError errL
407 IError errR -> IError errR
409 {-# INLINE consume #-}
410 consume buf inp1 inp2 stp st y = do
411 let (x, inp11, inp21) =
414 z : [] -> (z, reverse (x:inp2), [])
415 z : zs -> (z, zs, x:inp2)
418 return (buf1, r, inp11, inp21)
420 -- consume one input item and return the next state of the fold
421 {-# INLINE useStream #-}
422 useStream buf inp1 inp2 stp st y = do
423 (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y
426 let state = ([], StepState s, inp11, inp21)
427 in return (state, Yld 0)
428 Partial n _ -> return (undefined, Yld n) -- Not implemented
430 let state = (Prelude.take n buf1, StepResult b, inp11, inp21)
431 in assert (n <= length buf1) (return (state, Stp n))
432 -- Continue 0 s -> (buf1, Right s, inp11, inp21)
434 let (src0, buf2) = splitAt n buf1
435 src = Prelude.reverse src0
436 state = (buf2, StepState s, src ++ inp11, inp21)
437 in assert (n <= length buf1) (return (state, Skp))
438 Error err -> return (undefined, Err err)
440 -- XXX Even if a parse finished earlier it may not be shortest if the other
441 -- parser finishes later but returns a lot of unconsumed input. Our current
442 -- criterion of shortest is whichever parse decided to stop earlier.
443 {-# INLINE_LATE step #-}
444 step (TeePair (bufL, StepState sL, inpL1, inpL2)
445 (bufR, StepState sR, inpR1, inpR2)) x = do
446 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
447 (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x
448 let next = TeePair l r
449 return $ case (stL,stR) of
451 let (_, StepResult rL, _, _) = l
454 let (_, StepResult rR, _, _) = r
456 (Yld n1, Yld n2) -> Partial (min n1 n2) next
457 (Err err, _) -> Error err
458 (_, Err err) -> Error err
463 {-# INLINE_LATE extract #-}
466 TeePair (_, StepState sL, _, _) _ -> extractL sL
467 _ -> error "unreachable"
469-- | See 'Streamly.Internal.Data.Parser.longest'.
473{-# INLINE longest #-}
474longest :: MonadCatch m => Parser m x a -> Parser m x a -> Parser m x a
475longest (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
476 Parser step initial extract
481 {-# INLINE_LATE initial #-}
485 return $ case resL of
488 IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])
489 ([], StepState sr, [], [])
490 IDone br -> IPartial $ TeePair ([], StepState sl, [], [])
491 ([], StepResult br, [], [])
493 IPartial $ TeePair ([], StepState sl, [], [])
494 ([], StepResult undefined, [], [])
498 IPartial $ TeePair ([], StepResult bl, [], [])
499 ([], StepState sr, [], [])
505 IPartial $ TeePair ([], StepResult undefined, [], [])
506 ([], StepState sr, [], [])
508 IError err -> IError err
510 {-# INLINE consume #-}
511 consume buf inp1 inp2 stp st y = do
512 let (x, inp11, inp21) =
515 z : [] -> (z, reverse (x:inp2), [])
516 z : zs -> (z, zs, x:inp2)
519 return (buf1, r, inp11, inp21)
521 -- consume one input item and return the next state of the fold
522 {-# INLINE useStream #-}
523 useStream buf inp1 inp2 stp st y = do
524 (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y
527 let state = ([], StepState s, inp11, inp21)
528 in return (state, Yld 0)
529 Partial n _ -> return (undefined, Yld n) -- Not implemented
531 let state = (Prelude.take n buf1, StepResult b, inp11, inp21)
532 in assert (n <= length buf1) (return (state, Stp n))
533 -- Continue 0 s -> (buf1, Right s, inp11, inp21)
535 let (src0, buf2) = splitAt n buf1
536 src = Prelude.reverse src0
537 state = (buf2, StepState s, src ++ inp11, inp21)
538 in assert (n <= length buf1) (return (state, Skp))
539 Error err -> return (undefined, Err err)
541 {-# INLINE_LATE step #-}
542 step (TeePair (bufL, StepState sL, inpL1, inpL2)
543 (bufR, StepState sR, inpR1, inpR2)) x = do
544 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
545 (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x
546 let next = TeePair l r
547 return $ case (stL,stR) of
548 (Yld n1, Yld n2) -> Partial (min n1 n2) next
549 (Yld n1, Stp n2) -> Partial (min n1 n2) next
550 (Stp n1, Yld n2) -> Partial (min n1 n2) next
552 let (_, StepResult rL, _, _) = l
553 (_, StepResult rR, _, _) = r
554 in Done (max n1 n2) (if n1 >= n2 then rL else rR)
555 (Err err, _) -> Error err
556 (_, Err err) -> Error err
559 -- XXX the parser that finishes last may not be the longest because it may
560 -- return a lot of unused input which makes it shorter. Our current
561 -- criterion of deciding longest is based on whoever decides to finish
562 -- last and not whoever consumed more input.
564 -- To actually know who made more progress we need to keep an account of
565 -- how many items are unconsumed since the last yield.
567 step (TeePair (bufL, StepState sL, inpL1, inpL2)
568 r@(_, StepResult _, _, _)) x = do
569 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
570 let next = TeePair l r
572 Yld n -> Partial n next
574 let (_, StepResult rL, _, _) = l
576 Skp -> Continue 0 next
579 step (TeePair l@(_, StepResult _, _, _)
580 (bufR, StepState sR, inpR1, inpR2)) x = do
581 (r, stR) <- useStream bufR inpR1 inpR2 stepR sR x
582 let next = TeePair l r
584 Yld n -> Partial n next
586 let (_, StepResult rR, _, _) = r
588 Skp -> Continue 0 next
593 {-# INLINE_LATE extract #-}
595 -- XXX When results are partial we may not be able to precisely compare
596 -- which parser has made more progress till now. One way to do that is
597 -- to figure out the actually consumed input up to the last yield.
600 TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do
601 r <- try $ extractL sL
603 Left (_ :: ParseError) -> extractR sR
605 TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do
606 r <- try $ extractL sL
608 Left (_ :: ParseError) -> return rR
610 TeePair (_, StepResult rL, _, _) (_, StepState sR, _, _) -> do
611 r <- try $ extractR sR
613 Left (_ :: ParseError) -> return rL
615 TeePair (_, StepResult _, _, _) (_, StepResult _, _, _) ->
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list