packages feed

foldl-transduce 0.4.6.0 → 0.4.7.0

raw patch · 5 files changed

+178/−19 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Control.Foldl.Transduce.Text: instance GHC.Show.Show Control.Foldl.Transduce.Text.SectionsState
+ Control.Foldl.Transduce.Text: sections :: [Text] -> Transducer Text Text ()
+ Control.Foldl.Transduce.Text: textualBreak :: TextualMonoid m => (Char -> Bool) -> Transducer m m ()
+ Control.Foldl.Transduce.Text: textualSplit :: TextualMonoid m => (Char -> Bool) -> Transducer m m ()

Files

CHANGELOG view
@@ -1,3 +1,8 @@+# 0.4.7.0++- Added "sections" splitter.+- Deprecated Control.Foldl.Transduce.Textual+ # 0.4.6.0  - Deprecated Control.Foldl.Transduce.ByteString.IO 
foldl-transduce.cabal view
@@ -1,5 +1,5 @@ Name: foldl-transduce-Version: 0.4.6.0+Version: 0.4.7.0 Cabal-Version: >=1.8.0.2 Build-Type: Simple License: BSD3
src/Control/Foldl/Transduce/Text.hs view
@@ -21,26 +21,32 @@     ,   words     ,   lines     ,   paragraphs-        -- * Re-exports-        -- $reexports-    ,   module Control.Foldl.Transduce.Textual+    ,   sections+        -- * Textual+        -- $textual+    ,   textualSplit+    ,   textualBreak     ) where  import Prelude hiding (lines,words) import Data.Char-import Data.Monoid (mempty)+import Data.Bool+import Data.Maybe+import Data.List (unfoldr)+import Data.Monoid (mempty,(<>)) import Data.Foldable (foldMap,foldl') import qualified Data.ByteString as B import qualified Data.Text  import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding.Error as T+import qualified Data.Monoid.Textual as MT+import qualified Data.Monoid.Null as MN import Control.Applicative import Control.Monad.Trans.Except import Control.Monad.IO.Class import Control.Exception.Base  import qualified Control.Foldl.Transduce as L-import Control.Foldl.Transduce.Textual import Control.Foldl.Transduce.Internal (Pair(..)) import qualified Data.List import Data.List.Split@@ -337,7 +343,7 @@                 (SkippingAfterStreamStart, False) ->                      (,)                     SkippingAfterNewline-                    (prepend ["\n",T.stripStart i] outputs) +                    (continue ["\n",T.stripStart i] outputs)                  (SkippingAfterNewline, True) ->                      (,)                      SkippingAfterBlankLine @@ -345,7 +351,7 @@                 (SkippingAfterNewline, False) ->                      (,)                     SkippingAfterNewline-                    (prepend ["\n",T.stripStart i] outputs)+                    (continue ["\n",T.stripStart i] outputs)                 (SkippingAfterBlankLine, True) ->                      (,)                      SkippingAfterBlankLine @@ -353,11 +359,11 @@                 (SkippingAfterBlankLine, False) ->                      (,)                     SkippingAfterNewline-                    (prepend ["\n",T.stripStart i] (NonEmpty.cons [] outputs)) +                    (continue ["\n",T.stripStart i] (NonEmpty.cons [] outputs))                  (ContinuingNonemptyLine, _) ->                      (,)                     SkippingAfterNewline-                    (prepend ["\n",i] outputs)+                    (continue ["\n",i] outputs)         advanceLast                  :: (ParagraphsState, NonEmpty [T.Text])                  -> T.Text @@ -371,7 +377,7 @@                 (SkippingAfterStreamStart, False) ->                      (,)                     ContinuingNonemptyLine-                    (prepend [T.stripStart i] outputs)+                    (continue [T.stripStart i] outputs)                 (SkippingAfterNewline, True) ->                      (,)                      SkippingAfterNewline @@ -379,7 +385,7 @@                 (SkippingAfterNewline, False) ->                      (,)                     ContinuingNonemptyLine-                    (prepend [T.stripStart i] outputs)+                    (continue [T.stripStart i] outputs)                 (SkippingAfterBlankLine, True) ->                      (,)                     SkippingAfterBlankLine@@ -387,16 +393,145 @@                 (SkippingAfterBlankLine, False) ->                      (,)                     ContinuingNonemptyLine-                    (prepend [T.stripStart i] (NonEmpty.cons [] outputs))+                    (continue [T.stripStart i] (NonEmpty.cons [] outputs))                 (ContinuingNonemptyLine, _) ->                      (,)                     ContinuingNonemptyLine-                    (prepend [i] outputs)-        prepend :: [a] -> NonEmpty [a] -> NonEmpty [a]-        prepend as (as':| rest) = (as ++ as') :| rest+                    (continue [i] outputs) +{-| ++    Given a (possibly infinite) list of section headings, split the stream into+    sections and remove the headings. ++>>> map mconcat (L.fold (folds (sections (map T.pack ["#1\n","#2\n"])) L.list L.list) (map T.pack [" #1\naa\n#","2\nbb"]))+[" ","aa\n","bb"]++>>> map mconcat (L.fold (folds (sections (map T.pack ["1234"])) L.list L.list) (map T.pack [" 1","2","x","1","2","3","4","5"]))+[" 12x","5"]++    Used with 'L.transduce', it simply removes all headings.+-}+sections :: [T.Text] -> L.Transducer T.Text T.Text ()+sections seps = L.Transducer step (initialstate seps) done +    where+        step tstate txt =+            let (emitted,fmap snd -> states) = Data.List.unzip (unfoldWithState splitTextStep (txt,tstate))+                finalState = NonEmpty.last (tstate :| states)+                continuing :| following = NonEmpty.reverse (fmap Data.List.reverse (foldl' advance ([]:|[]) emitted))+            in (finalState, continuing, following)                        +        advance :: NonEmpty [x] -> ([x],Bool) -> NonEmpty [x]+        advance l (e,b) = bool id (separate []) b (continue e l)+        done Done = +            ((),[],[])+        done (Pending acc _ _) =+            ((),[acc],[])    +        initialstate [] = Done+        initialstate (x:xs) = Pending T.empty x xs+++continue :: [a] -> NonEmpty [a] -> NonEmpty [a]+continue as (as':| rest) = (as ++ as') :| rest++separate :: [x] -> NonEmpty [x] -> NonEmpty [x]+separate = NonEmpty.cons++data SectionsState = +      Done+    | Pending T.Text T.Text [T.Text] -- first is the accumulator+    deriving (Show)++{-| 		++>>> splitTextStep (T.pack "x",Done)+Just ((["x"],False),("",Done))++>>> splitTextStep (T.pack "aabbcc",Pending T.empty (T.pack "bb") [])+Just ((["aa"],True),("cc",Done))++>>> splitTextStep (T.pack "cc",Pending (T.pack "bb") (T.pack "bbcc") [T.pack "nextsep"])+Just (([""],True),("",Pending "" "nextsep" []))++>>> splitTextStep (T.pack "xx",Pending (T.pack "bb") (T.pack "bbcc") [])+Just ((["bbxx"],False),("",Pending "" "bbcc" []))++>>> splitTextStep (T.pack "xbb",Pending (T.pack "bbc") (T.pack "bbcccc") [])+Just ((["bbcx"],False),("",Pending "bb" "bbcccc" []))++-}+splitTextStep +    :: (T.Text, SectionsState) +    -> Maybe (([T.Text],Bool), (T.Text, SectionsState))+splitTextStep (txt, _) | T.null txt           = Nothing+splitTextStep (txt, Done)                     = Just (([txt],False),(T.empty,Done))+splitTextStep (txt, Pending acc sep nextseps) = Just $+    let (before,after) = T.breakOn sep (acc <> txt)+    in+    if T.null after +       then -- not present+          let (m0,m) = maxintersect before sep+          in+          (([m0],False),(T.empty, Pending m sep nextseps))+       else -- present+          let unprefixed = T.drop (T.length sep) after+              nextstate = case nextseps of+                  [] -> Done+                  z:zs -> Pending T.empty z zs+          in+          (([before],True),(unprefixed,nextstate))+                               +maxintersect :: T.Text -> T.Text -> (T.Text,T.Text)+maxintersect txt sep = +    let prefixes = (tail . reverse . tail . T.inits) sep +        partialmatches = filter (flip T.isSuffixOf txt) prefixes+        m = maybe T.empty id (listToMaybe partialmatches)+    in+    (T.take (T.length txt - T.length m) txt,m)+        +unfoldWithState :: (b -> Maybe (a, b)) -> b -> [(a, b)]+unfoldWithState f = unfoldr (fmap (\t@(_, b) -> (t, b)) . f)+ ------------------------------------------------------------------------------ -{- $reexports+{- $textual +    Transducers that work on 'Text' and other text-like types.+ -}++{-| ++>>> L.fold (folds (textualSplit (=='.')) L.list L.list) [".","bb.bb","c.c."]+[[""],["","bb"],["bb","c"],["c"],[""]]++-}++textualSplit :: MT.TextualMonoid m => (Char -> Bool) -> L.Transducer m m ()+textualSplit predicate = L.Transducer step () done +  where+    step _ txt = case MT.split predicate txt of+        x:xs -> ((),[x],map (:[]) xs)+        _ -> error "never happens"+    done _ = mempty+++data SplitWhenWhenState = +      SplitWhenConditionEncountered +    | SplitWhenConditionPending++{-| 		++>>> L.fold (bisect (textualBreak (=='.')) (reify id) ignore L.list) ["aa","bb.bb","cc"]+["aa","bb"]+-}+textualBreak :: MT.TextualMonoid m => (Char -> Bool) -> L.Transducer m m ()+textualBreak predicate = +    L.Transducer step SplitWhenConditionPending done +    where+        step SplitWhenConditionPending (MT.break (const False) predicate -> (i0,i1)) = +            if MN.null i1+               then (SplitWhenConditionPending,[i0],[])+               else (SplitWhenConditionEncountered,[i0],[[i1]])+        step SplitWhenConditionEncountered i = +               (SplitWhenConditionEncountered,[i],[])+        done = mempty
src/Control/Foldl/Transduce/Textual.hs view
@@ -3,7 +3,7 @@ -- | -- -- This module has transducers that work on 'Text' and other text-like types.-module Control.Foldl.Transduce.Textual (+module Control.Foldl.Transduce.Textual {-# DEPRECATED "Use Control.Foldl.Transduce.Text instead." #-} (         -- * Splitters         textualSplit     ,   textualBreak
tests/tests.hs view
@@ -18,7 +18,6 @@ import qualified Control.Foldl as L import Control.Foldl.Transduce import Control.Foldl.Transduce.Text-import Control.Foldl.Transduce.Textual  {- $quickcheck @@ -132,6 +131,11 @@ paragraphsUnderTest txt =     map mconcat (L.fold (folds paragraphs L.list L.list) txt) +sectionsUnderTest+    :: [T.Text] -> [T.Text] -> [T.Text] +sectionsUnderTest stns txt =+    map mconcat (L.fold (folds (sections stns) L.list L.list) txt)+ paragraph01 :: T.Text paragraph01 =      T.pack @@ -220,6 +224,21 @@                         ==                         ignoreLastNewline (paragraphsBaseline (mconcat chunks)))             ]+        ],+        testGroup "sections"+        [+            testCaseEq "no separators at all"+                (map T.pack ["aaabbcc"])+                (sectionsUnderTest (map T.pack []) (map T.pack ["a","aa","bbc","c"])),+            testCaseEq "incomplete separator"+                (map T.pack ["123#_","aa","bb#"])+                (sectionsUnderTest (map T.pack ["1234","#"]) (map T.pack ["1","23","#_1234aa#b","b#"])),+            testCaseEq "small chunks"+                (map T.pack ["0","01","aa","a#bb","c"])+                (sectionsUnderTest (map T.pack ["_","_","##","##","##"]) (map T.pack ["0","_","0","1_","a","a","#","#a","#","b","b#","#","c"])),+            testCaseEq "big chunk with multiple seps"+                (map T.pack ["1x","aa","bb","cc1x","dd"])+                (sectionsUnderTest (map T.pack (cycle ["12"])) (map T.pack ["1","x12aa12bb12cc1","x1","2dd"]))         ],         testGroup "quiesceWith"           [