{-# LANGUAGE GADTs, ScopedTypeVariables, ViewPatterns #-} {-# OPTIONS_GHC -fno-do-lambda-eta-expansion #-} module Text.Regex.Applicative.Compile (compile) where import Text.Regex.Applicative.Types compile :: forall a s r . RE s a -> (a -> [Thread s r]) -> [Thread s r] compile e k = compile2 e k k -- The whole point of this module is this function, compile2, which needs to be -- compiled with -fno-do-lambda-eta-expansion for efficiency. -- -- Since this option would make other code perform worse, we place this -- function in a separate module and make sure it's not inlined. -- -- The point of "-fno-do-lambda-eta-expansion" is to make sure the tree is -- "compiled" only once. -- -- compile2 function takes two continuations: one when the match is empty and -- one when the match is non-empty. See the "Rep" case for the reason. compile2 :: forall a s r . RE s a -> (a -> [Thread s r]) -> (a -> [Thread s r]) -> [Thread s r] compile2 e = case e of Eps -> \ke _kn -> ke $ error "empty" Symbol i p -> \_ke kn -> [t kn] where t :: (a -> [Thread s r]) -> Thread s r t k = Thread i $ \s -> if p s then k s else [] App (compile2 -> a1) (compile2 -> a2) -> \ke kn -> a1 -- empty (\a1_value -> a2 (ke . a1_value) (kn . a1_value)) -- non-empty (\a1_value -> a2 (kn . a1_value) (kn . a1_value)) Alt (compile2 -> a1) (compile2 -> a2) -> \ke kn -> a1 ke kn ++ a2 ke kn Fmap f (compile2 -> a) -> \ke kn -> a (ke . f) (kn . f) -- This is actually the point where we use the difference between -- continuations. For the inner RE the empty continuation is a -- "failing" one in order to avoid non-termination. Rep g f b (compile2 -> a) -> let combine continue stop = case g of Greedy -> continue ++ stop NonGreedy -> stop ++ continue threads b ke kn = combine (a (\_ -> []) (\v -> let b' = f b v in threads b' kn kn)) (ke b) in threads b