2{-# LANGUAGE ScopedTypeVariables #-}
4#if __GLASGOW_HASKELL__ >= 800
5{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
11-- Module : Streamly.Internal.Data.Parser.ParserD.Tee
12-- Copyright : (c) 2020 Composewell Technologies
13-- License : BSD-3-Clause
14-- Maintainer : streamly@composewell.com
15-- Stability : experimental
18-- Parallel parsers. Distributing the input to multiple parsers at the same
21-- For simplicity, we are using code where a particular state is unreachable
22-- but it is not prevented by types. Somehow uni-pattern match using "let"
23-- produces better optimized code compared to using @case@ match and using
24-- explicit error messages in unreachable cases.
26-- There seem to be no way to silence individual warnings so we use a global
27-- incomplete uni-pattern match warning suppression option for the file.
28-- Disabling the warning for other code as well has the potential to mask off
29-- some legit warnings, therefore, we have segregated only the code that uses
30-- uni-pattern matches in this module.
32module Streamly.Internal.Data.Parser.ParserD.Tee
39 -- Parallel alternatives
45import Control.Exception (assert)
46import Control.Monad.Catch (MonadCatch, try)
48 hiding (any, all, takeWhile)
50import Fusion.Plugin.Types (Fuse(..))
51import Streamly.Internal.Data.Parser.ParserD.Types
52 (Parser(..), Step(..), ParseError)
54-------------------------------------------------------------------------------
55-- Distribute input to two parsers and collect both results
56-------------------------------------------------------------------------------
58-- When the input stream is distributed to two parsers, both the parsers can
59-- backtrack independently. Therefore, we need separate buffer state for each
64-- We can keep the state of each parser in the zipper and pass around that
65-- zipper to the parsers. Each parser can consume from the zipper and then pass
66-- around the zipper to the other parser.
70-- In the approach we have taken here, the driver pushes one element at a time
71-- to the tee and each of the parsers in the tee may buffer it independently
72-- for backtracking. So they do not need to depend on the original stream
73-- source for individual parser backtracking. Problem arises when both the
74-- parsers backtrack and they do not need any input from the driver rather they
75-- must consume from their buffers. For such situation we may need a
76-- "Skip/Continue" style driver command from the tee so that the driver runs
77-- the tee without providing it any input. Or we may need a local driver loop
78-- until new input is to be demanded from the input stream.
80-- When the tee errors out or stops, the tee driver may have to backtrack by
81-- the specified amount (or the tee must return the leftover input). Therefore,
82-- the tee driver also has to buffer, this leads to triple buffering.
84-- When the tee stops we need to determine the backtracking amount from the
85-- leftover of both the parsers. Since both the parsers may have consumed
86-- different lengths of the stream we consider the maximum of the two as
89{-# ANN type StepState Fuse #-}
90data StepState s a = StepState s | StepResult a
92-- | State of the pair of parsers in a tee composition
93-- Note: strictness annotation is important for fusing the constructors
94{-# ANN type TeeState Fuse #-}
95data TeeState sL sR x a b =
96-- @TeePair (past buffer, parser state, future-buffer1, future-buffer2) ...@
97 TeePair !([x], StepState sL a, [x], [x]) !([x], StepState sR b, [x], [x])
99{-# ANN type Res Fuse #-}
100data Res = Yld Int | Stp Int | Skp | Err String
102-- | See 'Streamly.Internal.Data.Parser.teeWith'.
106{-# INLINE teeWith #-}
108 => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
109teeWith zf (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
110 Parser step initial extract
114 {-# INLINE_LATE initial #-}
118 return $ TeePair ([], StepState sL, [], []) ([], StepState sR, [], [])
120 {-# INLINE consume #-}
121 consume buf inp1 inp2 stp st y = do
122 let (x, inp11, inp21) =
125 z : [] -> (z, reverse (x:inp2), [])
126 z : zs -> (z, zs, x:inp2)
129 return (buf1, r, inp11, inp21)
131 -- XXX This is currently broken, even though both the parsers need to
132 -- consume from their buffers after backtracking the driver would still be
133 -- pushing more input to the buffers.
135 -- consume one input item and return the next state of the fold
136 {-# INLINE useStream #-}
137 useStream buf inp1 inp2 stp st y = do
138 (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y
141 let state = (Prelude.take n buf1, StepState s, inp11, inp21)
142 in assert (n <= length buf1) (return (state, Yld n))
144 let src0 = Prelude.take n buf1
145 src = Prelude.reverse src0
146 state = ([], StepState s, src ++ inp11, inp21)
147 in assert (n <= length buf1) (return (state, Yld n))
149 let state = (Prelude.take n buf1, StepResult b, inp11, inp21)
150 in assert (n <= length buf1) (return (state, Stp n))
151 -- Skip 0 s -> (buf1, Right s, inp11, inp21)
153 let (src0, buf2) = splitAt n buf1
154 src = Prelude.reverse src0
155 state = (buf2, StepState s, src ++ inp11, inp21)
156 in assert (n <= length buf1) (return (state, Skp))
157 Error err -> return (undefined, Err err)
159 {-# INLINE_LATE step #-}
160 step (TeePair (bufL, StepState sL, inpL1, inpL2)
161 (bufR, StepState sR, inpR1, inpR2)) x = do
162 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
163 (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x
164 let next = TeePair l r
165 return $ case (stL,stR) of
166 (Yld n1, Yld n2) -> Yield (min n1 n2) next
167 (Yld n1, Stp n2) -> Yield (min n1 n2) next
168 (Stp n1, Yld n2) -> Yield (min n1 n2) next
170 -- Uni-pattern match results in better optimized code compared
172 let (_, StepResult rL, _, _) = l
173 (_, StepResult rR, _, _) = r
174 in Stop (min n1 n2) (zf rL rR)
175 (Err err, _) -> Error err
176 (_, Err err) -> Error err
179 step (TeePair (bufL, StepState sL, inpL1, inpL2)
180 r@(_, StepResult rR, _, _)) x = do
181 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
182 let next = TeePair l r
183 -- XXX If the unused count of this stream is lower than the unused
184 -- count of the stopped stream, only then this will be correct. We need
185 -- to fix the other case. We need to keep incrementing the unused count
186 -- of the stopped stream and take the min of the two.
188 Yld n -> Yield n next
190 let (_, StepResult rL, _, _) = l
195 step (TeePair l@(_, StepResult rL, _, _)
196 (bufR, StepState sR, inpR1, inpR2)) x = do
197 (r, stR) <- useStream bufR inpR1 inpR2 stepR sR x
198 let next = TeePair l r
199 -- XXX If the unused count of this stream is lower than the unused
200 -- count of the stopped stream, only then this will be correct. We need
201 -- to fix the other case. We need to keep incrementing the unused count
202 -- of the stopped stream and take the min of the two.
204 Yld n -> Yield n next
206 let (_, StepResult rR, _, _) = r
213 {-# INLINE_LATE extract #-}
216 TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do
220 TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do
223 TeePair (_, StepResult rL, _, _) (_, StepState sR, _, _) -> do
226 TeePair (_, StepResult rL, _, _) (_, StepResult rR, _, _) ->
229-- | See 'Streamly.Internal.Data.Parser.teeWithFst'.
233{-# INLINE teeWithFst #-}
235 => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
236teeWithFst zf (Parser stepL initialL extractL)
237 (Parser stepR initialR extractR) =
238 Parser step initial extract
242 {-# INLINE_LATE initial #-}
246 return $ TeePair ([], StepState sL, [], []) ([], StepState sR, [], [])
248 {-# INLINE consume #-}
249 consume buf inp1 inp2 stp st y = do
250 let (x, inp11, inp21) =
253 z : [] -> (z, reverse (x:inp2), [])
254 z : zs -> (z, zs, x:inp2)
257 return (buf1, r, inp11, inp21)
259 -- consume one input item and return the next state of the fold
260 {-# INLINE useStream #-}
261 useStream buf inp1 inp2 stp st y = do
262 (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y
265 let state = (Prelude.take n buf1, StepState s, inp11, inp21)
266 in assert (n <= length buf1) (return (state, Yld n))
267 YieldB _ _ -> undefined
269 let state = (Prelude.take n buf1, StepResult b, inp11, inp21)
270 in assert (n <= length buf1) (return (state, Stp n))
271 -- Skip 0 s -> (buf1, Right s, inp11, inp21)
273 let (src0, buf2) = splitAt n buf1
274 src = Prelude.reverse src0
275 state = (buf2, StepState s, src ++ inp11, inp21)
276 in assert (n <= length buf1) (return (state, Skp))
277 Error err -> return (undefined, Err err)
279 {-# INLINE_LATE step #-}
280 step (TeePair (bufL, StepState sL, inpL1, inpL2)
281 (bufR, StepState sR, inpR1, inpR2)) x = do
282 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
283 (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x
284 let next = TeePair l r
286 -- XXX what if the first parser returns an unused count which is
287 -- more than the second parser's unused count? It does not make
288 -- sense for the second parser to consume more than the first
289 -- parser. We reset the input cursor based on the first parser.
290 -- Error out if the second one has consumed more then the first?
292 -- Uni-pattern match results in better optimized code compared
294 let (_, StepResult rL, _, _) = l
295 (_, StepResult rR, _, _) = r
296 in return $ Stop n1 (zf rL rR)
298 let (_, StepResult rL, _, _) = l
299 (_, StepState ssR, _, _) = r
302 return $ Stop n1 (zf rL rR)
303 (Yld n1, Yld n2) -> return $ Yield (min n1 n2) next
304 (Yld n1, Stp n2) -> return $ Yield (min n1 n2) next
305 (Err err, _) -> return $ Error err
306 (_, Err err) -> return $ Error err
307 _ -> return $ Skip 0 next
309 step (TeePair (bufL, StepState sL, inpL1, inpL2)
310 r@(_, StepResult rR, _, _)) x = do
311 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
312 let next = TeePair l r
313 -- XXX If the unused count of this stream is lower than the unused
314 -- count of the stopped stream, only then this will be correct. We need
315 -- to fix the other case. We need to keep incrementing the unused count
316 -- of the stopped stream and take the min of the two.
318 Yld n -> Yield n next
320 let (_, StepResult rL, _, _) = l
327 {-# INLINE_LATE extract #-}
330 TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do
334 TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do
337 _ -> error "unreachable"
339-- | See 'Streamly.Internal.Data.Parser.teeWithMin'.
343{-# INLINE teeWithMin #-}
346 (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
347teeWithMin = undefined
349-------------------------------------------------------------------------------
350-- Distribute input to two parsers and choose one result
351-------------------------------------------------------------------------------
353-- | See 'Streamly.Internal.Data.Parser.shortest'.
357{-# INLINE shortest #-}
358shortest :: Monad m => Parser m x a -> Parser m x a -> Parser m x a
359shortest (Parser stepL initialL extractL) (Parser stepR initialR _) =
360 Parser step initial extract
364 {-# INLINE_LATE initial #-}
368 return $ TeePair ([], StepState sL, [], []) ([], StepState sR, [], [])
370 {-# INLINE consume #-}
371 consume buf inp1 inp2 stp st y = do
372 let (x, inp11, inp21) =
375 z : [] -> (z, reverse (x:inp2), [])
376 z : zs -> (z, zs, x:inp2)
379 return (buf1, r, inp11, inp21)
381 -- consume one input item and return the next state of the fold
382 {-# INLINE useStream #-}
383 useStream buf inp1 inp2 stp st y = do
384 (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y
387 let state = (Prelude.take n buf1, StepState s, inp11, inp21)
388 in assert (n <= length buf1) (return (state, Yld n))
389 YieldB _ _ -> undefined
391 let state = (Prelude.take n buf1, StepResult b, inp11, inp21)
392 in assert (n <= length buf1) (return (state, Stp n))
393 -- Skip 0 s -> (buf1, Right s, inp11, inp21)
395 let (src0, buf2) = splitAt n buf1
396 src = Prelude.reverse src0
397 state = (buf2, StepState s, src ++ inp11, inp21)
398 in assert (n <= length buf1) (return (state, Skp))
399 Error err -> return (undefined, Err err)
401 -- XXX Even if a parse finished earlier it may not be shortest if the other
402 -- parser finishes later but returns a lot of unconsumed input. Our current
403 -- criterion of shortest is whichever parse decided to stop earlier.
404 {-# INLINE_LATE step #-}
405 step (TeePair (bufL, StepState sL, inpL1, inpL2)
406 (bufR, StepState sR, inpR1, inpR2)) x = do
407 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
408 (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x
409 let next = TeePair l r
410 return $ case (stL,stR) of
412 let (_, StepResult rL, _, _) = l
415 let (_, StepResult rR, _, _) = r
417 (Yld n1, Yld n2) -> Yield (min n1 n2) next
418 (Err err, _) -> Error err
419 (_, Err err) -> Error err
424 {-# INLINE_LATE extract #-}
427 TeePair (_, StepState sL, _, _) _ -> extractL sL
428 _ -> error "unreachable"
430-- | See 'Streamly.Internal.Data.Parser.longest'.
434{-# INLINE longest #-}
435longest :: MonadCatch m => Parser m x a -> Parser m x a -> Parser m x a
436longest (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
437 Parser step initial extract
441 {-# INLINE_LATE initial #-}
445 return $ TeePair ([], StepState sL, [], []) ([], StepState sR, [], [])
447 {-# INLINE consume #-}
448 consume buf inp1 inp2 stp st y = do
449 let (x, inp11, inp21) =
452 z : [] -> (z, reverse (x:inp2), [])
453 z : zs -> (z, zs, x:inp2)
456 return (buf1, r, inp11, inp21)
458 -- consume one input item and return the next state of the fold
459 {-# INLINE useStream #-}
460 useStream buf inp1 inp2 stp st y = do
461 (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y
464 let state = (Prelude.take n buf1, StepState s, inp11, inp21)
465 in assert (n <= length buf1) (return (state, Yld n))
466 YieldB _ _ -> undefined
468 let state = (Prelude.take n buf1, StepResult b, inp11, inp21)
469 in assert (n <= length buf1) (return (state, Stp n))
470 -- Skip 0 s -> (buf1, Right s, inp11, inp21)
472 let (src0, buf2) = splitAt n buf1
473 src = Prelude.reverse src0
474 state = (buf2, StepState s, src ++ inp11, inp21)
475 in assert (n <= length buf1) (return (state, Skp))
476 Error err -> return (undefined, Err err)
478 {-# INLINE_LATE step #-}
479 step (TeePair (bufL, StepState sL, inpL1, inpL2)
480 (bufR, StepState sR, inpR1, inpR2)) x = do
481 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
482 (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x
483 let next = TeePair l r
484 return $ case (stL,stR) of
485 (Yld n1, Yld n2) -> Yield (min n1 n2) next
486 (Yld n1, Stp n2) -> Yield (min n1 n2) next
487 (Stp n1, Yld n2) -> Yield (min n1 n2) next
489 let (_, StepResult rL, _, _) = l
490 (_, StepResult rR, _, _) = r
491 in Stop (max n1 n2) (if n1 >= n2 then rL else rR)
492 (Err err, _) -> Error err
493 (_, Err err) -> Error err
496 -- XXX the parser that finishes last may not be the longest because it may
497 -- return a lot of unused input which makes it shorter. Our current
498 -- criterion of deciding longest is based on whoever decides to finish
499 -- last and not whoever consumed more input.
501 -- To actually know who made more progress we need to keep an account of
502 -- how many items are unconsumed since the last yield.
504 step (TeePair (bufL, StepState sL, inpL1, inpL2)
505 r@(_, StepResult _, _, _)) x = do
506 (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x
507 let next = TeePair l r
509 Yld n -> Yield n next
511 let (_, StepResult rL, _, _) = l
516 step (TeePair l@(_, StepResult _, _, _)
517 (bufR, StepState sR, inpR1, inpR2)) x = do
518 (r, stR) <- useStream bufR inpR1 inpR2 stepR sR x
519 let next = TeePair l r
521 Yld n -> Yield n next
523 let (_, StepResult rR, _, _) = r
530 {-# INLINE_LATE extract #-}
532 -- XXX When results are partial we may not be able to precisely compare
533 -- which parser has made more progress till now. One way to do that is
534 -- to figure out the actually consumed input up to the last yield.
537 TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do
538 r <- try $ extractL sL
540 Left (_ :: ParseError) -> extractR sR
542 TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do
543 r <- try $ extractL sL
545 Left (_ :: ParseError) -> return rR
547 TeePair (_, StepResult rL, _, _) (_, StepState sR, _, _) -> do
548 r <- try $ extractR sR
550 Left (_ :: ParseError) -> return rL
552 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