{-# LANGUAGE BangPatterns, EmptyDataDecls, ScopedTypeVariables #-}
module Data.Text.ICU.Regex.Pure
(
MatchOption(..)
, ParseError(errError, errLine, errOffset)
, Match
, Regex
, Regular
, regex
, regex'
, pattern
, find
, findAll
, groupCount
, unfold
, span
, group
, prefix
, suffix
) where
import qualified Control.Exception as E
import Data.String (IsString(..))
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Foreign as T
import Data.Text.ICU.Error.Internal (ParseError(..), handleError)
import qualified Data.Text.ICU.Regex as IO
import Data.Text.ICU.Regex.Internal hiding (Regex(..), regex)
import qualified Data.Text.ICU.Regex.Internal as Internal
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Marshal.Array (advancePtr)
import Foreign.Storable (peek)
import Prelude hiding (span)
import System.IO.Unsafe (unsafeInterleaveIO, unsafePerformIO)
newtype Regex = Regex {
Regex -> Regex
reRe :: Internal.Regex
}
instance Show Regex where
show :: Regex -> String
show Regex
re = String
"Regex " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show (Regex -> Text
forall r. Regular r => r -> Text
pattern Regex
re)
instance IsString Regex where
fromString :: String -> Regex
fromString = [MatchOption] -> Text -> Regex
regex [] (Text -> Regex) -> (String -> Text) -> String -> Regex
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack
data Match = Match {
Match -> Regex
matchRe :: Internal.Regex
, Match -> I16
_matchPrev :: T.I16
}
instance Show Match where
show :: Match -> String
show Match
m = String
"Match " String -> ShowS
forall a. [a] -> [a] -> [a]
++ [Text] -> String
forall a. Show a => a -> String
show ((Int -> Match -> Maybe Text) -> Match -> [Text]
unfold Int -> Match -> Maybe Text
group Match
m)
class Regular r where
regRe :: r -> Internal.Regex
regFp :: r -> ForeignPtr URegularExpression
regFp = Regex -> ForeignPtr URegularExpression
Internal.reRe (Regex -> ForeignPtr URegularExpression)
-> (r -> Regex) -> r -> ForeignPtr URegularExpression
forall b c a. (b -> c) -> (a -> b) -> a -> c
. r -> Regex
forall r. Regular r => r -> Regex
regRe
{-# INLINE regFp #-}
instance Regular Match where
regRe :: Match -> Regex
regRe = Match -> Regex
matchRe
instance Regular Regex where
regRe :: Regex -> Regex
regRe = Regex -> Regex
reRe
regex :: [MatchOption] -> Text -> Regex
regex :: [MatchOption] -> Text -> Regex
regex [MatchOption]
opts Text
pat = Regex -> Regex
Regex (Regex -> Regex) -> (IO Regex -> Regex) -> IO Regex -> Regex
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO Regex -> Regex
forall a. IO a -> a
unsafePerformIO (IO Regex -> Regex) -> IO Regex -> Regex
forall a b. (a -> b) -> a -> b
$ [MatchOption] -> Text -> IO Regex
IO.regex [MatchOption]
opts Text
pat
regex' :: [MatchOption] -> Text -> Either ParseError Regex
regex' :: [MatchOption] -> Text -> Either ParseError Regex
regex' [MatchOption]
opts Text
pat = IO (Either ParseError Regex) -> Either ParseError Regex
forall a. IO a -> a
unsafePerformIO (IO (Either ParseError Regex) -> Either ParseError Regex)
-> IO (Either ParseError Regex) -> Either ParseError Regex
forall a b. (a -> b) -> a -> b
$
((Regex -> Either ParseError Regex
forall a b. b -> Either a b
Right (Regex -> Either ParseError Regex)
-> (Regex -> Regex) -> Regex -> Either ParseError Regex
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Regex -> Regex
Regex) (Regex -> Either ParseError Regex)
-> IO Regex -> IO (Either ParseError Regex)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` [MatchOption] -> Text -> IO Regex
Internal.regex [MatchOption]
opts Text
pat) IO (Either ParseError Regex)
-> (ParseError -> IO (Either ParseError Regex))
-> IO (Either ParseError Regex)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`E.catch`
\(ParseError
err::ParseError) -> Either ParseError Regex -> IO (Either ParseError Regex)
forall (m :: * -> *) a. Monad m => a -> m a
return (ParseError -> Either ParseError Regex
forall a b. a -> Either a b
Left ParseError
err)
pattern :: Regular r => r -> Text
pattern :: r -> Text
pattern r
r = IO Text -> Text
forall a. IO a -> a
unsafePerformIO (IO Text -> Text)
-> ((Ptr URegularExpression -> IO Text) -> IO Text)
-> (Ptr URegularExpression -> IO Text)
-> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ForeignPtr URegularExpression
-> (Ptr URegularExpression -> IO Text) -> IO Text
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr (r -> ForeignPtr URegularExpression
forall r. Regular r => r -> ForeignPtr URegularExpression
regFp r
r) ((Ptr URegularExpression -> IO Text) -> Text)
-> (Ptr URegularExpression -> IO Text) -> Text
forall a b. (a -> b) -> a -> b
$ \Ptr URegularExpression
rePtr ->
(Ptr Int32 -> IO Text) -> IO Text
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr Int32 -> IO Text) -> IO Text)
-> (Ptr Int32 -> IO Text) -> IO Text
forall a b. (a -> b) -> a -> b
$ \Ptr Int32
lenPtr -> do
Ptr UChar
textPtr <- (Ptr UErrorCode -> IO (Ptr UChar)) -> IO (Ptr UChar)
forall a. (Ptr UErrorCode -> IO a) -> IO a
handleError ((Ptr UErrorCode -> IO (Ptr UChar)) -> IO (Ptr UChar))
-> (Ptr UErrorCode -> IO (Ptr UChar)) -> IO (Ptr UChar)
forall a b. (a -> b) -> a -> b
$ Ptr URegularExpression
-> Ptr Int32 -> Ptr UErrorCode -> IO (Ptr UChar)
uregex_pattern Ptr URegularExpression
rePtr Ptr Int32
lenPtr
(Ptr UChar -> I16 -> IO Text
T.fromPtr Ptr UChar
textPtr (I16 -> IO Text) -> (Int32 -> I16) -> Int32 -> IO Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> I16
forall a b. (Integral a, Num b) => a -> b
fromIntegral) (Int32 -> IO Text) -> IO Int32 -> IO Text
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Ptr Int32 -> IO Int32
forall a. Storable a => Ptr a -> IO a
peek Ptr Int32
lenPtr
find :: Regex -> Text -> Maybe Match
find :: Regex -> Text -> Maybe Match
find Regex
re0 Text
haystack = IO (Maybe Match) -> Maybe Match
forall a. IO a -> a
unsafePerformIO (IO (Maybe Match) -> Maybe Match)
-> ((Regex -> IO (Maybe Match)) -> IO (Maybe Match))
-> (Regex -> IO (Maybe Match))
-> Maybe Match
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
Regex -> Text -> (Regex -> IO (Maybe Match)) -> IO (Maybe Match)
forall a. Regex -> Text -> (Regex -> IO a) -> IO a
matching Regex
re0 Text
haystack ((Regex -> IO (Maybe Match)) -> Maybe Match)
-> (Regex -> IO (Maybe Match)) -> Maybe Match
forall a b. (a -> b) -> a -> b
$ \Regex
re -> do
Bool
m <- Regex -> IO Bool
IO.findNext Regex
re
Maybe Match -> IO (Maybe Match)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Match -> IO (Maybe Match))
-> Maybe Match -> IO (Maybe Match)
forall a b. (a -> b) -> a -> b
$! if Bool
m then Match -> Maybe Match
forall a. a -> Maybe a
Just (Regex -> I16 -> Match
Match Regex
re I16
0) else Maybe Match
forall a. Maybe a
Nothing
findAll :: Regex -> Text -> [Match]
findAll :: Regex -> Text -> [Match]
findAll Regex
re0 Text
haystack = IO [Match] -> [Match]
forall a. IO a -> a
unsafePerformIO (IO [Match] -> [Match])
-> (IO [Match] -> IO [Match]) -> IO [Match] -> [Match]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO [Match] -> IO [Match]
forall a. IO a -> IO a
unsafeInterleaveIO (IO [Match] -> [Match]) -> IO [Match] -> [Match]
forall a b. (a -> b) -> a -> b
$ I16 -> IO [Match]
go I16
0
where
len :: I16
len = Int -> I16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> I16) -> (Text -> Int) -> Text -> I16
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Int
T.lengthWord16 (Text -> I16) -> Text -> I16
forall a b. (a -> b) -> a -> b
$ Text
haystack
go :: I16 -> IO [Match]
go !I16
n | I16
n I16 -> I16 -> Bool
forall a. Ord a => a -> a -> Bool
>= I16
len = [Match] -> IO [Match]
forall (m :: * -> *) a. Monad m => a -> m a
return []
| Bool
otherwise = Regex -> Text -> (Regex -> IO [Match]) -> IO [Match]
forall a. Regex -> Text -> (Regex -> IO a) -> IO a
matching Regex
re0 Text
haystack ((Regex -> IO [Match]) -> IO [Match])
-> (Regex -> IO [Match]) -> IO [Match]
forall a b. (a -> b) -> a -> b
$ \Regex
re -> do
Bool
found <- Regex -> I16 -> IO Bool
IO.find Regex
re I16
n
if Bool
found
then do
I16
n' <- Regex -> Int -> IO I16
IO.end_ Regex
re Int
0
(Regex -> I16 -> Match
Match Regex
re I16
nMatch -> [Match] -> [Match]
forall a. a -> [a] -> [a]
:) ([Match] -> [Match]) -> IO [Match] -> IO [Match]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` I16 -> IO [Match]
go I16
n'
else [Match] -> IO [Match]
forall (m :: * -> *) a. Monad m => a -> m a
return []
matching :: Regex -> Text -> (IO.Regex -> IO a) -> IO a
matching :: Regex -> Text -> (Regex -> IO a) -> IO a
matching (Regex Regex
re0) Text
haystack Regex -> IO a
act = do
Regex
re <- Regex -> IO Regex
IO.clone Regex
re0
Regex -> Text -> IO ()
IO.setText Regex
re Text
haystack
Regex -> IO a
act Regex
re
groupCount :: Regular r => r -> Int
groupCount :: r -> Int
groupCount = IO Int -> Int
forall a. IO a -> a
unsafePerformIO (IO Int -> Int) -> (r -> IO Int) -> r -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Regex -> IO Int
IO.groupCount (Regex -> IO Int) -> (r -> Regex) -> r -> IO Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. r -> Regex
forall r. Regular r => r -> Regex
regRe
{-# INLINE groupCount #-}
unfold :: (Int -> Match -> Maybe Text) -> Match -> [Text]
unfold :: (Int -> Match -> Maybe Text) -> Match -> [Text]
unfold Int -> Match -> Maybe Text
f Match
m = Int -> [Text]
go Int
0
where go :: Int -> [Text]
go !Int
n = case Int -> Match -> Maybe Text
f Int
n Match
m of
Maybe Text
Nothing -> []
Just Text
z -> Text
z Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: Int -> [Text]
go (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1)
group :: Int -> Match -> Maybe Text
group :: Int -> Match -> Maybe Text
group Int
n Match
m = Int -> Match -> (Regex -> IO Text) -> Maybe Text
forall a. Int -> Match -> (Regex -> IO a) -> Maybe a
grouping Int
n Match
m ((Regex -> IO Text) -> Maybe Text)
-> (Regex -> IO Text) -> Maybe Text
forall a b. (a -> b) -> a -> b
$ \Regex
re -> do
let n' :: Int
n' = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n
I16
start <- I16 -> I16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (I16 -> I16) -> IO I16 -> IO I16
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Regex -> Int -> IO I16
IO.start_ Regex
re Int
n'
I16
end <- I16 -> I16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (I16 -> I16) -> IO I16 -> IO I16
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Regex -> Int -> IO I16
IO.end_ Regex
re Int
n'
(ForeignPtr UChar
fp,I16
_) <- Regex -> IO (ForeignPtr UChar, I16)
IO.getText Regex
re
ForeignPtr UChar -> (Ptr UChar -> IO Text) -> IO Text
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr UChar
fp ((Ptr UChar -> IO Text) -> IO Text)
-> (Ptr UChar -> IO Text) -> IO Text
forall a b. (a -> b) -> a -> b
$ \Ptr UChar
ptr ->
Ptr UChar -> I16 -> IO Text
T.fromPtr (Ptr UChar
ptr Ptr UChar -> Int -> Ptr UChar
forall a. Storable a => Ptr a -> Int -> Ptr a
`advancePtr` I16 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral I16
start) (I16
end I16 -> I16 -> I16
forall a. Num a => a -> a -> a
- I16
start)
prefix :: Int -> Match -> Maybe Text
prefix :: Int -> Match -> Maybe Text
prefix Int
n Match
m = Int -> Match -> (Regex -> IO Text) -> Maybe Text
forall a. Int -> Match -> (Regex -> IO a) -> Maybe a
grouping Int
n Match
m ((Regex -> IO Text) -> Maybe Text)
-> (Regex -> IO Text) -> Maybe Text
forall a b. (a -> b) -> a -> b
$ \Regex
re -> do
I16
start <- I16 -> I16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (I16 -> I16) -> IO I16 -> IO I16
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Regex -> Int -> IO I16
IO.start_ Regex
re Int
n
(ForeignPtr UChar
fp,I16
_) <- Regex -> IO (ForeignPtr UChar, I16)
IO.getText Regex
re
ForeignPtr UChar -> (Ptr UChar -> IO Text) -> IO Text
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr UChar
fp (Ptr UChar -> I16 -> IO Text
`T.fromPtr` I16
start)
span :: Match -> Text
span :: Match -> Text
span (Match Regex
re I16
p) = IO Text -> Text
forall a. IO a -> a
unsafePerformIO (IO Text -> Text) -> IO Text -> Text
forall a b. (a -> b) -> a -> b
$ do
I16
start <- Regex -> Int -> IO I16
IO.start_ Regex
re Int
0
(ForeignPtr UChar
fp,I16
_) <- Regex -> IO (ForeignPtr UChar, I16)
IO.getText Regex
re
ForeignPtr UChar -> (Ptr UChar -> IO Text) -> IO Text
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr UChar
fp ((Ptr UChar -> IO Text) -> IO Text)
-> (Ptr UChar -> IO Text) -> IO Text
forall a b. (a -> b) -> a -> b
$ \Ptr UChar
ptr ->
Ptr UChar -> I16 -> IO Text
T.fromPtr (Ptr UChar
ptr Ptr UChar -> Int -> Ptr UChar
forall a. Storable a => Ptr a -> Int -> Ptr a
`advancePtr` I16 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral I16
p) (I16
start I16 -> I16 -> I16
forall a. Num a => a -> a -> a
- I16
p)
suffix :: Int -> Match -> Maybe Text
suffix :: Int -> Match -> Maybe Text
suffix Int
n Match
m = Int -> Match -> (Regex -> IO Text) -> Maybe Text
forall a. Int -> Match -> (Regex -> IO a) -> Maybe a
grouping Int
n Match
m ((Regex -> IO Text) -> Maybe Text)
-> (Regex -> IO Text) -> Maybe Text
forall a b. (a -> b) -> a -> b
$ \Regex
re -> do
I16
end <- I16 -> I16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (I16 -> I16) -> IO I16 -> IO I16
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Regex -> Int -> IO I16
IO.end_ Regex
re Int
n
(ForeignPtr UChar
fp,I16
len) <- Regex -> IO (ForeignPtr UChar, I16)
IO.getText Regex
re
ForeignPtr UChar -> (Ptr UChar -> IO Text) -> IO Text
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr UChar
fp ((Ptr UChar -> IO Text) -> IO Text)
-> (Ptr UChar -> IO Text) -> IO Text
forall a b. (a -> b) -> a -> b
$ \Ptr UChar
ptr -> do
Ptr UChar -> I16 -> IO Text
T.fromPtr (Ptr UChar
ptr Ptr UChar -> Int -> Ptr UChar
forall a. Storable a => Ptr a -> Int -> Ptr a
`advancePtr` I16 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral I16
end) (I16
len I16 -> I16 -> I16
forall a. Num a => a -> a -> a
- I16
end)
grouping :: Int -> Match -> (Internal.Regex -> IO a) -> Maybe a
grouping :: Int -> Match -> (Regex -> IO a) -> Maybe a
grouping Int
n (Match Regex
m I16
_) Regex -> IO a
act = IO (Maybe a) -> Maybe a
forall a. IO a -> a
unsafePerformIO (IO (Maybe a) -> Maybe a) -> IO (Maybe a) -> Maybe a
forall a b. (a -> b) -> a -> b
$ do
Int
count <- Regex -> IO Int
IO.groupCount Regex
m
let n' :: Int
n' = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n
if Int
n' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
|| (Int
n' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 Bool -> Bool -> Bool
&& Int
n' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
count)
then a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> IO a -> IO (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Regex -> IO a
act Regex
m
else Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing