pcre-utils (empty) → 0.1.0.0
raw patch · 6 files changed
+276/−0 lines, 6 filesdep +HUnitdep +attoparsecdep +basesetup-changed
Dependencies added: HUnit, attoparsec, base, bytestring, mtl, pcre-utils, regex-pcre-builtin, vector
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- Text/Regex/PCRE/ByteString/Utils.hs +129/−0
- pcre-utils.cabal +45/−0
- test/split.hs +39/−0
- test/subs.hs +31/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2013, Simon Marechal++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Simon Marechal nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Text/Regex/PCRE/ByteString/Utils.hs view
@@ -0,0 +1,129 @@+module Text.Regex.PCRE.ByteString.Utils+ ( substitute+ , split+ , substituteCompile+ , splitCompile+ ) where++import Text.Regex.PCRE.ByteString+import qualified Data.ByteString.Char8 as BS+import Control.Monad.Error+import qualified Data.Vector as V+import Data.Attoparsec.ByteString.Char8+import Control.Applicative++{-| Substitutes values matched by a `Regex`. References can be used.++It doesn't support anything else than global substitution for now ..+-}+substitute :: Regex -- ^ The regular expression, taken from a call to `compile`+ -> BS.ByteString -- ^ The source string+ -> BS.ByteString -- ^ The replacement string+ -> IO (Either String BS.ByteString)+substitute regexp srcstring repla = runErrorT $ do+ let check x = case x of+ Right y -> return y+ Left rr -> throwError rr+ parsedReplacement <- check (parseOnly repparser repla)+ (matches, captures) <- getMatches regexp srcstring V.empty+ let !replaceString = applyCaptures parsedReplacement captures+ applyReplacement :: RegexpSplit BS.ByteString -> BS.ByteString+ applyReplacement (Unmatched x) = x+ applyReplacement (Matched _) = replaceString+ return $! BS.concat $! map applyReplacement matches++-- Transforms the parsed replacement and the vector of captured stuff into+-- the destination ByteString.+applyCaptures :: [Replacement] -> V.Vector BS.ByteString -> BS.ByteString+applyCaptures repl capt = BS.concat (map applyCaptures' repl)+ where+ applyCaptures' :: Replacement -> BS.ByteString+ applyCaptures' (RawReplacement r) = r+ applyCaptures' (IndexedReplacement idx) = if V.length capt < idx+ then ""+ else capt V.! (idx-1)++-- | Splits strings, using a `Regex` as delimiter.+split :: Regex -- ^ The regular expression, taken from a call to `compile`+ -> BS.ByteString -- ^ The source string+ -> IO (Either String [BS.ByteString])+split regexp srcstring = fmap (either Left (Right . removeEmptyLeft . regexpUnmatched . fst)) $ runErrorT (getMatches regexp srcstring V.empty)+ where+ removeEmptyLeft = reverse . dropWhile BS.null . reverse++data RegexpSplit a = Matched a+ | Unmatched a+ deriving (Show, Eq, Ord)++instance Functor RegexpSplit where+ fmap f (Matched x) = Matched (f x)+ fmap f (Unmatched x) = Unmatched (f x)++regexpAll :: [RegexpSplit a] -> [a]+regexpAll = map unreg+ where+ unreg ( Matched x ) = x+ unreg ( Unmatched x ) = x++isMatched :: RegexpSplit a -> Bool+isMatched (Matched _) = True+isMatched _ = False++regexpUnmatched :: [RegexpSplit a] -> [a]+regexpUnmatched = regexpAll . filter (not . isMatched)++getMatches :: Regex -> BS.ByteString -> V.Vector BS.ByteString -> ErrorT String IO ([RegexpSplit BS.ByteString], V.Vector BS.ByteString)+getMatches _ "" curcaptures = return ([], curcaptures)+getMatches creg src curcaptures = do+ x <- liftIO $ regexec creg src+ case x of+ Left (rcode, rerror) -> throwError ("Regexp application error: " ++ rerror ++ "(" ++ show rcode ++ ")")+ Right Nothing -> return ([Unmatched src], curcaptures)++ -- Now this is a trick, I don't know exactly why this happens, but this happens with empty regexps. We are going to cheat here+ Right (Just ("","",rm,_)) -> return (map (Unmatched . BS.singleton) (BS.unpack rm), curcaptures)++ Right (Just (before,current,remaining,captures)) -> do+ (remain, nextcaptures) <- getMatches creg remaining (curcaptures V.++ (V.fromList captures))+ return (Unmatched before : Matched current : remain, nextcaptures)+++data Replacement = RawReplacement BS.ByteString+ | IndexedReplacement Int+ deriving (Show)++repparser :: Parser [Replacement]+repparser = many replacement <* endOfInput++replacement :: Parser Replacement+replacement = fmap RawReplacement rawData <|> escapedThing++rawData :: Parser BS.ByteString+rawData = takeWhile1 (/= '\\')++escapedThing :: Parser Replacement+escapedThing = do+ void (char '\\')+ fmap IndexedReplacement decimal <|> fmap (RawReplacement . BS.cons '\\') rawData+++-- | Compiles the regular expression (using default options) and `substitute`s+substituteCompile :: BS.ByteString -- ^ The regular expression+ -> BS.ByteString -- ^ The source string+ -> BS.ByteString -- ^ The replacement string+ -> IO (Either String BS.ByteString)+substituteCompile regexp srcstring repla = do+ re <- compile compBlank execBlank regexp+ case re of+ Right cre -> substitute cre srcstring repla+ Left rr -> return $ Left $ "Regexp compilation failed: " ++ show rr++-- | Compiles the regular expression (using default options) and `split`s.+splitCompile :: BS.ByteString -- ^ The regular expression+ -> BS.ByteString -- ^ The source string+ -> IO (Either String [BS.ByteString])+splitCompile regexp srcstring = do+ re <- compile compBlank execBlank regexp+ case re of+ Right cre -> split cre srcstring+ Left rr -> return $ Left $ "Regexp compilation failed: " ++ show rr
+ pcre-utils.cabal view
@@ -0,0 +1,45 @@+-- Initial pcre-utils.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: pcre-utils+version: 0.1.0.0+synopsis: Perl-like substitute and split for PCRE regexps.+description: This package introduces split and replace like functions using PCRE regexps.+license: BSD3+license-file: LICENSE+author: Simon Marechal+maintainer: bartavelle@gmail.com+-- copyright: +category: Text+build-type: Simple+cabal-version: >=1.8++source-repository head+ type: git+ location: git://github.com/bartavelle/pcre-utils.git+++library+ exposed-modules: Text.Regex.PCRE.ByteString.Utils+ -- other-modules: + ghc-options: -Wall+ ghc-prof-options: -caf-all -auto-all+ extensions: OverloadedStrings, BangPatterns+ build-depends: base ==4.6.*, regex-pcre-builtin, bytestring, attoparsec, mtl, vector++Test-Suite test-split+ hs-source-dirs: test+ type: exitcode-stdio-1.0+ ghc-options: -Wall+ extensions: OverloadedStrings+ build-depends: base,pcre-utils,HUnit,bytestring, regex-pcre-builtin+ main-is: split.hs++Test-Suite test-subs+ hs-source-dirs: test+ type: exitcode-stdio-1.0+ ghc-options: -Wall+ extensions: OverloadedStrings+ build-depends: base,pcre-utils,HUnit,bytestring, regex-pcre-builtin+ main-is: subs.hs+
+ test/split.hs view
@@ -0,0 +1,39 @@+module Main where++import Test.HUnit+import Text.Regex.PCRE.ByteString+import Text.Regex.PCRE.ByteString.Utils+import qualified Data.ByteString as BS++tests :: [(BS.ByteString, BS.ByteString, [BS.ByteString])]+tests = [ ("a" , "lala" , ["l","l"])+ , ("l" , "lala" , ["", "a", "a"])+ , ("x" , "lala" , ["lala"])+ , ("[la]", "lalaz" , ["", "", "", "", "z"])+ , ("[la]", "lalazoula" , ["", "", "", "", "zou"])+ , ("[la]", "lalazoulaz" , ["", "", "", "", "zou", "", "z"])+ , ("[la]", "lala" , [])+ , ("l" , "l\nl" , ["", "\n"])+ , ("" , "abc" , ["a", "b", "c"])+ , ("(ha)", "lahachac" , ["la","c","c"])+ ]++toTest :: (BS.ByteString, BS.ByteString, [BS.ByteString]) -> IO Test+toTest cas@(regexp, string, result) = do+ let failtest :: String -> IO Test+ failtest rr = return $ TestCase $ assertFailure $ rr ++ " " ++ show cas+ x <- compile compBlank execBlank regexp+ case x of+ Left rr -> failtest ("Failure when compiling regexp: " ++ show rr)+ Right rregexp -> do+ res <- split rregexp string+ case res of+ Right rs -> return (rs ~?= result)+ Left err -> failtest ("Could not split regexp: " ++ err)++main :: IO ()+main = do+ (Counts _ _ e f) <- fmap TestList (mapM toTest tests) >>= runTestTT+ if (e > 0) || (f > 0)+ then error "Tests failed"+ else return ()
+ test/subs.hs view
@@ -0,0 +1,31 @@+module Main where++import Test.HUnit+import Text.Regex.PCRE.ByteString+import Text.Regex.PCRE.ByteString.Utils+import qualified Data.ByteString as BS++tests :: [(BS.ByteString, BS.ByteString, BS.ByteString, BS.ByteString)]+tests = [ ("l", "x", "lap\\nin", "xap\\nin")+ , ("log(\\d+)truc", "x\\1x", "log186truc", "x186x")+ ]++toTest :: (BS.ByteString, BS.ByteString, BS.ByteString, BS.ByteString) -> IO Test+toTest cas@(regexp, replacement, string, result) = do+ let failtest :: String -> IO Test+ failtest rr = return $ TestCase $ assertFailure $ rr ++ " " ++ show cas+ x <- compile compBlank execBlank regexp+ case x of+ Left rr -> failtest ("Failure when compiling regexp: " ++ show rr)+ Right rregexp -> do+ res <- substitute rregexp string replacement+ case res of+ Right rs -> return (rs ~?= result)+ Left err -> failtest ("Could not split regexp: " ++ err)++main :: IO ()+main = do+ (Counts _ _ e f) <- fmap TestList (mapM toTest tests) >>= runTestTT+ if (e > 0) || (f > 0)+ then error "Tests failed"+ else return ()