module Text.Regex.DFA.Wrap(Regex(..),CompOption(..),ExecOption(..),(=~),(=~~),makeCompat) where
import Text.Regex.Base.RegexLike(RegexMaker(..),RegexOptions(..),RegexContext(..))
import Text.Regex.DFA.Common(CompOption(..),ExecOption(..))
import Text.Regex.DFA.Engine
import Text.Regex.DFA.Pattern
import Text.Regex.DFA.ReadRegex(decodePatternSet)
import Data.Char(toUpper,toLower)
import Data.List(nub,sort)
import qualified Data.Set as Set(toList)
data Regex = Regex {asPattern::Pattern
,asLexer::Lexer
,compOptions::CompOption
,execOptions::ExecOption}
instance RegexOptions Regex CompOption ExecOption where
blankCompOpt = CompOption {caseSensitive = True,multiline = True}
blankExecOpt = ExecOption
defaultCompOpt = CompOption {caseSensitive = True,multiline = True}
defaultExecOpt = ExecOption
setExecOpts e r = r {execOptions=e}
getExecOpts r = execOptions r
(=~) :: (RegexMaker Regex CompOption ExecOption source,RegexContext Regex source1 target)
=> source1 -> source -> target
(=~) x r = let q :: Regex
q = makeRegex r
in match q x
(=~~) :: (RegexMaker Regex CompOption ExecOption source,RegexContext Regex source1 target,Monad m)
=> source1 -> source -> m target
(=~~) x r = do (q::Regex) <- makeRegexM r
matchM q x
makeCompat :: CompOption -> Pattern -> Regexp
makeCompat c pIn = makeCompatCont c pIn emptyOp
makeCompatCont :: CompOption -> Pattern -> Regexp -> Regexp
makeCompatCont CompOption {caseSensitive = caseOpt, multiline = lineOpt} = reflect
where
reflect :: Pattern -> Regexp -> Regexp
reflect pIn cont =
case pIn of
PEmpty -> cont
PGroup _ p -> reflect p cont
POr [] -> cont
POr ps -> orRE (map (\p->reflect p cont) ps)
PConcat [] -> cont
PConcat ps -> foldr ($) cont (map reflect ps)
PQuest p -> quest (reflect p emptyOp) cont
PPlus p -> plus (reflect p emptyOp) cont
PStar p -> star (reflect p emptyOp) cont
PBound 0 Nothing p -> reflect (PStar p) cont
PBound i Nothing p | 0<i -> reflect (PConcat ((replicate i p)++[PStar p])) cont
| otherwise -> die
PBound 0 (Just 0) _ -> cont
PBound 0 (Just 1) p -> reflect (PQuest p) cont
PBound 0 (Just j) p | j>1 -> reflect (PQuest (PConcat [p,PBound 0 (Just (pred j)) p])) cont
| otherwise -> die
PBound i (Just j) p | 0<i && i == j -> reflect (PConcat (replicate i p)) cont
| 0<i && i < j -> reflect (PConcat ((replicate i p)++[PBound 0 (Just (ji)) p])) cont
| otherwise -> die
PCarat i -> (if lineOpt then beginLine i else beginInput i) +> cont
PDollar i -> (if lineOpt then endLine i else endInput i) +> cont
PDot i -> (if lineOpt then altNot i ['\n'] else allChar i) +> cont
PAny i patset -> let chars = charCases . Set.toList . decodePatternSet $ patset
in alt i chars +> cont
PAnyNot i patset -> let chars = charCases . Set.toList . decodePatternSet $ patset
in altNot i chars +> cont
PEscape i c -> charOrAlt i c +> cont
PChar i c -> charOrAlt i c +> cont
where
die = error ("Text.Regex.DFA.Wrap makeCompatCont failed: show pIn")
charOrAlt i c = if caseOpt || (toUpper c == toLower c)
then char i c
else alt i [toLower c,toUpper c]
charCases cs = if caseOpt
then cs
else let chars'up = map toUpper cs
chars'down = map toLower cs
in nub . sort $ (chars'up ++ chars'down)