module Text.Regex.TDFA.TNFA(patternToNFA
,QNFA(..),QT(..),QTrans,TagUpdate(..)) where
import Control.Monad(when)
import Control.Monad.State(State,runState,execState,get,put,modify)
import Data.Array.IArray(Array,array)
import Data.Char(toLower,toUpper,isAlpha,ord)
import Data.List(foldl')
import Data.IntMap (IntMap)
import qualified Data.IntMap as IMap(toAscList,null,unionWith,singleton,fromList,fromDistinctAscList)
import Data.IntMap.CharMap2(CharMap(..))
import qualified Data.IntMap.CharMap2 as Map(null,singleton,map)
import qualified Data.IntMap.EnumMap2 as EMap(null,keysSet,assocs)
import Data.IntSet.EnumSet2(EnumSet)
import qualified Data.IntSet.EnumSet2 as Set(singleton,toList,insert)
import Data.Maybe(catMaybes,isNothing)
import Data.Monoid as Mon(Monoid(..))
import qualified Data.Set as S(Set,insert,toAscList,empty)
import Text.Regex.TDFA.Common(QT(..),QNFA(..),QTrans,TagTask(..),TagUpdate(..),DoPa(..)
,CompOption(..)
,Tag,TagTasks,TagList,Index,WinTags,GroupIndex,GroupInfo(..)
,common_error,noWin,snd3,mapSnd)
import Text.Regex.TDFA.CorePattern(Q(..),P(..),OP(..),WhichTest,cleanNullView,NullView
,SetTestInfo(..),Wanted(..),TestInfo
,mustAccept,cannotAccept,patternToQ)
import Text.Regex.TDFA.Pattern(Pattern(..),PatternSet(..),unSEC,PatternSetCharacterClass(..))
ecart :: String -> a -> a
ecart _ = id
err :: String -> a
err t = common_error "Text.Regex.TDFA.TNFA" t
debug :: (Show a) => a -> s -> s
debug _ s = s
qtwin,qtlose :: QT
qtwin = Simple {qt_win=[(1,PreUpdate TagTask)],qt_trans=mempty,qt_other=mempty}
qtlose = Simple {qt_win=mempty,qt_trans=mempty,qt_other=mempty}
patternToNFA :: CompOption
-> (Pattern,(GroupIndex, DoPa))
-> ((Index,Array Index QNFA)
,Array Tag OP
,Array GroupIndex [GroupInfo])
patternToNFA compOpt pattern =
let (q,tags,groups) = patternToQ compOpt pattern
msg = unlines [ show q ]
in debug msg (qToNFA compOpt q,tags,groups)
nullable :: Q -> Bool
nullable = not . null . nullQ
notNullable :: Q -> Bool
notNullable = null . nullQ
maybeOnlyEmpty :: Q -> Maybe WinTags
maybeOnlyEmpty (Q {nullQ = ((SetTestInfo sti,tags):_)}) = if EMap.null sti then Just tags else Nothing
maybeOnlyEmpty _ = Nothing
usesQNFA :: Q -> Bool
usesQNFA (Q {wants=WantsBoth}) = True
usesQNFA (Q {wants=WantsQNFA}) = True
usesQNFA _ = False
mkQNFA :: Index -> QT -> QNFA
mkQNFA i qt = debug ("\n>QNFA id="++show i) $
QNFA i (debug ("\ngetting QT for "++show i) qt)
mkTesting :: QT -> QT
mkTesting t@(Testing {qt_a=a,qt_b=b}) = if a==b then a else t
mkTesting t = t
nullQT :: QT -> Bool
nullQT (Simple {qt_win=w,qt_trans=t,qt_other=o}) = noWin w && Map.null t && IMap.null o
nullQT _ = False
listTestInfo :: QT -> EnumSet WhichTest -> EnumSet WhichTest
listTestInfo qt s = execState (helper qt) s
where helper (Simple {}) = return ()
helper (Testing {qt_test = wt, qt_a = a, qt_b = b}) = do
modify (Set.insert wt)
helper a
helper b
applyNullViews :: NullView -> QT -> QT
applyNullViews [] win = win
applyNullViews nvs win = foldl' (dominate win) qtlose (reverse $ cleanNullView nvs) where
preferNullViews :: NullView -> QT -> QT
preferNullViews [] win = win
preferNullViews nvs win = foldl' (dominate win) win (reverse $ cleanNullView nvs) where
dominate :: QT -> QT -> (SetTestInfo,WinTags) -> QT
dominate win lose x@(SetTestInfo sti,tags) = debug ("dominate "++show x) $
let
win' = prependTags' tags win
winTests = listTestInfo win $ mempty
allTests = (listTestInfo lose $ winTests) `mappend` (EMap.keysSet sti)
useTest _ [] w _ = w
useTest (aTest:tests) allD@((dTest,dopas):ds) w l =
let (wA,wB,wD) = branches w
(lA,lB,lD) = branches l
branches qt@(Testing {}) | aTest==qt_test qt = (qt_a qt,qt_b qt,qt_dopas qt)
branches qt = (qt,qt,mempty)
in if aTest == dTest
then Testing {qt_test = aTest
,qt_dopas = (dopas `mappend` wD) `mappend` lD
,qt_a = useTest tests ds wA lA
,qt_b = lB}
else Testing {qt_test = aTest
,qt_dopas = wD `mappend` lD
,qt_a = useTest tests allD wA lA
,qt_b = useTest tests allD wB lB}
useTest [] _ _ _ = err "This case in dominate.useText cannot happen: second argument would have to have been null and that is checked before this case"
in useTest (Set.toList allTests) (EMap.assocs sti) win' lose
applyTest :: TestInfo -> QT -> QT
applyTest (wt,dopa) qt | nullQT qt = qt
| otherwise = applyTest' qt where
applyTest' :: QT -> QT
applyTest' q@(Simple {}) =
mkTesting $ Testing {qt_test = wt
,qt_dopas = Set.singleton dopa
,qt_a = q
,qt_b = qtlose}
applyTest' q@(Testing {qt_test=wt'}) =
case compare wt wt' of
LT -> Testing {qt_test = wt
,qt_dopas = Set.singleton dopa
,qt_a = q
,qt_b = qtlose}
EQ -> q {qt_dopas = Set.insert dopa (qt_dopas q)
,qt_b = qtlose}
GT -> q {qt_a = applyTest' (qt_a q)
,qt_b = applyTest' (qt_b q)}
mergeQT_2nd,mergeAltQT,mergeQT :: QT -> QT -> QT
mergeQT_2nd q1 q2 | nullQT q1 = q2
| otherwise = mergeQTWith (\_ w2 -> w2) q1 q2
mergeAltQT q1 q2 | nullQT q1 = q2
| otherwise = mergeQTWith (\w1 w2 -> if noWin w1 then w2 else w1) q1 q2
mergeQT q1 q2 | nullQT q1 = q2
| nullQT q2 = q1
| otherwise = mergeQTWith mappend q1 q2
mergeQTWith :: (WinTags -> WinTags -> WinTags) -> QT -> QT -> QT
mergeQTWith mergeWins = merge where
merge :: QT -> QT -> QT
merge (Simple w1 t1 o1) (Simple w2 t2 o2) =
let w' = mergeWins w1 w2
t' = fuseQTrans t1 o1 t2 o2
o' = mergeQTrans o1 o2
in Simple w' t' o'
merge t1@(Testing _ _ a1 b1) s2@(Simple {}) = mkTesting $
t1 {qt_a=(merge a1 s2), qt_b=(merge b1 s2)}
merge s1@(Simple {}) t2@(Testing _ _ a2 b2) = mkTesting $
t2 {qt_a=(merge s1 a2), qt_b=(merge s1 b2)}
merge t1@(Testing wt1 ds1 a1 b1) t2@(Testing wt2 ds2 a2 b2) = mkTesting $
case compare wt1 wt2 of
LT -> t1 {qt_a=(merge a1 t2), qt_b=(merge b1 t2)}
EQ -> Testing {qt_test = wt1
,qt_dopas = mappend ds1 ds2
,qt_a = merge a1 a2
,qt_b = merge b1 b2}
GT -> t2 {qt_a=(merge t1 a2), qt_b=(merge t1 b2)}
fuseQTrans :: (CharMap QTrans) -> QTrans
-> (CharMap QTrans) -> QTrans
-> CharMap QTrans
fuseQTrans (CharMap t1) o1 (CharMap t2) o2 = CharMap (IMap.fromDistinctAscList (fuse l1 l2)) where
l1 = IMap.toAscList t1
l2 = IMap.toAscList t2
fuse [] y = mapSnd (mergeQTrans o1) y
fuse x [] = mapSnd (mergeQTrans o2) x
fuse x@((xc,xa):xs) y@((yc,ya):ys) =
case compare xc yc of
LT -> (xc,mergeQTrans xa o2) : fuse xs y
EQ -> (xc,mergeQTrans xa ya) : fuse xs ys
GT -> (yc,mergeQTrans o1 ya) : fuse x ys
mergeQTrans :: QTrans -> QTrans -> QTrans
mergeQTrans = IMap.unionWith mappend
prependPreTag :: Maybe Tag -> QT -> QT
prependPreTag Nothing qt = qt
prependPreTag (Just tag) qt = prependTags' [(tag,PreUpdate TagTask)] qt
prependGroupResets :: [Tag] -> QT -> QT
prependGroupResets [] qt = qt
prependGroupResets tags qt = prependTags' [(tag,PreUpdate ResetGroupStopTask)|tag<-tags] qt
prependTags' :: TagList -> QT -> QT
prependTags' [] qt = qt
prependTags' tcs' qt@(Testing {}) = qt { qt_a = prependTags' tcs' (qt_a qt)
, qt_b = prependTags' tcs' (qt_b qt) }
prependTags' tcs' (Simple {qt_win=w,qt_trans=t,qt_other=o}) =
Simple { qt_win = if noWin w then w else tcs' `mappend` w
, qt_trans = Map.map prependQTrans t
, qt_other = prependQTrans o }
where prependQTrans = fmap (map (\(d,tcs) -> (d,tcs' `mappend` tcs)))
type S = State (Index
,[(Index,QNFA)]->[(Index,QNFA)])
type E = (TagTasks
,Either QNFA QT)
type ActCont = ( E
, Maybe E
, Maybe (TagTasks,QNFA))
newQNFA :: String -> QT -> S QNFA
newQNFA s qt = do
(thisI,oldQs) <- get
let futureI = succ thisI in seq futureI $ debug (">newQNFA< "++s++" : "++show thisI) $ do
let qnfa = mkQNFA thisI qt
put $! (futureI, oldQs . ((thisI,qnfa):))
return qnfa
fromQNFA :: QNFA -> E
fromQNFA qnfa = (mempty,Left qnfa)
fromQT :: QT -> E
fromQT qt = (mempty,Right qt)
asQNFA :: String -> E -> S E
asQNFA _ x@(_,Left _) = return x
asQNFA s (tags,Right qt) = do qnfa <- newQNFA s qt
return (tags, Left qnfa)
getQNFA :: String -> E -> S QNFA
getQNFA _ ([],Left qnfa) = return qnfa
getQNFA s (tags,Left qnfa) = newQNFA s (prependTags' (promoteTasks PreUpdate tags) (q_qt qnfa))
getQNFA s (tags,Right qt) = newQNFA s (prependTags' (promoteTasks PreUpdate tags) qt)
getQT :: E -> QT
getQT (tags,cont) = prependTags' (promoteTasks PreUpdate tags) (either q_qt id cont)
addTest :: TestInfo -> E -> E
addTest ti (tags,cont) = (tags, Right . applyTest ti . either q_qt id $ cont)
promoteTasks :: (TagTask->TagUpdate) -> TagTasks -> TagList
promoteTasks promote tags = map (\(tag,task) -> (tag,promote task)) tags
demoteTags :: TagList -> TagTasks
demoteTags = map helper
where helper (tag,PreUpdate tt) = (tag,tt)
helper (tag,PostUpdate tt) = (tag,tt)
{-# INLINE addWinTags #-}
addWinTags :: WinTags -> (TagTasks,a) -> (TagTasks,a)
addWinTags wtags (tags,cont) = (demoteTags wtags `mappend` tags
,cont)
{-# INLINE addTag' #-}
addTag' :: Tag -> (TagTasks,a) -> (TagTasks,a)
addTag' tag (tags,cont) = ((tag,TagTask):tags
,cont)
addTag :: Maybe Tag -> E -> E
addTag Nothing e = e
addTag (Just tag) e = addTag' tag e
{-# INLINE addGroupResets #-}
addGroupResets :: (Show a) => [Tag] -> (TagTasks,a) -> (TagTasks,a)
addGroupResets [] x = x
addGroupResets tags (tags',cont) = (foldr (:) tags' . map (\tag -> (tag,ResetGroupStopTask)) $ tags
,cont)
addGroupSets :: (Show a) => [Tag] -> (TagTasks,a) -> (TagTasks,a)
addGroupSets [] x = x
addGroupSets tags (tags',cont) = (foldr (:) tags' . map (\tag -> (tag,SetGroupStopTask)) $ tags
,cont)
getE :: ActCont -> E
getE (_,_,Just (tags,qnfa)) = (tags, Left qnfa)
getE (eLoop,Just accepting,_) = fromQT (mergeQT (getQT eLoop) (getQT accepting))
getE (eLoop,Nothing,_) = eLoop
addTestAC :: TestInfo -> ActCont -> ActCont
addTestAC ti (e,mE,_) = (addTest ti e
,fmap (addTest ti) mE
,Nothing)
addTagAC :: Maybe Tag -> ActCont -> ActCont
addTagAC Nothing ac = ac
addTagAC (Just tag) (e,mE,mQNFA) = (addTag' tag e
,fmap (addTag' tag) mE
,fmap (addTag' tag) mQNFA)
addGroupResetsAC :: [Tag] -> ActCont -> ActCont
addGroupResetsAC [] ac = ac
addGroupResetsAC tags (e,mE,mQNFA) = (addGroupResets tags e
,fmap (addGroupResets tags) mE
,fmap (addGroupResets tags) mQNFA)
addGroupSetsAC :: [Tag] -> ActCont -> ActCont
addGroupSetsAC [] ac = ac
addGroupSetsAC tags (e,mE,mQNFA) = (addGroupSets tags e
,fmap (addGroupSets tags) mE
,fmap (addGroupSets tags) mQNFA)
addWinTagsAC :: WinTags -> ActCont -> ActCont
addWinTagsAC wtags (e,mE,mQNFA) = (addWinTags wtags e
,fmap (addWinTags wtags) mE
,fmap (addWinTags wtags) mQNFA)
qToNFA :: CompOption -> Q -> (Index,Array Index QNFA)
qToNFA compOpt qTop = (q_id startingQNFA
,array (0,pred lastIndex) (table [])) where
(startingQNFA,(lastIndex,table)) =
runState (getTrans qTop (fromQT $ qtwin) >>= getQNFA "top level") startState
startState = (0,id)
getTrans,getTransTagless :: Q -> E -> S E
getTrans qIn@(Q {preReset=resets,postSet=sets,preTag=pre,postTag=post,unQ=pIn}) e = debug (">< getTrans "++show qIn++" <>") $
case pIn of
OneChar pat -> newTrans "getTrans/OneChar" resets pre pat . addTag post . addGroupSets sets $ e
Empty -> return . addGroupResets resets . addTag pre . addTag post . addGroupSets sets $ e
Test ti -> return . addGroupResets resets . addTag pre . addTest ti . addTag post . addGroupSets sets $ e
_ -> return . addGroupResets resets . addTag pre =<< getTransTagless qIn (addTag post . addGroupSets sets $ e)
getTransTagless qIn e = debug (">< getTransTagless "++show qIn++" <>") $
case unQ qIn of
Seq q1 q2 -> getTrans q1 =<< getTrans q2 e
Or [] -> return e
Or [q] -> getTrans q e
Or qs -> do
eqts <- if usesQNFA qIn
then do
eQNFA <- asQNFA "getTransTagless/Or/usesQNFA" e
sequence [ getTrans q eQNFA | q <- qs ]
else sequence [ getTrans q e | q <- qs ]
let qts = map getQT eqts
return (fromQT (foldr1 mergeAltQT qts))
Star mOrbit resetTheseOrbits mayFirstBeNull q ->
let (e',clear) =
if notNullable q then (e,True)
else if null resetTheseOrbits && isNothing mOrbit
then case maybeOnlyEmpty q of
Just [] -> (e,True)
Just tagList -> (addWinTags tagList e,False)
_ -> (fromQT . preferNullViews (nullQ q) . getQT $ e,False)
else (fromQT . resetOrbitsQT resetTheseOrbits . enterOrbitQT mOrbit
. preferNullViews (nullQ q) . getQT . leaveOrbit mOrbit $ e,False)
in if cannotAccept q then return e' else mdo
mqt <- inStar q this
(this,ans) <- case mqt of
Nothing -> err ("Weird pattern in getTransTagless/Star: " ++ show (qTop,qIn))
Just qt -> do
let qt' = resetOrbitsQT resetTheseOrbits . enterOrbitQT mOrbit $ qt
thisQT = mergeQT qt' . getQT . leaveOrbit mOrbit $ e
ansE = fromQT . mergeQT qt' . getQT $ e'
thisE <- if usesQNFA q
then return . fromQNFA =<< newQNFA "getTransTagless/Star" thisQT
else return . fromQT $ thisQT
return (thisE,ansE)
return (if mayFirstBeNull then (if clear then this
else ans)
else this)
NonEmpty q -> ecart ("\n> getTransTagless/NonEmpty"++show qIn) $ do
when (cannotAccept q) (err $ "getTransTagless/NonEmpty : provided with a *cannotAccept* pattern: "++show (qTop,qIn))
when (mustAccept q) (err $ "getTransTagless/NonEmpty : provided with a *mustAccept* pattern: "++show (qTop,qIn))
let e' = case maybeOnlyEmpty qIn of
Just [] -> e
Just _wtags -> e
Nothing -> err $ "getTransTagless/NonEmpty is supposed to have an emptyNull nullView : "++show qIn
mqt <- inStar q e
return $ case mqt of
Nothing -> err ("Weird pattern in getTransTagless/NonEmpty: " ++ show (qTop,qIn))
Just qt -> fromQT . mergeQT_2nd qt . getQT $ e'
_ -> err ("This case in Text.Regex.TNFA.TNFA.getTransTagless cannot happen" ++ show (qTop,qIn))
inStar,inStarNullableTagless :: Q -> E -> S (Maybe QT)
inStar qIn@(Q {preReset=resets,postSet=sets,preTag=pre,postTag=post}) eLoop | notNullable qIn =
debug (">< inStar/1 "++show qIn++" <>") $
return . Just . getQT =<< getTrans qIn eLoop
| otherwise =
debug (">< inStar/2 "++show qIn++" <>") $
return . fmap (prependGroupResets resets . prependPreTag pre) =<< inStarNullableTagless qIn (addTag post . addGroupSets sets $ eLoop)
inStarNullableTagless qIn eLoop = debug (">< inStarNullableTagless "++show qIn++" <>") $ do
case unQ qIn of
Empty -> return Nothing
Or [] -> return Nothing
Or [q] -> inStar q eLoop
Or qs -> do
mqts <- if usesQNFA qIn
then do eQNFA <- asQNFA "inStarNullableTagless/Or/usesQNFA" eLoop
sequence [ inStar q eQNFA | q <- qs ]
else sequence [inStar q eLoop | q <- qs ]
let qts = catMaybes mqts
mqt = if null qts then Nothing else Just (foldr1 mergeAltQT qts)
return mqt
Seq q1 q2 -> do (_,meAcceptingOut,_) <- actNullable q1 =<< actNullable q2 (eLoop,Nothing,Nothing)
return (fmap getQT meAcceptingOut)
Star {} -> do (_,meAcceptingOut,_) <- actNullableTagless qIn (eLoop,Nothing,Nothing)
return (fmap getQT meAcceptingOut)
NonEmpty {} -> ecart ("\n> inStarNullableTagless/NonEmpty"++show qIn) $
do (_,meAcceptingOut,_) <- actNullableTagless qIn (eLoop,Nothing,Nothing)
return (fmap getQT meAcceptingOut)
Test {} -> return Nothing
OneChar {} -> err ("OneChar cannot have nullable True")
act :: Q -> ActCont -> S (Maybe E)
act qIn c | nullable qIn = fmap snd3 $ actNullable qIn c
| otherwise = debug (">< act "++show qIn++" <>") $ do
mqt <- return . Just =<< getTrans qIn ( getE $ c )
return mqt
actNullable,actNullableTagless :: Q -> ActCont -> S ActCont
actNullable qIn@(Q {preReset=resets,postSet=sets,preTag=pre,postTag=post,unQ=pIn}) ac =
debug (">< actNullable "++show qIn++" <>") $ do
case pIn of
Empty -> return . addGroupResetsAC resets . addTagAC pre . addTagAC post . addGroupSetsAC sets $ ac
Test ti -> return . addGroupResetsAC resets . addTagAC pre . addTestAC ti . addTagAC post . addGroupSetsAC sets $ ac
OneChar {} -> err ("OneChar cannot have nullable True ")
_ -> return . addGroupResetsAC resets . addTagAC pre =<< actNullableTagless qIn ( addTagAC post . addGroupSetsAC sets $ ac )
actNullableTagless qIn ac@(eLoop,mAccepting,mQNFA) = debug (">< actNullableTagless "++show (qIn)++" <>") $ do
case unQ qIn of
Seq q1 q2 -> actNullable q1 =<< actNullable q2 ac
Or [] -> return ac
Or [q] -> actNullableTagless q ac
Or qs -> do
cqts <- do
if all nullable qs
then sequence [fmap snd3 $ actNullable q ac | q <- qs]
else do
e' <- asQNFA "qToNFA/actNullableTagless/Or" . getE $ ac
let act' :: Q -> S (Maybe E)
act' q = return . Just =<< getTrans q e'
sequence [ if nullable q then fmap snd3 $ actNullable q ac else act' q | q <- qs ]
let qts = map getQT (catMaybes cqts)
eLoop' = case maybeOnlyEmpty qIn of
Just wtags -> addWinTags wtags eLoop
Nothing -> fromQT $ applyNullViews (nullQ qIn) (getQT eLoop)
mAccepting' = if null qts
then fmap (fromQT . applyNullViews (nullQ qIn) . getQT) mAccepting
else Just (fromQT $ foldr1 mergeAltQT qts)
mQNFA' = if null qts
then case maybeOnlyEmpty qIn of
Just wtags -> fmap (addWinTags wtags) mQNFA
Nothing -> Nothing
else Nothing
return (eLoop',mAccepting',mQNFA')
Star mOrbit resetTheseOrbits mayFirstBeNull q -> do
let (ac0@(_,mAccepting0,_),clear) =
if notNullable q
then (ac,True)
else if null resetTheseOrbits && isNothing mOrbit
then case maybeOnlyEmpty q of
Just [] -> (ac,True)
Just wtags -> (addWinTagsAC wtags ac,False)
_ -> let nQ = fromQT . preferNullViews (nullQ q) . getQT
in ((nQ eLoop,fmap nQ mAccepting,Nothing),False)
else let nQ = fromQT . resetOrbitsQT resetTheseOrbits . enterOrbitQT mOrbit
. preferNullViews (nullQ q) . getQT . leaveOrbit mOrbit
in ((nQ eLoop,fmap nQ mAccepting,Nothing),False)
if cannotAccept q then return ac0 else mdo
mChildAccepting <- act q (this,Nothing,Nothing)
(thisAC@(this,_,_),ansAC) <-
case mChildAccepting of
Nothing -> err $ "Weird pattern in getTransTagless/Star: " ++ show (qTop,qIn)
Just childAccepting -> do
let childQT = resetOrbitsQT resetTheseOrbits . enterOrbitQT mOrbit . getQT $ childAccepting
thisQT = mergeQT childQT . getQT . leaveOrbit mOrbit . getE $ ac
thisAccepting =
case mAccepting of
Just futureAccepting -> Just . fromQT . mergeQT childQT . getQT $ futureAccepting
Nothing -> Just . fromQT $ childQT
thisAll <- if usesQNFA q
then do thisQNFA <- newQNFA "actNullableTagless/Star" thisQT
return (fromQNFA thisQNFA, thisAccepting, Just (mempty,thisQNFA))
else return (fromQT thisQT, thisAccepting, Nothing)
let skipQT = mergeQT childQT . getQT . getE $ ac0
skipAccepting =
case mAccepting0 of
Just futureAccepting0 -> Just . fromQT . mergeQT childQT . getQT $ futureAccepting0
Nothing -> Just . fromQT $ childQT
ansAll = (fromQT skipQT, skipAccepting, Nothing)
return (thisAll,ansAll)
return (if mayFirstBeNull then (if clear then thisAC else ansAC)
else thisAC)
NonEmpty q -> ecart ("\n> actNullableTagless/NonEmpty"++show qIn) $ do
when (mustAccept q) (err $ "actNullableTagless/NonEmpty : provided with a *mustAccept* pattern: "++show (qTop,qIn))
when (cannotAccept q) (err $ "actNullableTagless/NonEmpty : provided with a *cannotAccept* pattern: "++show (qTop,qIn))
let (clearE,_,_) = case maybeOnlyEmpty qIn of
Just [] -> ac
Just _wtags -> ac
Nothing -> err $ "actNullableTagless/NonEmpty is supposed to have an emptyNull nullView : "++show (qTop,qIn)
(_,mChildAccepting,_) <- actNullable q ac
case mChildAccepting of
Nothing -> err $ "Weird pattern in actNullableTagless/NonEmpty: " ++ show (qTop,qIn)
Just childAccepting -> do
let childQT = getQT childAccepting
thisAccepting = case mAccepting of
Nothing -> Just . fromQT $ childQT
Just futureAcceptingE -> Just . fromQT . mergeQT childQT . getQT $ futureAcceptingE
return (clearE,thisAccepting,Nothing)
_ -> err $ "This case in Text.Regex.TNFA.TNFA.actNullableTagless cannot happen: "++show (qTop,qIn)
resetOrbitsQT :: [Tag] -> QT -> QT
resetOrbitsQT | lastStarGreedy compOpt = const id
| otherwise = (\tags -> prependTags' [(tag,PreUpdate ResetOrbitTask)|tag<-tags])
enterOrbitQT :: Maybe Tag -> QT -> QT
enterOrbitQT | lastStarGreedy compOpt = const id
| otherwise = maybe id (\tag->prependTags' [(tag,PreUpdate EnterOrbitTask)])
leaveOrbit :: Maybe Tag -> E -> E
leaveOrbit | lastStarGreedy compOpt = const id
| otherwise = maybe id (\tag->(\(tags,cont)->((tag,LeaveOrbitTask):tags,cont)))
newTrans :: String
-> [Tag]
-> Maybe Tag
-> Pattern
-> E
-> S E
newTrans s resets mPre pat (tags,cont) = do
i <- case cont of
Left qnfa -> return (q_id qnfa)
Right qt -> do qnfa <- newQNFA s qt
return (q_id qnfa)
let post = promoteTasks PostUpdate tags
pre = promoteTasks PreUpdate ([(tag,ResetGroupStopTask) | tag<-resets] ++ maybe [] (\tag -> [(tag,TagTask)]) mPre)
return . fromQT $ acceptTrans pre pat post i
acceptTrans :: TagList -> Pattern -> TagList -> Index -> QT
acceptTrans pre pIn post i =
let target = IMap.singleton i [(getDoPa pIn,pre++post)]
in case pIn of
PChar _ char ->
let trans = toMap target [char]
in Simple { qt_win = mempty, qt_trans = trans, qt_other = mempty }
PEscape _ char ->
let trans = toMap target [char]
in Simple { qt_win = mempty, qt_trans = trans, qt_other = mempty }
PDot _ -> Simple { qt_win = mempty, qt_trans = dotTrans, qt_other = target }
PAny _ ps ->
let trans = toMap target . S.toAscList . decodePatternSet $ ps
in Simple { qt_win = mempty, qt_trans = trans, qt_other = mempty }
PAnyNot _ ps ->
let trans = toMap mempty . S.toAscList . addNewline . decodePatternSet $ ps
in Simple { qt_win = mempty, qt_trans = trans, qt_other = target }
_ -> err ("Cannot acceptTrans pattern "++show (qTop,pIn))
where
toMap :: IntMap [(DoPa,[(Tag, TagUpdate)])] -> [Char]
-> CharMap (IntMap [(DoPa,[(Tag, TagUpdate)])])
toMap dest | caseSensitive compOpt = CharMap . IMap.fromDistinctAscList . map (\c -> (ord c,dest))
| otherwise = CharMap . IMap.fromList . ($ [])
. foldr (\c dl -> if isAlpha c
then (dl.((ord (toUpper c),dest):)
.((ord (toLower c),dest):)
)
else (dl.((ord c,dest):))
) id
addNewline | multiline compOpt = S.insert '\n'
| otherwise = id
dotTrans | multiline compOpt = Map.singleton '\n' mempty
| otherwise = Mon.mempty
decodePatternSet :: PatternSet -> S.Set Char
decodePatternSet (PatternSet msc mscc _ msec) =
let baseMSC = maybe S.empty id msc
withMSCC = foldl (flip S.insert) baseMSC (maybe [] (concatMap decodeCharacterClass . S.toAscList) mscc)
withMSEC = foldl (flip S.insert) withMSCC (maybe [] (concatMap unSEC . S.toAscList) msec)
in withMSEC
decodeCharacterClass :: PatternSetCharacterClass -> String
decodeCharacterClass (PatternSetCharacterClass s) =
case s of
"alnum" -> ['0'..'9']++['a'..'z']++['A'..'Z']
"digit" -> ['0'..'9']
"punct" -> ['\33'..'\47']++['\58'..'\64']++['\91'..'\95']++"\96"++['\123'..'\126']
"alpha" -> ['a'..'z']++['A'..'Z']
"graph" -> ['\41'..'\126']
"space" -> "\t\n\v\f\r "
"blank" -> "\t "
"lower" -> ['a'..'z']
"upper" -> ['A'..'Z']
"cntrl" -> ['\0'..'\31']++"\127"
"print" -> ['\32'..'\126']
"xdigit" -> ['0'..'9']++['a'..'f']++['A'..'F']
"word" -> ['0'..'9']++['a'..'z']++['A'..'Z']++"_"
_ -> []