regex-do 2.6 → 2.6.1
raw patch · 16 files changed
+212/−67 lines, 16 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Text.Regex.Do.Format: instance Text.Regex.Do.Format.Format [(GHC.Base.String, GHC.Base.String)]
- Text.Regex.Do.Format: instance Text.Regex.Do.Format.Format [GHC.Base.String]
+ Text.Regex.Do.Format: class ReplaceOne idx a
+ Text.Regex.Do.Format: instance Text.Regex.Do.Format.ReplaceOne Data.ByteString.Internal.ByteString Data.ByteString.Internal.ByteString
+ Text.Regex.Do.Format: instance Text.Regex.Do.Format.ReplaceOne Data.Text.Internal.Text Data.Text.Internal.Text
+ Text.Regex.Do.Format: instance Text.Regex.Do.Format.ReplaceOne GHC.Base.String GHC.Base.String
+ Text.Regex.Do.Format: instance Text.Regex.Do.Format.ReplaceOne GHC.Types.Int Data.ByteString.Internal.ByteString
+ Text.Regex.Do.Format: instance Text.Regex.Do.Format.ReplaceOne GHC.Types.Int Data.Text.Internal.Text
+ Text.Regex.Do.Format: instance Text.Regex.Do.Format.ReplaceOne GHC.Types.Int GHC.Base.String
+ Text.Regex.Do.Format: instance Text.Regex.Do.Format.ReplaceOne GHC.Types.Int a => Text.Regex.Do.Format.Format a [a]
+ Text.Regex.Do.Format: instance Text.Regex.Do.Format.ReplaceOne a a => Text.Regex.Do.Format.Format a [(a, a)]
+ Text.Regex.Do.Format: replaceOne :: ReplaceOne idx a => a -> idx -> a -> a
+ Text.Regex.Do.Format: type Formatable a = (Format a [a], Format a [(a, a)])
+ Text.Regex.Do.Trim: instance Text.Regex.Do.Trim.Trim Data.Text.Internal.Text
- Text.Regex.Do.Format: class Format a
+ Text.Regex.Do.Format: class Format a arg
- Text.Regex.Do.Format: format :: Format a => String -> a -> String
+ Text.Regex.Do.Format: format :: Format a arg => a -> arg -> a
Files
- changelog.md +7/−0
- regex-do.cabal +1/−1
- src/Text/Regex/Do/Format.hs +92/−39
- src/Text/Regex/Do/Pcre/Ascii/Match.hs +6/−1
- src/Text/Regex/Do/Pcre/Ascii/MatchHint.hs +4/−1
- src/Text/Regex/Do/Pcre/Ascii/Replace.hs +25/−1
- src/Text/Regex/Do/Pcre/Option.hs +8/−2
- src/Text/Regex/Do/Pcre/Utf8/Match.hs +6/−1
- src/Text/Regex/Do/Pcre/Utf8/MatchHint.hs +5/−1
- src/Text/Regex/Do/Pcre/Utf8/Replace.hs +8/−3
- src/Text/Regex/Do/ReplaceOpen.hs +7/−3
- src/Text/Regex/Do/Trim.hs +7/−1
- src/Text/Regex/Do/Type/MatchHint.hs +2/−2
- test/TestRegex/TestFormat.hs +32/−8
- test/TestRegex/TestReplaceUtf.hs +2/−2
- test/TestRegex/TestTrim.hs +0/−1
changelog.md view
@@ -1,3 +1,10 @@+##### 2.6.1+ compatible with 2.6+ + add Format instances: ByteString, Text + add Trim instance: Text + + ##### 2.6 compatible with 2.5
regex-do.cabal view
@@ -1,5 +1,5 @@ name: regex-do-version: 2.6+version: 2.6.1 synopsis: PCRE wrapper description: format, search, replace (String | ByteString) with PCRE regex. Utf8-safe author: Imants Cekusins
src/Text/Regex/Do/Format.hs view
@@ -1,63 +1,116 @@ module Text.Regex.Do.Format- (Format(..)) where+ (Format(..),+ ReplaceOne(..),+ Formatable) where import Prelude as P import Text.Regex.Do.Type.Do import Text.Regex.Do.Split as S (replace) import Text.Regex.Do.Convert+import Data.ByteString as B+import Data.Text as T -class Format a where- format::String -> a -> String+type Formatable a = (Format a [a], Format a [(a,a)]) +{- | ==== implemented a: -instance Format [String] where+ * 'String'+ * 'ByteString'+ * 'Text' -}++class Format a arg where+ format::a -> arg -> a+++instance ReplaceOne Int a =>+ Format a [a] where format = foldr_idx foldFn_idx--- ^--- === index based--- >>> format "на первое {0}, на второе {0}" ["перловка"]------ "на первое перловка, на второе перловка"------ >>> format "Polly {0} a {1}" ["got","cracker"]------ "Polly got a cracker"---+{- ^ === index based+ >>> format "на первое {0}, на второе {0}" ["перловка"] -foldFn_idx::String -> (Int, String) -> String-foldFn_idx v (i,body1) = replaceOne body1 (show i) v+ "на первое перловка, на второе перловка" + >>> format "Polly {0} a {1}" ["got","cracker"] -instance Format [(String,String)] where+ "Polly got a cracker"+-}++foldFn_idx::ReplaceOne k v =>+ v -> (k, v) -> v+foldFn_idx v0 (i0, body1) = replaceOne body1 i0 v0++++instance ReplaceOne a a =>+ Format a [(a, a)] where format = P.foldr foldFn_map--- ^--- === key based--- key may be {any string}------ >>> format "овчинка {a} не {b}" [("a","выделки"),("b","стоит")]------ "овчинка выделки не стоит"---+{- ^=== key based+ key may be {any a} -foldFn_map:: (String, String) -> String -> String-foldFn_map (k,v) body1 = replaceOne body1 k v+ >>> format "овчинка {a} не {b}" [("a","выделки"),("b","стоит")] -replaceOne::String -> String -> String -> String-replaceOne body k v = toString bs1- where pat1 = Pattern $ toByteString $ "{" ++ k ++ "}"- repl1 = Replacement $ toByteString v- bs1 = S.replace pat1 repl1 $ Body $ toByteString body+ "овчинка выделки не стоит"+-} +foldFn_map::ReplaceOne k v =>+ (k, v) -> v -> v+foldFn_map (k0, v0) body1 = replaceOne body1 k0 v0+++ -- fold with index type CustomerFn a b = (a -> (Int,b) -> b) -foldr_idx :: CustomerFn a b -> b -> [a] -> b-foldr_idx fn init1 list = b1- where i0 = P.length list - 1- (-1,b1) = P.foldr (foldFn fn) (i0,init1) list+foldr_idx::CustomerFn a b -> b -> [a] -> b+foldr_idx fn0 init1 list0 = b1+ where i0 = P.length list0 - 1+ (-1,b1) = P.foldr (foldFn fn0) (i0,init1) list0 -foldFn :: CustomerFn a b -> a -> (Int,b) -> (Int,b)-foldFn fn val t@(i,_)= (i-1,b1)- where b1 = fn val t++foldFn::CustomerFn a b -> a -> (Int, b) -> (Int, b)+foldFn fn0 val0 t0@(i0, _) = (i0 - 1, b1)+ where b1 = fn0 val0 t0+++class ReplaceOne idx a where+ replaceOne::a -> idx -> a -> a+++instance ReplaceOne Int String where+ replaceOne body0 k0 v0 = toString bs1+ where pat1 = Pattern $ toByteString $ "{" ++ (show k0) ++ "}"+ repl1 = Replacement $ toByteString v0+ bs1 = S.replace pat1 repl1 $ Body $ toByteString body0+++instance ReplaceOne String String where+ replaceOne body0 k0 v0 = toString bs1+ where pat1 = Pattern $ toByteString $ "{" ++ k0 ++ "}"+ repl1 = Replacement $ toByteString v0+ bs1 = S.replace pat1 repl1 $ Body $ toByteString body0+++instance ReplaceOne Int ByteString where+ replaceOne body0 k0 v0 = S.replace pat1 repl1 $ Body body0+ where pat1 = Pattern $ toByteString $ "{" ++ (show k0) ++ "}"+ repl1 = Replacement v0+++instance ReplaceOne ByteString ByteString where+ replaceOne body0 k0 v0 = S.replace pat1 repl1 $ Body body0+ where pat1 = Pattern $ B.concat [toByteString "{", k0, toByteString "}"]+ repl1 = Replacement v0++++instance ReplaceOne Int Text where+ replaceOne body0 k0 v0 = T.replace pat1 v0 body0+ where pat1 = T.pack $ "{" ++ (show k0) ++ "}"+++instance ReplaceOne Text Text where+ replaceOne body0 k0 v0 = T.replace pat1 v0 body0+ where pat1 = T.concat [T.pack "{", k0, T.pack "}"]
src/Text/Regex/Do/Pcre/Ascii/Match.hs view
@@ -19,8 +19,12 @@ import Text.Regex.Do.Type.MatchHint -{- | 'match' covers all result types+{- | * a: 'String', 'ByteString', 'Regex'+ * b: 'String', 'ByteString'+ * out: ['String'], [['String']], ['ByteString'], [['ByteString']], 'Bool', ['PosLen'], [['PosLen']] + 'match' covers all result types+ compiler looks up the appropriate function depending on the result type '=~' is borrowed from "Text.Regex.PCRE.Wrap",@@ -34,6 +38,7 @@ match::Pattern a -> Body b -> out +-- | synonym for 'match'. arg without newtypes (=~)::Match a b out => a -- ^ pattern -> b -- ^ body
src/Text/Regex/Do/Pcre/Ascii/MatchHint.hs view
@@ -21,7 +21,10 @@ import Data.ByteString -{- | picks 'M.Match' instance where 'Pattern' and 'Body' are of the same type+{- | * hint: 'Once', 'All', 'Test', 'PosLen'', 'PosLen_'+ * a: 'String', 'ByteString'++ picks 'M.Match' instance where 'Pattern' and 'Body' are of the same type 'Hint' and inferrable 'Pattern' or 'Body' type determine the instance
src/Text/Regex/Do/Pcre/Ascii/Replace.hs view
@@ -17,7 +17,31 @@ import Text.Regex.Do.Type.MatchHint import Text.Regex.Do.Pcre.Option as O -{- | arg overloading -}+{- | 'All' | 'Once' needs to be specified once with either pat, repl or body++ * pat: a: 'String' | 'ByteString' | 'Regex'++ * a+ * ('All' | 'Once' (a))+ * ('All' | 'Once' ('Pattern' a))++ * repl: b: 'String' | 'ByteString'++ * ('Replacement' b)+ * ('GroupReplacer' b)+ * ('All' | 'Once' ('Replacement' b))+ * ('All' | 'Once' ('GroupReplacer' b))++ * body: b: 'String' | 'ByteString'++ * b+ * 'Body' b+ * ('All' | 'Once' (b))++ * out:++ * 'String' | 'ByteString'+ -} class Replace pat repl body out where replace::pat -> repl -> body -> out
src/Text/Regex/Do/Pcre/Option.hs view
@@ -11,7 +11,11 @@ -- | <http://www.pcre.org/pcre.txt pcre man pages> -data Comp = Blank -- ^ 'B.compBlank'+data Comp = Blank {- ^ 'B.compBlank'++ clears default options: extended,caseSensitive,multiline regex+ -}+ | Anchored {- ^ 'B.compAnchored' the pattern is forced to be "anchored", that is, it@@ -85,7 +89,9 @@ deriving (Eq,Ord,Enum) -data Exec = BlankE -- ^ 'B.execBlank'+data Exec = BlankE {- ^ 'B.execBlank'++ clears default options: extended,caseSensitive,multiline regex -} | NotEmpty {- ^ 'B.execNotEmpty' An empty string is not considered to be a valid match if this option is
src/Text/Regex/Do/Pcre/Utf8/Match.hs view
@@ -24,10 +24,15 @@ import Data.ByteString -+{- | * enc: 'Utf8_'+ * a: 'String', 'ByteString', 'Regex'+ * b: 'String', 'ByteString'+ * out: ['String'], [['String']], ['ByteString'], [['ByteString']], 'Bool', ['PosLen'], [['PosLen']]+ -} class Match enc a b out where match::Pattern (enc a) -> Body (enc b) -> out +-- | synonym for 'match'. arg without newtypes (=~)::Match Utf8_ a b out => a -- ^ pattern -> b -- ^ body
src/Text/Regex/Do/Pcre/Utf8/MatchHint.hs view
@@ -12,7 +12,11 @@ import Data.ByteString -{- | picks 'M.Match' instance where 'Pattern' and 'Body' are of the same type+{- | * hint: 'Once', 'All', 'Test', 'PosLen'', 'PosLen_'+ * a: 'String', 'ByteString'+ * enc: 'Utf8_'++ picks 'M.Match' instance where 'Pattern' and 'Body' are of the same type 'Hint' and inferrable 'Pattern' or 'Body' type determine the instance
src/Text/Regex/Do/Pcre/Utf8/Replace.hs view
@@ -18,7 +18,12 @@ import Text.Regex.Do.Convert import Data.ByteString -{- | arg overloading -}+{- | see "Text.Regex.Do.Pcre.Ascii.Replace" for implemented types++ in full typed instance every b is wrapped in 'Utf8_' newtype++ 'GroupReplacer' is implemented only for 'ByteString' -}+ class Replace pat repl body out where replace::pat -> repl -> body -> out @@ -63,7 +68,7 @@ >>> replacer::GroupReplacer (ByteString) replacer = defaultReplacer 1 tweak1 where tweak1 s1- | s1 == toByteString "[команды]" = toByteString "A - B"+ | s1 == toByteString "[команды]" = toByteString "А - Я" | s1 == toByteString "[счёт]" = toByteString "5:0" | s1 == toByteString "[какая боль, ]" = empty | otherwise = traceShow s1 $ toByteString "?"@@ -72,7 +77,7 @@ body1 = toByteString "[какая боль, ][команды] : [счёт]" in replace rx1 (All replacer) body1 - "A - B : 5:0" -}+ "А - Я : 5:0" -} instance (T.Regex b, Replace' Once Utf8_ b repl,
src/Text/Regex/Do/ReplaceOpen.hs view
@@ -108,8 +108,11 @@ where str2 = tweak0 str1 -{- | get group content safely+{- | get group content safely: + * non-existing group idx will not error but return 'Nothing'+ * adjust for previous replacements length+ see 'defaultReplacer' source for use example -} getGroup::R.Extract a =>@@ -121,9 +124,10 @@ val1 = extract pl2 $ acc acc0 -{- | call from your custom 'GroupReplacer' passed to 'replaceGroup'+{- | replace group match while adjusting for previous replacements length - see 'defaultReplacer' source for use example -}+ see 'defaultReplacer' source for use example -}+ replaceMatch::Extract' a => PosLen -- ^ replaceable, unadjusted -> (a, ReplaceAcc a) -- ^ (new val, acc passed to 'GroupReplacer')
src/Text/Regex/Do/Trim.hs view
@@ -3,6 +3,7 @@ import Text.Regex.Do.Type.Do import Data.Char(isSpace) import qualified Data.ByteString as B+import qualified Data.Text as T import Text.Regex.Do.Convert import Text.Regex.Do.Pcre.Ascii.Replace import Text.Regex.Do.Type.MatchHint@@ -24,6 +25,11 @@ in makeRegexOpt p1 [Blank] [] -instance Trim String where+instance Trim String where trim = f . f where f = reverse . dropWhile isSpace+++instance Trim T.Text where+ trim = T.strip+-- ^ see 'T.strip'
src/Text/Regex/Do/Type/MatchHint.hs view
@@ -1,10 +1,10 @@ module Text.Regex.Do.Type.MatchHint where newtype Test a = Test a -- ^ test: does body match pattern?-newtype Once a = Once a -- ^ values+newtype Once a = Once a -- ^ match / replace once deriving (Functor) -newtype All a = All a -- ^ values+newtype All a = All a -- ^ match / replace all deriving (Functor) newtype PosLen' a = PosLen' a -- ^ once
test/TestRegex/TestFormat.hs view
@@ -1,24 +1,48 @@-{-# LANGUAGE NoOverloadedStrings #-} module TestRegex.TestFormat where import Test.Hspec import Text.Regex.Do.Format import Text.Regex.Do.Pad+import Data.String+import Text.Regex.Do.Convert+import Data.ByteString+import Data.Text as T main::IO()-main = hspec $ do+main = do+ oneFormat s show -- String+ oneFormat b $ toByteString . show -- ByteString+ oneFormat t $ T.pack . show -- Text+++oneFormat::(Formatable a, IsString a, Eq a, Show a) =>+ (String -> a) ->+ (Int -> a) ->+ IO()+oneFormat fn0 idx0 = hspec $ do describe "Habase.Bin.Format" $ do it "list arg 0,0 repl []" $- format "на первое {0}, на второе {0}" ([]::[String]) `shouldBe` "на первое {0}, на второе {0}"+ format (fn0 "на первое {0}, на второе {0}") (fn0 <$> []) `shouldBe` (fn0 "на первое {0}, на второе {0}") it "list arg 0,0" $- format "на первое {0}, на второе {0}" ["перловка"] `shouldBe` "на первое перловка, на второе перловка"+ format (fn0 "на первое {0}, на второе {0}") [fn0 "перловка"] `shouldBe` (fn0 "на первое перловка, на второе перловка") it "list arg 0,1" $ do- format "Polly {0} a {1}" ["gets","cracker"] `shouldBe` "Polly gets a cracker"- format "{10} {15} {21}" (show <$> [0..22]) `shouldBe` "10 15 21"- format "{ten} {пятнадцать} {vingt}" [("ten","10"), ("пятнадцать", "15"), ("vingt", "20")] `shouldBe` "10 15 20"+ format (fn0 "Polly {0} a {1}") [fn0 "gets",fn0 "cracker"] `shouldBe` (fn0 "Polly gets a cracker")+ format (fn0 "{10} {15} {21}") (idx0 <$> [0..22]) `shouldBe` (fn0 "10 15 21")+ format (fn0 "{ten} {пятнадцать} {vingt}") [(fn0 "ten", fn0 "10"), (fn0 "пятнадцать", fn0 "15"), (fn0 "vingt", fn0 "20")] `shouldBe` (fn0 "10 15 20") it "map arg" $- format "овчинка {a} не {b}" [("a","выделки"),("b","стоит")] `shouldBe` "овчинка выделки не стоит"+ format (fn0 "овчинка {a} не {b}") [(fn0 "a", fn0 "выделки"),(fn0 "b", fn0 "стоит")] `shouldBe` (fn0 "овчинка выделки не стоит")+ it "pad" $ pad '-' 5 "abc" `shouldBe` "--abc" it "pad'" $ pad' '-' 5 "abc" `shouldBe` "abc--" it "pad" $ pad '-' 3 "abcde" `shouldBe` "abcde"+++s::String -> String+s = id++b::String -> ByteString+b = toByteString++t::String -> Text+t = T.pack
test/TestRegex/TestReplaceUtf.hs view
@@ -66,7 +66,7 @@ groupReplace2_ = hspec $ do describe "TestRegex.TestReplaceUtf" $ do it "боль" $ do- runFn1 `shouldBe` (toByteString "A - B : 5:0")+ runFn1 `shouldBe` (toByteString "А - Я : 5:0") where runFn1 = let rx1 = toByteString "(\\[[^\\]]+\\])" body1 = toByteString "[какая боль, ][команды] : [счёт]"@@ -76,7 +76,7 @@ replacer2_::GroupReplacer (ByteString) replacer2_ = defaultReplacer 1 tweak1 where tweak1 s1- | s1 == toByteString "[команды]" = toByteString "A - B"+ | s1 == toByteString "[команды]" = toByteString "А - Я" | s1 == toByteString "[счёт]" = toByteString "5:0" | s1 == toByteString "[какая боль, ]" = empty | otherwise = traceShow s1 $ toByteString "?"
test/TestRegex/TestTrim.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE OverloadedStrings #-} module TestRegex.TestTrim where import Test.Hspec