regex-applicative-0.1.4: Text/Regex/Applicative/Implementation.hs
{-# LANGUAGE GADTs, TypeFamilies, ViewPatterns, PatternGuards #-}
module Text.Regex.Applicative.Implementation (match, Regexp(..)) where
import Prelude
import Control.Applicative hiding (empty)
import Control.Monad.State hiding (foldM)
import Text.Regex.Applicative.StateQueue
import Control.Monad.ST
import Text.Regex.Applicative.Types
import Text.Regex.Applicative.Compile
fresh :: (MonadState m, StateType m ~ ThreadId) => m ThreadId
fresh = do
i <- get
put $! i+1
return i
renumber :: Regexp s i a -> (Regexp s ThreadId a, ThreadId)
renumber e = flip runState 1 $ compile e
where
compile :: Regexp s i a -> State ThreadId (Regexp s ThreadId a)
compile e =
case e of
Eps -> return Eps
Symbol _ p -> Symbol <$> fresh <*> pure p
Alt a1 a2 -> Alt <$> compile a1 <*> compile a2
App a1 a2 -> App <$> compile a1 <*> compile a2
Fmap f a -> Fmap f <$> compile a
Rep f b a -> Rep f b <$> compile a
threadId :: Thread s a -> ThreadId
threadId Accept {} = 0
threadId Thread { threadId_ = i } = i
run :: StateQueue st (Thread s r)
-> StateQueue st (Thread s r)
-> [s] -> ST st (Maybe r)
run queue _ [] = fold f Nothing queue
where f a@Just{} _ _ = return a
f Nothing _ x | Accept r <- x = return $ Just r
| otherwise = return Nothing
run queue newQueue (s:ss) = do
let accum q _ t =
case t of
Accept {} -> return q
Thread _ c ->
foldM (\q x -> tryInsert x q) q $ c s
newQueue <- fold accum newQueue queue
let veryNewQueue = clear queue
run newQueue veryNewQueue ss
tryInsert :: Thread s r -> StateQueue st (Thread s r) -> ST st (StateQueue st (Thread s r))
tryInsert t@(threadId -> ThreadId i) queue = do
alreadyPresent <- member i queue
if alreadyPresent
then return queue
else insert i t queue
match :: Regexp s a r -> [s] -> Maybe r
match r s = runST $ do
let (rr, ThreadId numStates) = renumber r
q1 <- empty numStates
q2 <- empty numStates
let threads = compile rr (\x -> [Accept x])
q1 <- foldM (\q t -> tryInsert t q) q1 threads
run q1 q2 s
-- This turns out to be much faster than the standard foldM,
-- because of inlining.
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
foldM f a l = foldr (\x k a -> f a x >>= k) return l $ a