regex-do 1.0 → 1.1
raw patch · 10 files changed
+198/−169 lines, 10 filesdep ~QuickCheckdep ~arraydep ~base
Dependency ranges changed: QuickCheck, array, base, bytestring, hspec, regex-base, regex-pcre, stringsearch
Files
- regex-do.cabal +21/−15
- src/Regexdo/Pcre/Match.hs +7/−3
- src/Regexdo/Pcre/Replace.hs +135/−129
- src/Regexdo/Search.hs +2/−2
- src/Regexdo/Trim.hs +1/−2
- src/Regexdo/TypeDo.hs +8/−1
- test/TestRegex/Format.hs +2/−0
- test/TestRegex/Pcre.hs +2/−1
- test/TestRegex/Replace.hs +16/−12
- test/TestRegex/StringSearch.hs +4/−4
regex-do.cabal view
@@ -1,5 +1,5 @@ name: regex-do-version: 1.0+version: 1.1 synopsis: PCRE regex funs description: Convenience functions to search, replace, format String | ByteString with PCRE regex author: Imants Cekusins@@ -24,14 +24,16 @@ Regexdo.TypeRegex Regexdo.Convert + other-modules:+ ghc-options: -fwarn-unused-imports - build-depends: base >=4.8 && <4.9,- regex-base >= 0.93.2,- regex-pcre >= 0.94.4,- stringsearch >= 0.3.6.6,- bytestring >= 0.10.6.0,- array >= 0.5.1.0,+ build-depends: base <= 5.0,+ regex-base,+ regex-pcre,+ stringsearch,+ bytestring,+ array, text @@ -44,6 +46,8 @@ OverloadedStrings FlexibleContexts ConstraintKinds+ ScopedTypeVariables+ DeriveFunctor test-suite spec@@ -58,6 +62,8 @@ OverloadedStrings FlexibleContexts ConstraintKinds+ ScopedTypeVariables+ DeriveFunctor main-is: Main.hs@@ -68,13 +74,13 @@ TestRegex.StringSearch TestRegex.TestTrim - build-depends: base >= 4.8,- hspec >= 2.1.7,- QuickCheck >= 2.8.1,- regex-base >= 0.93.2,- regex-pcre >= 0.94.4,- stringsearch >= 0.3.6.6,- bytestring >= 0.10.6.0,- array >= 0.5.1.0,+ build-depends: base <= 5.0,+ hspec,+ QuickCheck,+ regex-base,+ regex-pcre,+ stringsearch,+ bytestring,+ array, text, regex-do
src/Regexdo/Pcre/Match.hs view
@@ -11,10 +11,11 @@ see "Regexdo.Pcre.Result" for funs converting 'MatchArray' to something useful - 'match' returns the first occurence - if any+ 'match' returns the first occurrence - if any -} + class Match_ctr n h => Match_cl n h where @@ -28,6 +29,8 @@ matchAll r0 (Haystack b0) = R.matchAll (r_ r0) b0 ++ -- | tweak Regex with options makeRegexOpts::Match_opt n => [O.Comp] -> [O.Exec] -> Needle n -> Regex@@ -58,8 +61,6 @@ instance Match_cl Regex ByteString -class Needle_ r where- r_::Needle r -> Regex instance Needle_ ByteString where r_ (Needle r0) = R.makeRegex r0@@ -74,3 +75,6 @@ -- | _ctr: constraint type Match_ctr n h = (R.Extract h, Needle_ n, R.RegexLike Regex h) type Match_opt n = R.RegexMaker Regex CompOption ExecOption n++class Needle_ r where+ r_::Needle r -> Regex
src/Regexdo/Pcre/Replace.hs view
@@ -1,34 +1,129 @@ module Regexdo.Pcre.Replace( ReplaceCase(..), Replace_cl(..),- replaceGroup,- replaceMatch+ replaceMatch,+ defaultReplacer,+ Mr ) where +import Data.Array+import Data.ByteString+import Prelude as P import qualified Data.ByteString as B+import qualified Regexdo.Pcre.Option as O import qualified Text.Regex.Base.RegexLike as R--import Regexdo.TypeDo-import Regexdo.TypeRegex+import Regexdo.Convert import Regexdo.Pcre.Match import Regexdo.Pcre.Result-import qualified Regexdo.Pcre.Option as O-import Regexdo.Convert+import Regexdo.TypeDo+import Regexdo.TypeRegex -data ReplaceCase = Once | All | Utf8 | Multiline deriving Eq -type Mm a = (Match_cl Regex a, Match_opt a)-type Rm a = (Replace_cl' a, Match_cl Regex a)+type Mr a = (Match_cl Regex a, Replace_cl' a, Match_opt a) -addOpt::Match_opt a =>- Needle a -> [O.Comp] -> Needle Regex-addOpt pat1 opt1 = Needle rx1- where rx1 = makeRegexOpts opt1 [] pat1+class Replace_cl' a where+ prefix::PosLen -> a -> a+ suffix::PosLen -> a -> a+ concat'::[a] -> a+ toByteString'::a -> ByteString+ toA::ByteString -> a +class Replace_cl a where+ replace::Mr a =>+ [ReplaceCase] -> (Needle a, Replacement a) -> Haystack a -> a+ replace cases0 (pat1,repl1) hay0 =+ if isUtf8 cases0 then utfFn2+ else fn2 (pat2 pat1,repl1) hay0+ where fn1 = if P.elem All cases0 then rall else ronce+ fn2 = if P.elem All cases0 then rall else ronce+ utfFn2 = let res1 = fn1 (pat2 $ toByteString' <$> pat1, toByteString' <$> repl1) $ toByteString' <$> hay0+ in toA res1+ pat2 pat1 = addOpt pat1 cOpt1+ cOpt1 = comp cases0 -ronce::Rm a =>++ replaceGroup::Mr a =>+ [ReplaceCase] -> (Needle a, GroupReplacer a) -> Haystack a -> a+ replaceGroup cases (pat1,repl1) = fn1 (pat2,repl1)+ where pat2 = addOpt pat1 cOpt+ cOpt = comp cases+ fn1 = if P.elem All cases+ then rallGroup+ else ronceGroup++{- ^+ == dynamic group replace+ custom replacer fn returns replacement value++ >>> replacer::GroupReplacer String+ replacer = defaultReplacer 3 tweak1+ where tweak1 str1 = case str1 of+ "101" -> "[A]"+ "3" -> "[Be]"+++ 'Once' vs 'All' options++ >>> replaceGroup [Once] (Needle "(\\w)(=)(\\d{1,3})", replacer) $ Haystack "a=101 b=3 12"++ "a=[A] b=3 12"++ >>> replaceGroup [All] (Needle "(\\w)(=)(\\d{1,3})", replacer) $ Haystack "a=101 b=3 12"++ "a=[A] b=[Be] 12"+++ == static replace for simple (no group) needle++ >>> replace [Once,Utf8] (Needle "менее", Replacement "более") (Haystack "менее менее")++ "более менее"++ >>> replace [Once,Utf8] (Needle "^a\\s", Replacement "A") (Haystack "a bc хол.гор.")++ "Abc хол.гор." -}++instance Replace_cl String++instance Replace_cl' String where+ prefix pl1 = P.take $ fst pl1+ suffix pl1 = P.drop (pos1 + len1)+ where pos1 = fst pl1+ len1 = snd pl1+ concat' = P.concat+ toByteString' = toByteString+ toA = toString+++instance Replace_cl B.ByteString++instance Replace_cl' B.ByteString where+ prefix pl1 = B.take $ fst pl1+ suffix pl1 = B.drop (pos1 + len1)+ where pos1 = fst pl1+ len1 = snd pl1+ concat' = B.concat+ toByteString' = id+ toA = id+++-- | use in your custom 'GroupReplacer' passed to 'replaceGroup'+--+-- see example replacer above or use 'defaultReplacer'+--+replaceMatch::Replace_cl' a =>+ (R.MatchOffset, R.MatchLength) ->+ (a, a) -- ^ (new val, acc passed to 'GroupReplacer')+ -> a -- ^ new acc+replaceMatch pl1 (repl1, bs_str1) =+ concat' [prefix1,repl1,suffix1]+ where prefix1 = prefix pl1 bs_str1+ suffix1 = suffix pl1 bs_str1++-- static+ronce::Mr a => (Needle Regex, Replacement a) -> Haystack a -> a ronce (pat1, Replacement repl1) body1@(Haystack bs_str1) = let pl2 = do@@ -39,15 +134,14 @@ Just pl_arr -> firstGroup pl_arr (repl1,bs_str1) -rall::Rm a =>+rall::Mr a => (Needle Regex, Replacement a) -> Haystack a -> a rall (pat1, Replacement repl1) body1@(Haystack bs_str1) = let pl_arr_arr1 = do let m1 = matchAll pat1 body1 poslen m1 folderFn pl_arr acc1 = firstGroup pl_arr (repl1,acc1)- in foldr folderFn bs_str1 pl_arr_arr1-+ in P.foldr folderFn bs_str1 pl_arr_arr1 firstGroup::Replace_cl' a =>@@ -55,55 +149,22 @@ firstGroup (pl1:_) r1@(repl1,bs_str1) = replaceMatch pl1 r1 --- | use in your custom 'GroupReplacer' passed to 'replaceGroup'------ see example replacer above----replaceMatch::Replace_cl' a =>- (R.MatchOffset, R.MatchLength) ->- (a, a) -- ^ (new val, acc passed to 'GroupReplacer')- -> a-replaceMatch pl1 (repl1, bs_str1) =- concat' [prefix1,repl1,suffix1]- where prefix1 = prefix pl1 bs_str1- suffix1 = suffix pl1 bs_str1---{- | == dynamic group replace- custom replacer fn returns replacement value-- >>> replacer::GroupReplacer String- replacer marr1 acc1 = case val1 of- "101" -> fn1 "[A]"- "3" -> fn1 "[Be]"- where ol1 = marr1 ! 3 :: (MatchOffset, MatchLength)- val1 = extract ol1 acc1- fn1 str1 = replaceMatch ol1 (str1,acc1)-- see 'extract'-- below test compares 'Once' vs 'All' options+-- dynamic+{- | replace with a tweak to specified (by idx) group match - >>> groupReplace::IO()- groupReplace = hspec $ do- describe "Pcre.Replace group" $ do- it "Once" $ do- runFn1 [Once] `shouldBe` "a=[A] b=3 12"- it "All" $ do- runFn1 [All] `shouldBe` "a=[A] b=[Be] 12"- where runFn1 opts1 =- let rx1 = Needle "(\\w)(=)(\\d{1,3})"- body1 = Haystack "a=101 b=3 12"- in replaceGroup opts1 (rx1,replacer) body1--}-replaceGroup::Mm a =>- [ReplaceCase]->(Needle a,GroupReplacer a) -> Haystack a -> a-replaceGroup cases (pat1,repl1) = fn1 (pat2,repl1)- where pat2 = addOpt pat1 cOpt- cOpt = comp cases- fn1 = if elem All cases- then rallGroup- else ronceGroup+ see 'defaultReplacer' source for hints: how to write custom replacer+ -}+defaultReplacer::(Replace_cl' a, R.Extract a) =>+ Int -- ^ idx of match within a group+ -> (a -> a) -- ^ (group match -> replacement) tweak+ -> GroupReplacer a+defaultReplacer idx0 tweak0 (ma0::MatchArray) acc0 =+ if idx0 >= P.length ma0 then acc0 -- safety catch+ else fn1 val1+ where poslen1 = ma0 ! idx0 :: (R.MatchOffset, R.MatchLength)+ val1 = extract poslen1 acc0+ fn1 str1 = replaceMatch poslen1 (str2,acc0)+ where str2 = tweak0 str1 ronceGroup::Match_cl Regex a =>@@ -119,77 +180,22 @@ (Needle Regex, GroupReplacer a) -> Haystack a -> a rallGroup (pat1, repl1) body1@(Haystack bs_str1) = let marrList1 = matchAll pat1 body1- in foldr repl1 bs_str1 marrList1----class Replace_cl a where- replace::[ReplaceCase] -> (Needle a, Replacement a) -> Haystack a -> a---class Replace_cl' a where- prefix::PosLen -> a -> a- suffix::PosLen -> a -> a- concat'::[a] -> a---{- |- >>> replace [Once,Utf8] (Needle "поп", Replacement "крестьянин") (Haystack "у попа была собака")-- "у крестьянина была собака"-- >>> replace [Once,Utf8] (Needle "^a\\s", Replacement "A") (Haystack "a bc хол.гор.")-- "Abc хол.гор."-- -}--instance Replace_cl String where- replace::[ReplaceCase] -> (Needle String, Replacement String) -> Haystack String -> String- replace cases0 (pat1,repl1) hay0 = if isUtf8 cases0 then- let res1 = fn1 (pat2 $ toByteString <$> pat1, toByteString <$> repl1) $ toByteString <$> hay0- in toString res1- else fn2 (pat2 pat1,repl1) hay0- where pat2 pat1 = addOpt pat1 cOpt- cOpt = comp cases0- fn1 = if elem All cases0 then rall else ronce- fn2 = if elem All cases0 then rall else ronce---instance Replace_cl' String where- prefix pl1 = take $ fst pl1- suffix pl1 = drop (pos1 + len1)- where pos1 = fst pl1- len1 = snd pl1- concat' = concat+ in P.foldr repl1 bs_str1 marrList1 -instance Replace_cl B.ByteString where- replace::[ReplaceCase] -> (Needle B.ByteString, Replacement B.ByteString) ->- Haystack B.ByteString -> B.ByteString- replace cases0 (pat1,repl1) hay0 = fn1 (pat2,repl1) hay0- where pat2 = addOpt pat1 cOpt- cOpt = comp cases0- fn1 = if elem All cases0- then rall- else ronce---instance Replace_cl' B.ByteString where- prefix pl1 = B.take $ fst pl1- suffix pl1 = B.drop (pos1 + len1)- where pos1 = fst pl1- len1 = snd pl1- concat' = B.concat+addOpt::Match_opt a =>+ Needle a -> [O.Comp] -> Needle Regex+addOpt pat1 opt1 = Needle rx1+ where rx1 = makeRegexOpts opt1 [] pat1 comp::[ReplaceCase]-> [O.Comp]-comp = map mapFn . filter filterFn- where filterFn o1 = o1 `elem` [Utf8,Multiline]+comp = P.map mapFn . P.filter filterFn+ where filterFn o1 = o1 `P.elem` [Utf8,Multiline] mapFn Utf8 = O.Utf8 mapFn Multiline = O.Multiline isUtf8::[ReplaceCase] -> Bool-isUtf8 case0 = Utf8 `elem` case0+isUtf8 case0 = Utf8 `P.elem` case0
src/Regexdo/Search.hs view
@@ -14,7 +14,7 @@ splitFront) where import qualified Data.ByteString.Search as S -import Regexdo.TypeDo+import Regexdo.TypeDo hiding (replace) import Data.ByteString as B hiding (break, breakEnd, split) import qualified Data.ByteString.Lazy as L import Prelude hiding (break)@@ -23,7 +23,7 @@ -- front: a \nb -- end: a\n b -{- | lose needle+{- | (front, end) : drop needle >>> break (Needle ":") (Haystack "0123:oid:90")
src/Regexdo/Trim.hs view
@@ -1,11 +1,10 @@ module Regexdo.Trim where -import Regexdo.Pcre.Replace(replace,ReplaceCase(All)) import Regexdo.TypeDo import Data.Char(isSpace) import qualified Data.ByteString as B import Regexdo.Convert-+import Regexdo.Pcre.Replace {- | removes leading and trailing spaces and tabs -}
src/Regexdo/TypeDo.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE DeriveFunctor #-} module Regexdo.TypeDo where import Text.Regex.Base.RegexLike@@ -13,3 +12,11 @@ data Replacement r = Replacement r deriving (Functor) -- Bs, String type PosLen = (MatchOffset, MatchLength)+++data ReplaceCase = Once -- ^ may be omitted+ | All -- ^ if both Once and All are passed, All prevails+ | Utf8+ | Multiline deriving Eq++
test/TestRegex/Format.hs view
@@ -8,6 +8,8 @@ main::IO() main = hspec $ do describe "Habase.Bin.Format" $ do+ it "list arg 0,0 repl []" $ do+ format "на первое {0}, на второе {0}" ([]::[String]) `shouldBe` "на первое {0}, на второе {0}" it "list arg 0,0" $ do format "на первое {0}, на второе {0}" ["перловка"] `shouldBe` "на первое перловка, на второе перловка" it "list arg 0,1" $ do
test/TestRegex/Pcre.hs view
@@ -3,9 +3,10 @@ import Test.Hspec import Regexdo.TypeDo-import qualified Regexdo.Pcre.Match as M+import Regexdo.Pcre.Match as M import qualified Regexdo.Pcre.Result as R import Regexdo.Convert+ main::IO()
test/TestRegex/Replace.hs view
@@ -2,13 +2,11 @@ module TestRegex.Replace where import Test.Hspec-import Data.Array((!)) import Regexdo.TypeDo import Regexdo.Pcre.Replace import Regexdo.Convert import Data.ByteString as B-import Text.Regex.Base.RegexLike main::IO()@@ -22,9 +20,18 @@ doc::IO() doc = hspec $ do describe "Pcre.Replace doc" $ do- it "replace" $ do- replace [Once,Utf8] (Needle "поп", Replacement "крестьянин") (Haystack "у попа была собака") `shouldBe` "у крестьянина была собака"-+ it "replaceGroup 1" $ do+ replaceGroup [Once] (Needle "(\\w)(=)(\\d{1,3})", replacer) (Haystack "a=101 b=3 12")+ `shouldBe` "a=[A] b=3 12"+ it "replaceGroup 2" $ do+ replaceGroup [All] (Needle "(\\w)(=)(\\d{1,3})", replacer) (Haystack "a=101 b=3 12")+ `shouldBe` "a=[A] b=[Be] 12"+ it "replace 3" $ do+ replace [Once,Utf8] (Needle "менее", Replacement "более") (Haystack "менее менее")+ `shouldBe` "более менее"+ it "replace 4" $ do+ replace [Once,Utf8] (Needle "^a\\s", Replacement "A") (Haystack "a bc хол.гор.")+ `shouldBe` "Abc хол.гор." onceUtf8::IO()@@ -73,10 +80,7 @@ replacer::GroupReplacer String-replacer marr1 acc1 = case val1 of- "101" -> fn1 "[A]"- "3" -> fn1 "[Be]"- where ol1 = marr1 ! 3 :: (MatchOffset, MatchLength)- val1 = extract ol1 acc1--- !val2 = trace (show val1) val1- fn1 str1 = replaceMatch ol1 (str1,acc1)+replacer = defaultReplacer 3 tweak1+ where tweak1 str1 = case str1 of+ "101" -> "[A]"+ "3" -> "[Be]"
test/TestRegex/StringSearch.hs view
@@ -4,7 +4,7 @@ import Test.Hspec import Control.Exception (evaluate) import Regexdo.TypeDo-import Regexdo.Search+import Regexdo.Search as S import qualified Data.ByteString as B import Regexdo.Convert @@ -21,7 +21,7 @@ it "break end" $ do breakEnd (Needle "\n") (Haystack "a\nbc\nde") `shouldBe` ("a\n", "bc\nde") it "replace" $ do- replace (Needle "\n") (Replacement ",") (Haystack "a\nbc\nde") `shouldBe` "a,bc,de"+ S.replace (Needle "\n") (Replacement ",") (Haystack "a\nbc\nde") `shouldBe` "a,bc,de" it "split" $ do split (Needle "\n") (Haystack "a\nbc\nde") `shouldBe` ["a", "bc", "de"] it "split_sp" $ do@@ -42,7 +42,7 @@ it "break end" $ do evaluate (errFn breakEnd) `shouldThrow` anyException it "replace" $ do- evaluate (replace (Needle B.empty) with body) `shouldThrow` anyException+ evaluate (S.replace (Needle B.empty) with body) `shouldThrow` anyException it "split" $ do evaluate (errFn split) `shouldThrow` anyException it "split end" $ do@@ -62,7 +62,7 @@ break1 = break pat body breakFront1 = breakFront pat body breakEnd1 = breakEnd pat body- replace1 = replace pat with body+ replace1 = S.replace pat with body split1 = split pat body split_sp1 = split pat_sp body_sp splitFront1 = splitFront pat body