module EL.Private.Regex (
Regex
, Option(..)
, compile, compileOptions, compileUnsafe, compileOptionsUnsafe
, matches, groups, groupRanges
, substitute, substituteGroups
, escape
) where
import qualified Data.ByteString as ByteString
import qualified Data.Text as Text
import Data.Text (Text)
import qualified Data.Text.Encoding as Encoding
import GHC.Stack (HasCallStack)
import qualified Text.Regex.PCRE.Heavy as PCRE
import Text.Regex.PCRE.Heavy (Regex)
import qualified Text.Regex.PCRE.Light as PCRE
fromText :: Text -> ByteString.ByteString
fromText = Encoding.encodeUtf8
data Option = CaseInsensitive | DotAll | Multiline
deriving (Ord, Eq, Show)
compile :: String -> Either String Regex
compile = compileOptions []
compileOptions :: [Option] -> String -> Either String Regex
compileOptions options text =
case PCRE.compileM (fromText (Text.pack text)) (convertOptions options) of
Left msg -> Left $ "compiling regex " ++ show text ++ ": " ++ msg
Right regex -> Right regex
convertOptions :: [Option] -> [PCRE.PCREOption]
convertOptions = (options++) . map convert
where
convert opt = case opt of
CaseInsensitive -> PCRE.caseless
DotAll -> PCRE.dotall
Multiline -> PCRE.multiline
options = [PCRE.utf8, PCRE.no_utf8_check]
compileUnsafe :: HasCallStack => String -> Regex
compileUnsafe = compileOptionsUnsafe []
compileOptionsUnsafe :: HasCallStack => [Option] -> String -> Regex
compileOptionsUnsafe options = either error id . compileOptions options
matches :: Regex -> Text -> Bool
matches = flip (PCRE.=~)
groups :: Regex -> Text -> [(Text, [Text])]
groups = PCRE.scan
groupRanges :: Regex -> Text -> [((Int, Int), [(Int, Int)])]
groupRanges = PCRE.scanRanges
substitute :: Regex -> Text -> Text -> Text
substitute regex sub = PCRE.gsub regex sub
substituteGroups :: Regex -> (Text -> [Text] -> Text)
-> Text -> Text
substituteGroups = PCRE.gsub
escape :: String -> String
escape "" = ""
escape (c : cs)
| c `elem` ("\\^$.[|()?*+{" :: [Char]) = '\\' : c : escape cs
| otherwise = c : escape cs