lens-regex-pcre 0.1.1.0 → 0.2.0.0
raw patch · 5 files changed
+257/−247 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Control.Lens.Regex: grouped :: Traversal' Match [Text]
- Control.Lens.Regex: igroups :: IndexedTraversal' Int Match Text
- Control.Lens.Regex: groups :: Traversal' Match Text
+ Control.Lens.Regex: groups :: Traversal' Match [Text]
Files
- ChangeLog.md +3/−0
- README.md +147/−159
- lens-regex-pcre.cabal +2/−2
- src/Control/Lens/Regex.hs +63/−36
- test/Spec.hs +42/−50
ChangeLog.md view
@@ -1,5 +1,8 @@ # Changelog for lens-regex-pcre +# 2.0.0 +Unify `grouped`, `groups`, and `igroups` into just `groups` with optional traversal+ # 1.1.0 Adds `grouped` and `matchAndGroups`
README.md view
@@ -1,6 +1,8 @@ # lens-regex-pcre -* NOTE: I don't promise that this is __fast__ yet;+[Hackage and Docs](http://hackage.haskell.org/package/lens-regex-pcre)++* NOTE: I don't promise that this is __fast__ yet, nor do I have any benchmarks; * NOTE: currently only supports `Text` but should be generalizable to more string-likes; open an issue if you need it Based on `pcre-heavy`; so it should support any regexes which it supports.@@ -13,48 +15,45 @@ and alter zero or more matches; traversals can even carry indexes so you know which match or group you're working on. --Note that all traversals in this library are not techically lawful; the break the 'multi-set'-idempotence law; in reality this isn't usually a problem; but consider yourself warned. Test your code.+Note that all traversals in this library are not techically lawful, as the semantics of regular expressions don't allow for it;+They break the 'multi-set' idempotence law of traversals (same one broken by `filtered` from `lens`); in reality this isn't usually a problem (I've literally never encountered an issue with it); but consider yourself warned. Test your code. Here are a few examples: ```haskell--- Getting all matches:-> "one _two_ three _four_" ^.. regex [rx|_\w+_|] . match-["_two_","_four_"]---- Regex replace/mutation-> "one _two_ three _four_" & regex [rx|_\w+_|] . match %~ T.toUpper-"one _TWO_ three _FOUR_"---- Getting groups with their index.-> "1/2 and 3/4" ^.. regex [rx|(\d+)/(\d+)|] . igroups . withIndex-[(0,"1"),(1,"2"),(0,"3"),(1,"4")]+txt :: Text+txt = "raindrops on roses and whiskers on kittens" --- Check for any matches:-> has (regex [rx|ne+dle|]) "a needle in a haystack"+-- Search+λ> has (regex [rx|whisk|]) txt True --- Check for matches which also match a predicate:-> has (regex [rx|\w+|] . match . filtered ((> 7) . T.length)) "one word here is loooooooong"-True+-- Get matches+λ> txt ^.. regex [rx|\br\w+|] . match+["raindrops","roses"] --- Get the third match-> "alpha beta charlie delta" ^? (iregex [rx|\w+|] . index 2 . match)-Just "charlie"+-- Edit matches+λ> txt & regex [rx|\br\w+|] . match %~ T.intersperse '-' . T.toUpper+"R-A-I-N-D-R-O-P-S on R-O-S-E-S and whiskers on kittens" --- Replace the third match-> "alpha beta charlie delta" & (iregex [rx|\w+|] . index 2 . match) .~ "GAMMA"-"alpha beta GAMMA delta"+-- Get Groups+λ> txt ^.. regex [rx|(\w+) on (\w+)|] . groups+[["raindrops","roses"],["whiskers","kittens"]] --- Sort all matches alphabetically in place-> "*charlie* beta = _alpha_ delta" & partsOf (iregex [rx|[a-z]+|] . match) %~ sort-"*alpha* beta = _charlie_ delta"+-- Edit Groups+λ> txt & regex [rx|(\w+) on (\w+)|] . groups %~ reverse+"roses on raindrops and kittens on whiskers" --- Match integers, 'Read' them into ints, then sort each match in-place-> "Monday: 29, Tuesday: 99, Wednesday: 3" & partsOf' (iregex [rx|\d+|] . match . unpacked . _Show @Int) %~ sort+-- Get the third match+λ> txt ^? iregex [rx|\w+|] . index 2 . match+Just "roses"++-- Match integers, 'Read' them into ints, then sort them in-place+-- dumping them back into the source text afterwards.+λ> "Monday: 29, Tuesday: 99, Wednesday: 3" + & partsOf (iregex [rx|\d+|] . match . unpacked . _Show @Int) %~ sort "Monday: 3, Tuesday: 29, Wednesday: 99"+ ``` Basically anything you want to do is possible somehow.@@ -62,133 +61,122 @@ Expected behaviour (and examples) can be found in the test suite: ```haskell-import Control.Lens-import Control.Lens.Regex+describe "regex" $ do+ describe "match" $ do+ describe "getting" $ do+ it "should find one match" $ do+ "abc" ^.. regex [rx|b|] . match+ `shouldBe` ["b"] -describe "regex" $ do - describe "match" $ do - describe "getting" $ do - it "should find one match" $ do - "abc" ^.. regex [rx|b|] . match - `shouldBe` ["b"] - - it "should find many matches" $ do - "a b c" ^.. regex [rx|\w|] . match - `shouldBe` ["a", "b", "c"] - - it "should fold" $ do - "a b c" ^. regex [rx|\w|] . match - `shouldBe` "abc" - - it "should match with a group" $ do - "a b c" ^.. regex [rx|(\w)|] . match - `shouldBe` ["a", "b", "c"] - - it "should match with many groups" $ do - "a b c" ^.. regex [rx|(\w) (\w)|] . match - `shouldBe` ["a b"] - - it "should be greedy when overlapping" $ do - "abc" ^.. regex [rx|\w+|] . match - `shouldBe`["abc"] - - it "should respect lazy modifiers" $ do - "abc" ^.. regex [rx|\w+?|] . match - `shouldBe`["a", "b", "c"] - - describe "setting" $ do - it "should allow setting" $ do - ("one two three" & regex [rx|two|] . match .~ "new") - `shouldBe` "one new three" - - it "should allow setting many" $ do - ("one <two> three" & regex [rx|\w+|] . match .~ "new") - `shouldBe` "new <new> new" - - it "should allow mutating" $ do - ("one two three" & regex [rx|two|] . match %~ (<> "!!"). T.toUpper) - `shouldBe` "one TWO!! three" - - it "should allow mutating many" $ do - ("one two three" & regex [rx|two|] . match %~ T.toUpper) - `shouldBe` "one TWO three" - - describe "groups" $ do - describe "getting" $ do - it "should get a group" $ do - "a b c" ^.. regex [rx|(\w)|] . groups - `shouldBe` ["a", "b", "c"] - - it "should get many groups" $ do - "one two three" ^.. regex [rx|(\w+) (\w+)|] . groups - `shouldBe` ["one", "two"] - - describe "setting" $ do - it "should allow setting" $ do - ("one two three" & regex [rx|(\w+) (\w+)|] . groups .~ "new") - `shouldBe` "new new three" - - it "should allow setting many" $ do - ("one two three four" & regex [rx|(\w+) (\w+)|] . groups .~ "new") - `shouldBe` "new new new new" - - it "should allow mutating" $ do - ("one two three four" & regex [rx|one (two) three|] . groups %~ (<> "!!") . T.toUpper) - `shouldBe` "one TWO!! three four" - - it "should allow mutating" $ do - ("one two three four" & regex [rx|one (two) (three)|] . groups %~ (<> "!!") . T.toUpper) - `shouldBe` "one TWO!! THREE!! four" - -describe "iregex" $ do - describe "match" $ do - it "should allow folding with index" $ do - ("one two three" ^.. (iregex [rx|\w+|] <. match) . withIndex) - `shouldBe` [(0, "one"), (1, "two"), (2, "three")] - - it "should allow getting with index" $ do - ("one two three" ^.. iregex [rx|\w+|] . index 1 . match) - `shouldBe` ["two"] - - it "should allow setting with index" $ do - ("one two three" & iregex [rx|\w+|] <. match .@~ T.pack . show) - `shouldBe` "0 1 2" - - it "should allow mutating with index" $ do - ("one two three" & iregex [rx|\w+|] <. match %@~ \i s -> (T.pack $ show i) <> ": " <> s) - `shouldBe` "0: one 1: two 2: three" - -describe "igroups" $ do - it "should allow folding with index" $ do - ("one two three four" ^.. regex [rx|(\w+) (\w+)|] . igroups . withIndex) - `shouldBe` [(0, "one"), (1, "two"), (0, "three"), (1, "four")] - - it "should allow getting a specific index" $ do - ("one two three four" ^.. regex [rx|(\w+) (\w+)|] . igroups . index 1) - `shouldBe` ["two", "four"] - - it "should allow setting with index" $ do - ("one two three four" & regex [rx|(\w+) (\w+)|] . igroups .@~ T.pack . show) - `shouldBe` "0 1 0 1" - - it "should allow mutating with index" $ do - ("one two three four" & regex [rx|(\w+) (\w+)|] . igroups %@~ \i s -> (T.pack $ show i) <> ": " <> s) - `shouldBe` "0: one 1: two 0: three 1: four" - - it "should compose indices with matches" $ do - ("one two three four" ^.. (iregex [rx|(\w+) (\w+)|] <.> igroups) . withIndex) - `shouldBe` [((0, 0), "one"), ((0, 1), "two"), ((1, 0), "three"), ((1, 1), "four")] - -describe "grouped" $ do - it "should get all groups in batches" $ do - "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . grouped - `shouldBe` [["raindrops","roses"],["whiskers","kittens"]] - it "should allow editing when result list is the same length" $ do - ("raindrops on roses and whiskers on kittens" & regex [rx|(\w+) on (\w+)|] . grouped %~ reverse) - `shouldBe` "roses on raindrops and kittens on whiskers" - -describe "matchAndGroups" $ do - it "should get match and groups" $ do - "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . matchAndGroups - `shouldBe` [("raindrops on roses",["raindrops","roses"]),("whiskers on kittens",["whiskers","kittens"])] + it "should find many matches" $ do+ "a b c" ^.. regex [rx|\w|] . match+ `shouldBe` ["a", "b", "c"]++ it "should fold" $ do+ "a b c" ^. regex [rx|\w|] . match+ `shouldBe` "abc"++ it "should match with a group" $ do+ "a b c" ^.. regex [rx|(\w)|] . match+ `shouldBe` ["a", "b", "c"]++ it "should match with many groups" $ do+ "a b c" ^.. regex [rx|(\w) (\w)|] . match+ `shouldBe` ["a b"]++ it "should be greedy when overlapping" $ do+ "abc" ^.. regex [rx|\w+|] . match+ `shouldBe`["abc"]++ it "should respect lazy modifiers" $ do+ "abc" ^.. regex [rx|\w+?|] . match+ `shouldBe`["a", "b", "c"]++ describe "setting" $ do+ it "should allow setting" $ do+ ("one two three" & regex [rx|two|] . match .~ "new")+ `shouldBe` "one new three"++ it "should allow setting many" $ do+ ("one <two> three" & regex [rx|\w+|] . match .~ "new")+ `shouldBe` "new <new> new"++ it "should allow mutating" $ do+ ("one two three" & regex [rx|two|] . match %~ (<> "!!"). T.toUpper)+ `shouldBe` "one TWO!! three"++ it "should allow mutating many" $ do+ ("one two three" & regex [rx|two|] . match %~ T.toUpper)+ `shouldBe` "one TWO three"++describe "iregex" $ do+ describe "match" $ do+ it "should allow folding with index" $ do+ ("one two three" ^.. (iregex [rx|\w+|] <. match) . withIndex)+ `shouldBe` [(0, "one"), (1, "two"), (2, "three")]++ it "should allow getting with index" $ do+ ("one two three" ^.. iregex [rx|\w+|] . index 1 . match)+ `shouldBe` ["two"]++ it "should allow setting with index" $ do+ ("one two three" & iregex [rx|\w+|] <. match .@~ T.pack . show)+ `shouldBe` "0 1 2"++ it "should allow mutating with index" $ do+ ("one two three" & iregex [rx|\w+|] <. match %@~ \i s -> (T.pack $ show i) <> ": " <> s)+ `shouldBe` "0: one 1: two 2: three"++describe "groups" $ do+ describe "getting" $ do+ it "should get groups" $ do+ "a b c" ^.. regex [rx|(\w)|] . groups+ `shouldBe` [["a"], ["b"], ["c"]]++ it "should get multiple groups" $ do+ "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . groups+ `shouldBe` [["raindrops","roses"],["whiskers","kittens"]]++ it "should allow getting a specific index" $ do+ ("one two three four" ^.. regex [rx|(\w+) (\w+)|] . groups . ix 1)+ `shouldBe` ["two", "four"]++ describe "setting" $ do+ it "should allow setting groups as a list" $ do+ ("one two three" & regex [rx|(\w+) (\w+)|] . groups .~ ["1", "2"])+ `shouldBe` "1 2 three"++ it "should allow editing when result list is the same length" $ do+ ("raindrops on roses and whiskers on kittens" & regex [rx|(\w+) on (\w+)|] . groups %~ reverse)+ `shouldBe` "roses on raindrops and kittens on whiskers"++ describe "traversed" $ do+ it "should allow setting all group matches" $ do+ ("one two three" & regex [rx|(\w+) (\w+)|] . groups . traversed .~ "new")+ `shouldBe` "new new three"++ it "should allow mutating" $ do+ ("one two three four" & regex [rx|one (two) (three)|] . groups . traversed %~ (<> "!!") . T.toUpper)+ `shouldBe` "one TWO!! THREE!! four"++ it "should allow folding with index" $ do+ ("one two three four" ^.. regex [rx|(\w+) (\w+)|] . groups . traversed . withIndex)+ `shouldBe` [(0, "one"), (1, "two"), (0, "three"), (1, "four")]++ it "should allow setting with index" $ do+ ("one two three four" & regex [rx|(\w+) (\w+)|] . groups . traversed .@~ T.pack . show)+ `shouldBe` "0 1 0 1"++ it "should allow mutating with index" $ do+ ("one two three four" & regex [rx|(\w+) (\w+)|] . groups . traversed %@~ \i s -> (T.pack $ show i) <> ": " <> s)+ `shouldBe` "0: one 1: two 0: three 1: four"++ it "should compose indices with matches" $ do+ ("one two three four" ^.. (iregex [rx|(\w+) (\w+)|] <.> groups . traversed) . withIndex)+ `shouldBe` [((0, 0), "one"), ((0, 1), "two"), ((1, 0), "three"), ((1, 1), "four")]++describe "matchAndGroups" $ do+ it "should get match and groups" $ do+ "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . matchAndGroups+ `shouldBe` [("raindrops on roses",["raindrops","roses"]),("whiskers on kittens",["whiskers","kittens"])] ```
lens-regex-pcre.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 847ca083cc4bffe2ad8319d0811ffdd549a436724924f1106c25966743c16fc6+-- hash: db3dca892f8e99d8936c709dd07b7d1a9ac11caa9f0cc2182b9367caa96e63c1 name: lens-regex-pcre-version: 0.1.1.0+version: 0.2.0.0 description: Please see the README on GitHub at <https://github.com/ChrisPenner/lens-regex-pcre#readme> homepage: https://github.com/ChrisPenner/lens-regex-pcre#readme bug-reports: https://github.com/ChrisPenner/lens-regex-pcre/issues
src/Control/Lens/Regex.hs view
@@ -4,7 +4,7 @@ Copyright : (c) Chris Penner, 2019 License : BSD3 -Note that all traversals in this library are not techically lawful; the break the 'multi-set'+Note that all traversals in this library are not techically lawful; they break the 'multi-set' idempotence law; in reality this isn't usually a problem; but consider yourself warned. Test your code. -} @@ -16,16 +16,18 @@ {-# LANGUAGE TemplateHaskell #-} module Control.Lens.Regex- ( regex+ (+ -- * Combinators+ regex , iregex , match , groups- , igroups- , grouped , matchAndGroups -- * QuasiQuoter , rx++ -- * Types , Match ) where @@ -34,7 +36,9 @@ import Control.Lens hiding (re, matching) import Language.Haskell.TH.Quote --- | Match represents a whole regex match; you can drill into it using 'match' or 'groups'+-- | Match represents a whole regex match; you can drill into it using 'match' or 'groups' or+-- 'matchAndGroups'+-- Consider this to be internal; don't depend on its representation. type Match = [Either Text Text] type MatchRange = (Int, Int) type GroupRanges = [(Int, Int)]@@ -45,38 +49,36 @@ rx :: QuasiQuoter rx = re --- | 'groups' but indexed by the group number. If you traverse over many matches you will--- encounter duplicate indices.--- E.g.+-- | Access all groups of a match at once. ----- > > "a 1 b 2" ^.. regex [rx|(\w) (\d)|] . igroups . withIndex--- > [(0,"a"),(1,"1"),(0,"b"),(1,"2")]+-- Note that you can edit the groups through this traversal,+-- Changing the length of the list has behaviour similar to 'partsOf'. ----- If you want only a specific group; combine this with `index`--- E.g.+-- Get all matched groups: ----- > > "a 1 b 2" ^.. regex [rx|(\w) (\d)|] . igroups . index 0--- > ["a","b"]-igroups :: IndexedTraversal' Int Match T.Text-igroups = indexing groups---- | traverse each group within a match. See 'igroups' for selecting specific groups or--- 'grouped' for handling all groups at once.-groups :: Traversal' Match T.Text-groups = traversed . _Right---- | Access all groups of a match at once.+-- > > "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . groups+-- > [["raindrops","roses"],["whiskers","kittens"]] ----- Note that this uses 'partsOf'; and is only a valid traversal if you don't--- alter the length of the list. It is valid as a Fold or Getter however.+-- You can access a specific group by combining with `ix` ----- > > "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . grouped--- > [["raindrops","roses"],["whiskers","kittens"]]+-- > > "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . groups . ix 1+-- > ["roses", "kittens"] ----- > > "raindrops on roses and whiskers on kittens" & regex [rx|(\w+) on (\w+)|] . grouped %~ reverse+-- @groups@ is a traversal; you can mutate matches through it.+-- > > "raindrops on roses and whiskers on kittens" & regex [rx|(\w+) on (\w+)|] . groups . ix 1 %~ T.toUpper+-- > "raindrops on ROSES and whiskers on KITTENS"+--+-- Editing the list rearranges groups+--+-- > > "raindrops on roses and whiskers on kittens" & regex [rx|(\w+) on (\w+)|] . groups %~ reverse -- > "roses on raindrops and kittens on whiskers"-grouped :: Traversal' Match [T.Text]-grouped = partsOf (traversed . _Right)+--+-- You can traverse the list to flatten out all groups+--+-- > > "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . groups . traversed+-- > ["raindrops","roses","whiskers","kittens"]+groups :: Traversal' Match [T.Text]+groups = partsOf (traversed . _Right) -- | Traverse each match as a whole --@@ -112,7 +114,7 @@ -- -- Getting groups with their group index. ----- > > "1/2 and 3/4" ^.. regex [rx|(\d+)/(\d+)|] . igroups . withIndex+-- > > "1/2 and 3/4" ^.. regex [rx|(\d+)/(\d+)|] . groups . traversed . withIndex -- > [(0,"1"),(1,"2"),(0,"3"),(1,"4")] -- -- Check for any matches:@@ -127,7 +129,7 @@ -- -- Get the third match ----- > > "alpha beta charlie delta" ^? (iregex [rx|\w+|] . index 2 . match)+-- > > "alpha beta charlie delta" ^? (iregex [rx|\w+|] . index 2 . match) -- > Just "charlie" -- -- Replace the third match@@ -140,15 +142,18 @@ -- > > "Monday: 29, Tuesday: 99, Wednesday: 3" & partsOf' (iregex [rx|\d+|] . match . unpacked . _Show @Int) %~ sort -- > "Monday: 3, Tuesday: 29, Wednesday: 99" regex :: Regex -> Traversal' T.Text Match-regex pattern f txt = collapse <$> apply (fmap splitAgain <$> splitter txt matches)+regex pattern f txt = collapseMatch <$> apply (fmap splitAgain <$> splitter txt matches) where matches :: [(MatchRange, GroupRanges)] matches = scanRanges pattern txt- collapse :: [Either Text [Either Text Text]] -> Text- collapse xs = xs ^. folded . beside id (traversed . chosen)+ collapseMatch :: [Either Text [Either Text Text]] -> Text+ collapseMatch xs = xs ^. folded . beside id (traversed . chosen) -- apply :: [Either Text [Either Text Text]] -> _ [Either Text [Either Text Text]] apply xs = xs & traversed . _Right %%~ f +matchText :: Match -> T.Text+matchText m = m ^. traversed . chosen+ -- | Collect both the match text AND all the matching groups -- -- > > "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . matchAndGroups@@ -156,7 +161,29 @@ -- > , ("whiskers on kittens", ["whiskers","kittens"]) -- > ] matchAndGroups :: Getter Match (T.Text, [T.Text])-matchAndGroups = to $ \m -> (m ^. traversed . chosen, m ^. grouped)+matchAndGroups = to $ \m -> (matchText m, m ^. groups)++-- | This allows you to "stash" the match text into an index for use later in the traversal.+-- This is a slight abuse of indices; but it can sometimes be handy. This allows you to+-- have the full match in scope when editing groups using indexed combinators.+--+-- If you're viewing or folding you should probably just use 'matchAndGroups'.+--+-- > > [(["raindrops","roses"],"raindrops on roses"),(["whiskers","kittens"],"whiskers on kittens")]+-- > "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . (withGroups <. match) . withIndex+withMatch :: IndexedTraversal' T.Text Match Match+withMatch p mtch = indexed p (matchText mtch) mtch++-- | This allows you to "stash" the match text into an index for use later in the traversal.+-- This is a slight abuse of indices; but it can sometimes be handy. This allows you to+-- have the full match in scope when editing groups using indexed combinators.+--+-- If you're viewing or folding you should probably just use 'matchAndGroups'.+--+-- > > "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . (withMatch <. groups) . withIndex+-- > [("raindrops on roses",["raindrops","roses"]),("whiskers on kittens",["whiskers","kittens"])]+withGroups :: IndexedTraversal' [T.Text] Match Match+withGroups p mtch = indexed p (mtch ^. groups) mtch splitter :: Text -> [(MatchRange, GroupRanges)] -> [Either T.Text (T.Text, GroupRanges)] splitter t [] | T.null t = []
test/Spec.hs view
@@ -55,33 +55,6 @@ ("one two three" & regex [rx|two|] . match %~ T.toUpper) `shouldBe` "one TWO three" - describe "groups" $ do- describe "getting" $ do- it "should get a group" $ do- "a b c" ^.. regex [rx|(\w)|] . groups- `shouldBe` ["a", "b", "c"]-- it "should get many groups" $ do- "one two three" ^.. regex [rx|(\w+) (\w+)|] . groups- `shouldBe` ["one", "two"]-- describe "setting" $ do- it "should allow setting" $ do- ("one two three" & regex [rx|(\w+) (\w+)|] . groups .~ "new")- `shouldBe` "new new three"-- it "should allow setting many" $ do- ("one two three four" & regex [rx|(\w+) (\w+)|] . groups .~ "new")- `shouldBe` "new new new new"-- it "should allow mutating" $ do- ("one two three four" & regex [rx|one (two) three|] . groups %~ (<> "!!") . T.toUpper)- `shouldBe` "one TWO!! three four"-- it "should allow mutating" $ do- ("one two three four" & regex [rx|one (two) (three)|] . groups %~ (<> "!!") . T.toUpper)- `shouldBe` "one TWO!! THREE!! four"- describe "iregex" $ do describe "match" $ do it "should allow folding with index" $ do@@ -100,34 +73,53 @@ ("one two three" & iregex [rx|\w+|] <. match %@~ \i s -> (T.pack $ show i) <> ": " <> s) `shouldBe` "0: one 1: two 2: three" - describe "igroups" $ do- it "should allow folding with index" $ do- ("one two three four" ^.. regex [rx|(\w+) (\w+)|] . igroups . withIndex)- `shouldBe` [(0, "one"), (1, "two"), (0, "three"), (1, "four")]+ describe "groups" $ do+ describe "getting" $ do+ it "should get groups" $ do+ "a b c" ^.. regex [rx|(\w)|] . groups+ `shouldBe` [["a"], ["b"], ["c"]] - it "should allow getting a specific index" $ do- ("one two three four" ^.. regex [rx|(\w+) (\w+)|] . igroups . index 1)- `shouldBe` ["two", "four"]+ it "should get multiple groups" $ do+ "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . groups+ `shouldBe` [["raindrops","roses"],["whiskers","kittens"]] - it "should allow setting with index" $ do- ("one two three four" & regex [rx|(\w+) (\w+)|] . igroups .@~ T.pack . show)- `shouldBe` "0 1 0 1"+ it "should allow getting a specific index" $ do+ ("one two three four" ^.. regex [rx|(\w+) (\w+)|] . groups . ix 1)+ `shouldBe` ["two", "four"] - it "should allow mutating with index" $ do- ("one two three four" & regex [rx|(\w+) (\w+)|] . igroups %@~ \i s -> (T.pack $ show i) <> ": " <> s)- `shouldBe` "0: one 1: two 0: three 1: four"+ describe "setting" $ do+ it "should allow setting groups as a list" $ do+ ("one two three" & regex [rx|(\w+) (\w+)|] . groups .~ ["1", "2"])+ `shouldBe` "1 2 three" - it "should compose indices with matches" $ do- ("one two three four" ^.. (iregex [rx|(\w+) (\w+)|] <.> igroups) . withIndex)- `shouldBe` [((0, 0), "one"), ((0, 1), "two"), ((1, 0), "three"), ((1, 1), "four")]+ it "should allow editing when result list is the same length" $ do+ ("raindrops on roses and whiskers on kittens" & regex [rx|(\w+) on (\w+)|] . groups %~ reverse)+ `shouldBe` "roses on raindrops and kittens on whiskers" - describe "grouped" $ do- it "should get all groups in batches" $ do- "raindrops on roses and whiskers on kittens" ^.. regex [rx|(\w+) on (\w+)|] . grouped- `shouldBe` [["raindrops","roses"],["whiskers","kittens"]]- it "should allow editing when result list is the same length" $ do- ("raindrops on roses and whiskers on kittens" & regex [rx|(\w+) on (\w+)|] . grouped %~ reverse)- `shouldBe` "roses on raindrops and kittens on whiskers"+ describe "traversed" $ do+ it "should allow setting all group matches" $ do+ ("one two three" & regex [rx|(\w+) (\w+)|] . groups . traversed .~ "new")+ `shouldBe` "new new three"++ it "should allow mutating" $ do+ ("one two three four" & regex [rx|one (two) (three)|] . groups . traversed %~ (<> "!!") . T.toUpper)+ `shouldBe` "one TWO!! THREE!! four"++ it "should allow folding with index" $ do+ ("one two three four" ^.. regex [rx|(\w+) (\w+)|] . groups . traversed . withIndex)+ `shouldBe` [(0, "one"), (1, "two"), (0, "three"), (1, "four")]++ it "should allow setting with index" $ do+ ("one two three four" & regex [rx|(\w+) (\w+)|] . groups . traversed .@~ T.pack . show)+ `shouldBe` "0 1 0 1"++ it "should allow mutating with index" $ do+ ("one two three four" & regex [rx|(\w+) (\w+)|] . groups . traversed %@~ \i s -> (T.pack $ show i) <> ": " <> s)+ `shouldBe` "0: one 1: two 0: three 1: four"++ it "should compose indices with matches" $ do+ ("one two three four" ^.. (iregex [rx|(\w+) (\w+)|] <.> groups . traversed) . withIndex)+ `shouldBe` [((0, 0), "one"), ((0, 1), "two"), ((1, 0), "three"), ((1, 1), "four")] describe "matchAndGroups" $ do it "should get match and groups" $ do