{-# LANGUAGE TypeFamilies, GADTs, TupleSections #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Text.Regex.Applicative.Interface where
import Control.Applicative hiding (empty)
import qualified Control.Applicative
import Control.Arrow
import Data.Traversable
import Data.String
import Data.Maybe
import Text.Regex.Applicative.Types
import Text.Regex.Applicative.Object
instance Functor (RE s) where
fmap f x = Fmap f x
f <$ x = pure f <* x
instance Applicative (RE s) where
pure x = const x <$> Eps
a1 <*> a2 = App a1 a2
a *> b = pure (const id) <*> Void a <*> b
a <* b = pure const <*> a <*> Void b
instance Alternative (RE s) where
a1 <|> a2 = Alt a1 a2
empty = Fail
many a = reverse <$> Rep Greedy (flip (:)) [] a
some a = (:) <$> a <*> many a
instance (char ~ Char, string ~ String) => IsString (RE char string) where
fromString = string
comap :: (s2 -> s1) -> RE s1 a -> RE s2 a
comap f re =
case re of
Eps -> Eps
Symbol t p -> Symbol t (p . f)
Alt r1 r2 -> Alt (comap f r1) (comap f r2)
App r1 r2 -> App (comap f r1) (comap f r2)
Fmap g r -> Fmap g (comap f r)
Fail -> Fail
Rep gr fn a r -> Rep gr fn a (comap f r)
Void r -> Void (comap f r)
psym :: (s -> Bool) -> RE s s
psym p = msym (\s -> if p s then Just s else Nothing)
msym :: (s -> Maybe a) -> RE s a
msym p = Symbol (error "Not numbered symbol") p
sym :: Eq s => s -> RE s s
sym s = psym (s ==)
anySym :: RE s s
anySym = msym Just
string :: Eq a => [a] -> RE a [a]
string = traverse sym
reFoldl :: Greediness -> (b -> a -> b) -> b -> RE s a -> RE s b
reFoldl g f b a = Rep g f b a
few :: RE s a -> RE s [a]
few a = reverse <$> Rep NonGreedy (flip (:)) [] a
withMatched :: RE s a -> RE s (a, [s])
withMatched Eps = flip (,) [] <$> Eps
withMatched (Symbol t p) = Symbol t (\s -> (,[s]) <$> p s)
withMatched (Alt a b) = withMatched a <|> withMatched b
withMatched (App a b) =
(\(f, s) (x, t) -> (f x, s ++ t)) <$>
withMatched a <*>
withMatched b
withMatched Fail = Fail
withMatched (Fmap f x) = (f *** id) <$> withMatched x
withMatched (Rep gr f a0 x) =
Rep gr (\(a, s) (x, t) -> (f a x, s ++ t)) (a0, []) (withMatched x)
withMatched (Void x) = (const () *** id) <$> withMatched x
(=~) :: [s] -> RE s a -> Maybe a
(=~) = flip match
infix 2 =~
match :: RE s a -> [s] -> Maybe a
match re = let obj = compile re in \str ->
listToMaybe $
results $
foldl (flip step) obj str
findFirstPrefix :: RE s a -> [s] -> Maybe (a, [s])
findFirstPrefix re str = go (compile re) str Nothing
where
walk obj [] = (obj, Nothing)
walk obj (t:ts) =
case getResult t of
Just r -> (obj, Just r)
Nothing -> walk (addThread t obj) ts
go obj str resOld =
case walk emptyObject $ threads obj of
(obj', resThis) ->
let res = ((flip (,) str) <$> resThis) <|> resOld
in
case str of
_ | failed obj' -> res
[] -> res
(s:ss) -> go (step s obj') ss res
findLongestPrefix :: RE s a -> [s] -> Maybe (a, [s])
findLongestPrefix re str = go (compile re) str Nothing
where
go obj str resOld =
let res = (fmap (flip (,) str) $ listToMaybe $ results obj) <|> resOld
in
case str of
_ | failed obj -> res
[] -> res
(s:ss) -> go (step s obj) ss res
findShortestPrefix :: RE s a -> [s] -> Maybe (a, [s])
findShortestPrefix re str = go (compile re) str
where
go obj str =
case results obj of
r : _ -> Just (r, str)
_ | failed obj -> Nothing
_ ->
case str of
[] -> Nothing
s:ss -> go (step s obj) ss
findFirstInfix :: RE s a -> [s] -> Maybe ([s], a, [s])
findFirstInfix re str =
fmap (\((first, res), last) -> (first, res, last)) $
findFirstPrefix ((,) <$> few anySym <*> re) str
prefixCounter :: RE s (Int, [s])
prefixCounter = second reverse <$> reFoldl NonGreedy f (0, []) anySym
where
f (i, prefix) s = ((,) $! (i+1)) $ s:prefix
data InfixMatchingState s a = GotResult
{ prefixLen :: !Int
, prefixStr :: [s]
, result :: a
, postfixStr :: [s]
}
| NoResult
preferOver
:: InfixMatchingState s a
-> InfixMatchingState s a
-> InfixMatchingState s a
preferOver NoResult b = b
preferOver b NoResult = b
preferOver a b =
case prefixLen a `compare` prefixLen b of
GT -> b
_ -> a
mkInfixMatchingState
:: [s]
-> Thread s ((Int, [s]), a)
-> InfixMatchingState s a
mkInfixMatchingState rest thread =
case getResult thread of
Just ((pLen, pStr), res) ->
GotResult
{ prefixLen = pLen
, prefixStr = pStr
, result = res
, postfixStr = rest
}
Nothing -> NoResult
gotResult :: InfixMatchingState s a -> Bool
gotResult GotResult {} = True
gotResult _ = False
findExtremalInfix
::
(InfixMatchingState s a -> InfixMatchingState s a -> InfixMatchingState s a)
-> RE s a
-> [s]
-> Maybe ([s], a, [s])
findExtremalInfix newOrOld re str =
case go (compile $ (,) <$> prefixCounter <*> re) str NoResult of
NoResult -> Nothing
r@GotResult{} ->
Just (prefixStr r, result r, postfixStr r)
where
go obj str resOld =
let resThis =
foldl
(\acc t -> acc `preferOver` mkInfixMatchingState str t)
NoResult $
threads obj
res = resThis `newOrOld` resOld
obj' =
if gotResult resThis && not (gotResult resOld)
then fromThreads $ init $ threads obj
else obj
in
case str of
[] -> res
_ | failed obj -> res
(s:ss) -> go (step s obj') ss res
findLongestInfix :: RE s a -> [s] -> Maybe ([s], a, [s])
findLongestInfix = findExtremalInfix preferOver
findShortestInfix :: RE s a -> [s] -> Maybe ([s], a, [s])
findShortestInfix = findExtremalInfix $ flip preferOver
replace :: RE s [s] -> [s] -> [s]
replace r = ($ []) . go
where go ys = case findLongestInfix r ys of
Nothing -> (ys ++)
Just (before, m, rest) -> (before ++) . (m ++) . go rest