on master help sign in
Simplify the takeBetween Parser
85033fdHarendra Kumar
src/Streamly/Internal/Data/Parser/ParserD.hs+60−47
@@ -166,6 +166,7 @@ module Streamly.Internal.Data.Parser.ParserD
166where
166where
167
167
168import Control.Exception (assert, Exception)
168import Control.Exception (assert, Exception)
169+import Control.Monad (when)
169import Control.Monad.Catch (MonadCatch, MonadThrow(..))
170import Control.Monad.Catch (MonadCatch, MonadThrow(..))
170import Fusion.Plugin.Types (Fuse(..))
171import Fusion.Plugin.Types (Fuse(..))
171import Streamly.Internal.Data.Fold.Type (Fold(..))
172import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -352,6 +353,10 @@ either parser = Parser step initial extract
352-- Taking elements
353-- Taking elements
353-------------------------------------------------------------------------------
354-------------------------------------------------------------------------------
354
355
356+-- Required to fuse "take" with "many" in "chunksOf", for ghc-9.x
357+{-# ANN type Tuple'Fused Fuse #-}
358+data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
359+
355-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
360-- | See 'Streamly.Internal.Data.Parser.takeBetween'.
356--
361--
357-- /Pre-release/
362-- /Pre-release/
@@ -360,61 +365,69 @@ either parser = Parser step initial extract
360takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
365takeBetween :: MonadCatch m => Int -> Int -> Fold m a b -> Parser m a b
361takeBetween low high (Fold fstep finitial fextract) =
366takeBetween low high (Fold fstep finitial fextract) =
362
367
363− Parser step initial extract
368+ Parser step initial (extract streamErr)
364
369
365 where
370 where
366
371
367− initial = do
372+ streamErr i =
368− res <- finitial
373+ "takeBetween: Expecting alteast " ++ show low
374+ ++ " elements, got " ++ show i
375+
376+ invalidRange =
377+ "takeBetween: lower bound - " ++ show low
378+ ++ " is greater than higher bound - " ++ show high
379+
380+ foldErr :: Int -> String
381+ foldErr i =
382+ "takeBetween: the collecting fold terminated after"
383+ ++ " consuming" ++ show i ++ " elements"
384+ ++ " minimum" ++ show low ++ " elements needed"
385+
386+ -- Exactly the same as snext except different constructors, we can possibly
387+ -- deduplicate the two.
388+ {-# INLINE inext #-}
389+ inext i res =
390+ let i1 = i + 1
369− return $ case res of
391+ in case res of
370− FL.Partial s -> IPartial $ Tuple' 0 s
392+ FL.Partial s -> do
393+ let s1 = Tuple'Fused i1 s
394+ if i1 < high
395+ -- XXX ideally this should be a Continue instead
396+ then return $ IPartial s1
397+ else IDone <$> extract foldErr s1
371 FL.Done b ->
398 FL.Done b ->
372− if low <= 0
399+ return
400+ $ if i1 >= low
373− then IDone b
401+ then IDone b
374− else IError
402+ else IError (foldErr i1)
375− $ "takeBetween: the collecting fold terminated without"
376− ++ " consuming any elements"
377− ++ " minimum" ++ show low ++ " elements needed"
378
403
379− step (Tuple' i s) a
404+ initial = do
380− | low > high =
405+ when (low >= 0 && high >= 0 && low > high)
381− throwM
382− $ ParseError
406+ $ throwM $ ParseError invalidRange
383− $ "takeBetween: lower bound - " ++ show low
407+
384− ++ " is greater than higher bound - " ++ show high
408+ finitial >>= inext (-1)
385− | high <= 0 = Done 1 <$> fextract s
409+
386− | i1 < low = do
410+ -- Keep the impl same as inext
387− res <- fstep s a
411+ {-# INLINE snext #-}
388− return
412+ snext i res =
389− $ case res of
413+ let i1 = i + 1
390− FL.Partial s1 -> Continue 0 $ Tuple' i1 s1
414+ in case res of
415+ FL.Partial s -> do
416+ let s1 = Tuple'Fused i1 s
417+ if i1 < high
418+ then return $ Continue 0 s1
419+ else Done 0 <$> extract foldErr s1
391− FL.Done _ ->
420+ FL.Done b ->
421+ return
422+ $ if i1 >= low
423+ then Done 0 b
392− Error
424+ else Error (foldErr i1)
393− $ "takeBetween: the collecting fold terminated after"
394− ++ " consuming" ++ show i1 ++ " elements"
395− ++ " minimum" ++ show low ++ " elements needed"
396− | otherwise = do
397− res <- fstep s a
398− case res of
399− FL.Partial s1 ->
400− if i1 >= high
401− then Done 0 <$> fextract s1
402− else return $ Partial 0 $ Tuple' i1 s1
403− FL.Done b -> return $ Done 0 b
404−
405− where
406
425
407− i1 = i + 1
426+ step (Tuple'Fused i s) a = fstep s a >>= snext i
408
427
409− extract (Tuple' i s)
428+ extract f (Tuple'Fused i s)
410 | i >= low && i <= high = fextract s
429 | i >= low && i <= high = fextract s
411− | otherwise = throwM $ ParseError err
430+ | otherwise = throwM $ ParseError (f i)
412−
413− where
414−
415− err =
416− "takeBetween: Expecting alteast " ++ show low
417− ++ " elements, got " ++ show i
418
431
419-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
432-- | See 'Streamly.Internal.Data.Parser.takeEQ'.
420--
433--
src/Streamly/Internal/Data/Parser/ParserD/Type.hs+25
@@ -157,6 +157,19 @@ import Prelude hiding (concatMap)
157-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream (parse)
157-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream (parse)
158-- >>> import qualified Streamly.Internal.Data.Parser as Parser
158-- >>> import qualified Streamly.Internal.Data.Parser as Parser
159
159
160+-- XXX The only differences between Initial and Step types are:
161+--
162+-- * There are no backtracking counts in Initial
163+-- * Continue and Partial are the same. Ideally Partial should mean that an
164+-- empty result is valid and can be extracted; and Continue should mean that
165+-- empty would result in an error on extraction. We can possibly distinguish
166+-- the two cases.
167+--
168+-- If we ignore the backtracking counts we can represent the Initial type using
169+-- Step itself. That will also simplify the implementation of various parsers
170+-- where the processing in intiial is just a sepcial case of step, see
171+-- takeBetween for example.
172+--
160-- | The type of a 'Parser''s initial action.
173-- | The type of a 'Parser''s initial action.
161--
174--
162-- /Internal/
175-- /Internal/
@@ -199,6 +212,18 @@ instance Functor (Initial s) where
199 {-# INLINE fmap #-}
212 {-# INLINE fmap #-}
200 fmap = second
213 fmap = second
201
214
215+-- We can simplify the Step type as follows:
216+--
217+-- Partial Int (Either s (s, b)) -- Left continue, right partial result
218+-- Done Int (Either String b)
219+--
220+-- In this case Error may also have a "leftover" return. This means that after
221+-- serveral successful partial results the last segment parsing failed and we
222+-- are returning the leftover of that. The driver may choose to restart from
223+-- the last segment where this parser failed or from the beginning.
224+--
225+-- Folds can only return the right values. Parsers can also return lefts.
226+
202-- | The return type of a 'Parser' step.
227-- | The return type of a 'Parser' step.
203--
228--
204-- The parse operation feeds the input stream to the parser one element at a
229-- The parse operation feeds the input stream to the parser one element at a