pcre-heavy 0.2.5 → 1.0.0
raw patch · 3 files changed
+59/−62 lines, 3 filesdep +string-conversionsdep −stringable
Dependencies added: string-conversions
Dependencies removed: stringable
Files
- README.md +5/−4
- library/Text/Regex/PCRE/Heavy.hs +52/−55
- pcre-heavy.cabal +2/−3
README.md view
@@ -2,19 +2,20 @@ *Finally!* A Haskell regular expressions library that does not suck. -- based on [pcre-light], none of that regex-compat-pluggable-backend stuff-- takes and returns [Stringables] everywhere, use ANY STRING TYPE (String, ByteString, LByteString, Text, LText, FilePath) -- but you need a bit more type annotations than usual+- based on [pcre-light], none of that regex-base complicated pluggable-backend stuff+- takes and returns [ConvertibleStrings] everywhere, use ANY STRING TYPE (String, ByteString, Lazy ByteString, Text, Lazy Text) -- but you need a bit more type annotations (or [ClassyPrelude]'s `asText`, `asString`, etc.) if you use `OverloadedStrings` which you probably can't live without - a QuasiQuoter for regexps that does compile time checking (BTW, [vim2hs] has correct syntax highlighting for that!) - **SEARCHES FOR MULTIPLE MATCHES! DOES REPLACEMENT!** [pcre-light]: https://hackage.haskell.org/package/pcre-light-[Stringables]: https://hackage.haskell.org/package/stringable+[ConvertibleStrings]: https://hackage.haskell.org/package/string-conversions+[ClassyPrelude]: https://hackage.haskell.org/package/classy-prelude [vim2hs]: https://github.com/dag/vim2hs#quasi-quoting ## Usage ```haskell-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE QuasiQuotes, FlexibleContexts #-} import Text.Regex.PCRE.Heavy ```
library/Text/Regex/PCRE/Heavy.hs view
@@ -1,9 +1,7 @@ {-# OPTIONS_GHC -fno-warn-orphans -fno-warn-unused-binds #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE FlexibleInstances, BangPatterns #-}-{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}+{-# LANGUAGE UndecidableInstances, FlexibleInstances, FlexibleContexts, BangPatterns #-}+{-# LANGUAGE TemplateHaskell, QuasiQuotes, UnicodeSyntax, CPP #-} {-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE UnicodeSyntax, CPP #-} -- | A usable regular expressions library on top of pcre-light. module Text.Regex.PCRE.Heavy (@@ -34,7 +32,7 @@ , rawSub ) where -#if __GLASGOW_HASKELL__ < 709+#if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>)) #endif import Language.Haskell.TH hiding (match)@@ -44,33 +42,32 @@ import Text.Regex.PCRE.Light.Base import Data.Maybe (isJust, fromMaybe) import Data.List (unfoldr, mapAccumL)-import Data.Stringable+import Data.String.Conversions import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Internal as BS import System.IO.Unsafe (unsafePerformIO) import Foreign (withForeignPtr, allocaBytes, nullPtr, plusPtr, peekElemOff)-import Debug.Trace -substr ∷ BS.ByteString → (Int, Int) → BS.ByteString+substr ∷ SBS → (Int, Int) → SBS substr s (f, t) = BS.take (t - f) . BS.drop f $ s behead ∷ [a] → (a, [a]) behead (h:t) = (h, t) behead [] = error "no head to behead" -reMatch ∷ Stringable a ⇒ Regex → a → Bool-reMatch r s = isJust $ PCRE.match r (toByteString s) []+reMatch ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ Regex → a → Bool+reMatch r s = isJust $ PCRE.match r (cs s) [] -- | Checks whether a string matches a regex. -- -- >>> :set -XQuasiQuotes+-- >>> :set -XFlexibleContexts -- >>> "https://unrelenting.technology" =~ [re|^http.*|] -- True-(=~) ∷ Stringable a ⇒ a → Regex → Bool+(=~), (≈) ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ a → Regex → Bool (=~) = flip reMatch -- | Same as =~.-(≈) ∷ Stringable a ⇒ a → Regex → Bool (≈) = (=~) -- | Does raw PCRE matching (you probably shouldn't use this directly).@@ -82,7 +79,7 @@ -- Just [(7,9)] -- >>> rawMatch [re|(\w)(\w)|] "a a ab abc ba" 0 [] -- Just [(4,6),(4,5),(5,6)]-rawMatch ∷ Regex → BS.ByteString → Int → [PCREExecOption] → Maybe [(Int, Int)]+rawMatch ∷ Regex → SBS → Int → [PCREExecOption] → Maybe [(Int, Int)] rawMatch r@(Regex pcreFp _) s offset opts = unsafePerformIO $ do withForeignPtr pcreFp $ \pcrePtr → do let nCapt = PCRE.captureCount r@@ -103,7 +100,7 @@ loop (n + 1) (o + 2) ((fromIntegral i, fromIntegral j) : acc) in loop 0 0 [] -nextMatch ∷ Regex → [PCREExecOption] → BS.ByteString → Int → Maybe ([(Int, Int)], Int)+nextMatch ∷ Regex → [PCREExecOption] → SBS → Int → Maybe ([(Int, Int)], Int) nextMatch r opts str offset = case rawMatch r str offset opts of Nothing → Nothing@@ -112,53 +109,53 @@ -- | Searches the string for all matches of a given regex. ----- >>> scan [re|\s*entry (\d+) (\w+)\s*&?|] " entry 1 hello &entry 2 hi"+-- >>> scan [re|\s*entry (\d+) (\w+)\s*&?|] (" entry 1 hello &entry 2 hi" :: String) -- [(" entry 1 hello &",["1","hello"]),("entry 2 hi",["2","hi"])] -- -- It is lazy! If you only need the first match, just apply 'head' (or -- 'headMay' from the "safe" library) -- no extra work will be performed! ----- >>> head $ scan [re|\s*entry (\d+) (\w+)\s*&?|] " entry 1 hello &entry 2 hi"+-- >>> head $ scan [re|\s*entry (\d+) (\w+)\s*&?|] (" entry 1 hello &entry 2 hi" :: String) -- (" entry 1 hello &",["1","hello"])-scan ∷ (Stringable a) ⇒ Regex → a → [(a, [a])]+scan ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ Regex → a → [(a, [a])] scan r s = scanO r [] s -- | Exactly like 'scan', but passes runtime options to PCRE.-scanO ∷ (Stringable a) ⇒ Regex → [PCREExecOption] → a → [(a, [a])]-scanO r opts s = map behead $ map (fromByteString . substr str) <$> unfoldr (nextMatch r opts str) 0- where str = toByteString s+scanO ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ Regex → [PCREExecOption] → a → [(a, [a])]+scanO r opts s = map behead $ map (cs . substr str) <$> unfoldr (nextMatch r opts str) 0+ where str = cs s -- | Searches the string for all matches of a given regex, like 'scan', but -- returns positions inside of the string. ----- >>> scanRanges [re|\s*entry (\d+) (\w+)\s*&?|] " entry 1 hello &entry 2 hi"+-- >>> scanRanges [re|\s*entry (\d+) (\w+)\s*&?|] (" entry 1 hello &entry 2 hi" :: String) -- [((0,17),[(7,8),(9,14)]),((17,27),[(23,24),(25,27)])] -- -- And just like 'scan', it's lazy.-scanRanges ∷ (Stringable a) ⇒ Regex → a → [((Int, Int), [(Int, Int)])]+scanRanges ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ Regex → a → [((Int, Int), [(Int, Int)])] scanRanges r s = scanRangesO r [] s -- | Exactly like 'scanRanges', but passes runtime options to PCRE.-scanRangesO ∷ Stringable a ⇒ Regex → [PCREExecOption] → a → [((Int, Int), [(Int, Int)])]+scanRangesO ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ Regex → [PCREExecOption] → a → [((Int, Int), [(Int, Int)])] scanRangesO r opts s = map behead $ unfoldr (nextMatch r opts str) 0- where str = toByteString s+ where str = cs s class RegexReplacement a where- performReplacement ∷ BS.ByteString → [BS.ByteString] → a → BS.ByteString+ performReplacement ∷ SBS → [SBS] → a → SBS -instance {-# OVERLAPPABLE #-} Stringable a ⇒ RegexReplacement a where- performReplacement _ _ to = toByteString to+instance {-# OVERLAPPABLE #-} ConvertibleStrings a SBS ⇒ RegexReplacement a where+ performReplacement _ _ to = cs to -instance Stringable a ⇒ RegexReplacement (a → [a] → a) where- performReplacement from groups replacer = toByteString $ replacer (fromByteString from) (map fromByteString groups)+instance (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ RegexReplacement (a → [a] → a) where+ performReplacement from groups replacer = cs $ replacer (cs from) (map cs groups) -instance Stringable a ⇒ RegexReplacement (a → a) where- performReplacement from _ replacer = toByteString $ replacer (fromByteString from)+instance (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ RegexReplacement (a → a) where+ performReplacement from _ replacer = cs $ replacer (cs from) -instance Stringable a ⇒ RegexReplacement ([a] → a) where- performReplacement _ groups replacer = toByteString $ replacer (map fromByteString groups)+instance (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ RegexReplacement ([a] → a) where+ performReplacement _ groups replacer = cs $ replacer (map cs groups) -rawSub ∷ RegexReplacement r ⇒ Regex → r → BS.ByteString → Int → [PCREExecOption] → Maybe (BS.ByteString, Int)+rawSub ∷ RegexReplacement r ⇒ Regex → r → SBS → Int → [PCREExecOption] → Maybe (SBS, Int) rawSub r t s offset opts = case rawMatch r s offset opts of Just ((begin, end):groups) →@@ -176,22 +173,22 @@ -- >>> sub [re|a|] "b" "c" :: String -- "c" ----- >>> sub [re|bad|] "xxxbad" "this is bad, right?"+-- >>> sub [re|bad|] "xxxbad" "this is bad, right?" :: String -- "this is xxxbad, right?" -- -- You can use functions!--- A function of Stringable gets the full match.--- A function of [Stringable] gets the groups.--- A function of Stringable → [Stringable] gets both.+-- A function of ConvertibleStrings SBS gets the full match.+-- A function of [ConvertibleStrings SBS] gets the groups.+-- A function of ConvertibleStrings SBS → [ConvertibleStrings SBS] gets both. -- -- >>> sub [re|%(\d+)(\w+)|] (\(d:w:_) -> "{" ++ d ++ " of " ++ w ++ "}" :: String) "Hello, %20thing" :: String -- "Hello, {20 of thing}"-sub ∷ (Stringable a, RegexReplacement r) ⇒ Regex → r → a → a+sub ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS, RegexReplacement r) ⇒ Regex → r → a → a sub r t s = subO r [] t s -- | Exactly like 'sub', but passes runtime options to PCRE.-subO ∷ (Stringable a, RegexReplacement r) ⇒ Regex → [PCREExecOption] → r → a → a-subO r opts t s = fromMaybe s $ fromByteString <$> fst <$> rawSub r t (toByteString s) 0 opts+subO ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS, RegexReplacement r) ⇒ Regex → [PCREExecOption] → r → a → a+subO r opts t s = fromMaybe s $ cs <$> fst <$> rawSub r t (cs s) 0 opts -- | Replaces all occurences of a given regex. --@@ -204,18 +201,18 @@ -- "Hello, world" -- -- https://github.com/myfreeweb/pcre-heavy/issues/2--- >>> gsub [re|good|] "bad" "goodgoodgood"+-- >>> gsub [re|good|] "bad" "goodgoodgood" :: String -- "badbadbad" ----- >>> gsub [re|bad|] "xxxbad" "this is bad, right? bad"+-- >>> gsub [re|bad|] "xxxbad" "this is bad, right? bad" :: String -- "this is xxxbad, right? xxxbad"-gsub ∷ (Stringable a, RegexReplacement r) ⇒ Regex → r → a → a+gsub ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS, RegexReplacement r) ⇒ Regex → r → a → a gsub r t s = gsubO r [] t s -- | Exactly like 'gsub', but passes runtime options to PCRE.-gsubO ∷ (Stringable a, RegexReplacement r) ⇒ Regex → [PCREExecOption] → r → a → a-gsubO r opts t s = fromByteString $ loop 0 str- where str = toByteString s+gsubO ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS, RegexReplacement r) ⇒ Regex → [PCREExecOption] → r → a → a+gsubO r opts t s = cs $ loop 0 str+ where str = cs s loop offset acc = case rawSub r t acc offset opts of Just (result, newOffset) →@@ -226,30 +223,30 @@ -- -- Is lazy. ----- >>> split [re|%(begin|next|end)%|] "%begin%hello%next%world%end%"+-- >>> split [re|%(begin|next|end)%|] ("%begin%hello%next%world%end%" :: String) -- ["","hello","world",""] ----- >>> split [re|%(begin|next|end)%|] ""+-- >>> split [re|%(begin|next|end)%|] ("" :: String) -- [""]-split ∷ Stringable a ⇒ Regex → a → [a]+split ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ Regex → a → [a] split r s = splitO r [] s -- | Exactly like 'split', but passes runtime options to PCRE.-splitO ∷ Stringable a ⇒ Regex → [PCREExecOption] → a → [a]-splitO r opts s = map fromByteString $ map' (substr str) partRanges+splitO ∷ (ConvertibleStrings SBS a, ConvertibleStrings a SBS) ⇒ Regex → [PCREExecOption] → a → [a]+splitO r opts s = map cs $ map' (substr str) partRanges where map' f = foldr ((:) . f) [f (lastL, BS.length str)] -- avoiding the snoc operation (lastL, partRanges) = mapAccumL invRange 0 ranges invRange acc (xl, xr) = (xr, (acc, xl)) ranges = map fst $ scanRangesO r opts str- str = toByteString s+ str = cs s instance Lift PCREOption where -- well, the constructor isn't exported, but at least it implements Read/Show :D lift o = let o' = show o in [| read o' ∷ PCREOption |] quoteExpRegex ∷ [PCREOption] → String → ExpQ-quoteExpRegex opts txt = [| PCRE.compile (toByteString (txt ∷ String)) opts |]- where !_ = PCRE.compile (toByteString txt) opts -- check at compile time+quoteExpRegex opts txt = [| PCRE.compile (cs (txt ∷ String)) opts |]+ where !_ = PCRE.compile (cs txt) opts -- check at compile time -- | Returns a QuasiQuoter like 're', but with given PCRE options. mkRegexQQ ∷ [PCREOption] → QuasiQuoter
pcre-heavy.cabal view
@@ -1,5 +1,5 @@ name: pcre-heavy-version: 0.2.5+version: 1.0.0 synopsis: A regexp library on top of pcre-light you can actually use. description: A regular expressions library that does not suck.@@ -34,12 +34,11 @@ base >= 4.3.0.0 && < 5 , pcre-light , bytestring- , stringable+ , string-conversions , template-haskell default-language: Haskell2010 exposed-modules: Text.Regex.PCRE.Heavy- ghc-prof-options: -prof ghc-options: -Wall hs-source-dirs: library