on master help sign in
Add Parser(D).groupByRolling
401f504Adithya Kumar
src/Streamly/Internal/Data/Parser.hs+33
@@ -92,6 +92,7 @@ module Streamly.Internal.Data.Parser
92 , escapedFrameBy
92 , escapedFrameBy
93 , wordBy
93 , wordBy
94 , groupBy
94 , groupBy
95+ , groupByRolling
95 , eqBy
96 , eqBy
96 -- | Unimplemented
97 -- | Unimplemented
97 --
98 --
@@ -595,6 +596,38 @@ wordBy f = K.toParserK . D.wordBy f
595groupBy :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
596groupBy :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
596groupBy cmp = K.toParserK . D.groupBy cmp
597groupBy cmp = K.toParserK . D.groupBy cmp
597
598
599+-- | Unlike 'groupBy' this combinator performs a rolling comparison of two
600+-- successive elements in the input stream. Assuming the input stream to the
601+-- parser is @[a,b,c,...]@ and the comparison function is @cmp@, the parser
602+-- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is
603+-- 'True' @b@ is also assigned to the same group. If @b \`cmp` c@ is 'True'
604+-- then @c@ is also assigned to the same group and so on. When the comparison
605+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
606+-- the result of the fold is the result of the parser.
607+--
608+-- * Stops - when the comparison fails.
609+-- * Fails - never.
610+--
611+-- > runGroupsByRolling eq =
612+-- > Stream.toList
613+-- > . Stream.parseMany (groupByRolling eq Fold.toList)
614+-- > . Stream.fromList
615+--
616+-- >>> runGroupsByRolling (<) []
617+-- []
618+--
619+-- >>> runGroupsByRolling (<) [1]
620+-- [[1]]
621+--
622+-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]
623+-- [[3, 5], [4], [1, 2], [0]]
624+--
625+-- /Internal/
626+--
627+{-# INLINABLE groupByRolling #-}
628+groupByRolling :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
629+groupByRolling eq = K.toParserK . D.groupByRolling eq
630+
598-- | Match the given sequence of elements using the given comparison function.
631-- | Match the given sequence of elements using the given comparison function.
599--
632--
600-- >>> S.parse $ S.eqBy (==) "string" $ S.fromList "string"
633-- >>> S.parse $ S.eqBy (==) "string" $ S.fromList "string"
src/Streamly/Internal/Data/Parser/ParserD.hs+33
@@ -63,6 +63,7 @@ module Streamly.Internal.Data.Parser.ParserD
63 --
63 --
64 , wordBy
64 , wordBy
65 , groupBy
65 , groupBy
66+ , groupByRolling
66 , eqBy
67 , eqBy
67 -- , prefixOf -- match any prefix of a given string
68 -- , prefixOf -- match any prefix of a given string
68 -- , suffixOf -- match any suffix of a given string
69 -- , suffixOf -- match any suffix of a given string
@@ -624,6 +625,38 @@ groupBy cmp (Fold fstep finitial fextract) = Parser step initial extract
624 extract (GroupByInit s) = fextract s
625 extract (GroupByInit s) = fextract s
625 extract (GroupByGrouping _ s) = fextract s
626 extract (GroupByGrouping _ s) = fextract s
626
627
628+-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
629+--
630+{-# INLINE groupByRolling #-}
631+groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
632+groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
633+
634+ where
635+
636+ {-# INLINE grouper #-}
637+ grouper s a = do
638+ res <- fstep s a
639+ return
640+ $ case res of
641+ FL.Done b -> Done 0 b
642+ FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
643+
644+ initial = do
645+ res <- finitial
646+ return
647+ $ case res of
648+ FL.Partial s -> GroupByInit s
649+ FL.Done _ -> error "groupByRolling: Done in initial not implemented"
650+
651+ step (GroupByInit s) a = grouper s a
652+ step (GroupByGrouping a0 s) a =
653+ if eq a0 a
654+ then grouper s a
655+ else Done 1 <$> fextract s
656+
657+ extract (GroupByInit s) = fextract s
658+ extract (GroupByGrouping _ s) = fextract s
659+
627-- XXX use an Unfold instead of a list?
660-- XXX use an Unfold instead of a list?
628-- XXX custom combinators for matching list, array and stream?
661-- XXX custom combinators for matching list, array and stream?
629--
662--