module Autoproc.Transform (generate) where
import qualified Autoproc.Procmail as Pm
import qualified Autoproc.Classifier as Cf
import Data.List (nub)
transformFlag :: Cf.Flag -> Pm.RecipeFlag
transformFlag Cf.Copy = Pm.Copy
transformFlag Cf.Wait = Pm.Wait
transformFlag Cf.IgnoreErrors = Pm.IgnoreErrors
transformFlag (Cf.NeedLock b) = (Pm.NeedLock b)
transformFlag Cf.Chain = Pm.Chain
transformFlag Cf.CaseSensitive = Pm.CaseSensitive
transformCond :: Cf.Cond -> [Pm.Condition]
transformCond (Cf.Or _ _) = error "transformCond cannot handle Or."
transformCond (Cf.And c1 c2) = transformCond c1 ++ transformCond c2
transformCond (Cf.Not c) = [Pm.Condition Pm.Invert c']
where [Pm.Condition _ c'] = transformCond c
transformCond Cf.Always = [Pm.Condition Pm.Normal []]
transformCond (Cf.CheckHeader s) = [Pm.Condition Pm.Normal s]
transformCond (Cf.CheckBody s) = [Pm.Condition Pm.Normal s]
transformCond (Cf.CheckMatch s) = [Pm.Condition (Pm.Var "$MATCH") s]
transformAct :: Cf.Act -> Pm.Action
transformAct (Cf.File s) = Pm.File s
transformAct (Cf.Filter s) = Pm.Pipe s
transformAct (Cf.Fwd es) = Pm.Forward (map unAddress es)
where unAddress (Cf.Addr a) = a
transformAct (Cf.Nest as) = Pm.Nest (map transform as)
distributeNot :: Cf.Cond -> Cf.Cond
distributeNot (Cf.Not (Cf.And c1 c2)) = Cf.Or (distributeNot (Cf.Not c1))
(distributeNot (Cf.Not c2))
distributeNot (Cf.Not (Cf.Or c1 c2)) = Cf.And (distributeNot (Cf.Not c1))
(distributeNot (Cf.Not c2))
distributeNot (Cf.Not (Cf.Not c)) = distributeNot c
distributeNot (Cf.And c1 c2) = Cf.And (distributeNot c1)
(distributeNot c2)
distributeNot (Cf.Or c1 c2) = Cf.Or (distributeNot c1)
(distributeNot c2)
distributeNot c = c
factor :: Cf.Cond -> Cf.Cond
factor (Cf.And (Cf.Or c1 c2) c3) = (Cf.Or (Cf.And (factor c1) (factor c3))
(Cf.And (factor c2) (factor c3)))
factor (Cf.And c1 (Cf.Or c2 c3)) = (Cf.Or (Cf.And (factor c1) (factor c2))
(Cf.And (factor c1) (factor c3)))
factor (Cf.Or c1 c2) = (Cf.Or (factor c1) (factor c2))
factor (Cf.And c1 c2) = (Cf.And (factor c1) (factor c2))
factor c = c
repeated :: (Cf.Cond -> Cf.Cond) -> Cf.Cond -> Cf.Cond
repeated t c = loop c
where loop c' = if c' == (t c') then c'
else repeated t (t c')
reduceOr :: Cf.CExp -> [Cf.CExp]
reduceOr (Cf.CExp fs (Cf.Or c1 c2) a) = (reduceOr (Cf.CExp fs c1 a)) ++
(reduceOr (Cf.CExp fs c2 a))
reduceOr x = [x]
simplify :: Cf.CExp -> [Cf.CExp]
simplify (Cf.CExp fs c a) = reduceOr (Cf.CExp fs c'' a)
where
c' = repeated distributeNot c
c'' = repeated factor c'
transform :: Cf.CExp -> Pm.PExp
transform (Cf.CExp _ (Cf.Or _ _) _) = error "use simplify."
transform (Cf.CExp fs c a) = Pm.PExp (nub fs') (transformCond c)
(transformAct a)
where
fs' = (if any' then [Pm.CheckHeader,Pm.CheckBody] else
if body then [Pm.CheckBody] else
if header then [Pm.CheckHeader] else [])++newFlags
body = checksBody c
header = checksHeader c
any' = checksAny c
newFlags = (if isFilter a then [Pm.Wait, Pm.PipeAsFilter] else [])++
(map transformFlag fs)
isFilter :: Cf.Act -> Bool
isFilter (Cf.Filter _) = True
isFilter _ = False
checksHeader :: Cf.Cond -> Bool
checksHeader (Cf.And c1 c2) = checksHeader c1 || checksHeader c2
checksHeader (Cf.Or c1 c2) = checksHeader c1 || checksHeader c2
checksHeader (Cf.Not c) = checksHeader c
checksHeader (Cf.CheckHeader _) = True
checksHeader _ = False
checksBody :: Cf.Cond -> Bool
checksBody (Cf.And c1 c2) = checksBody c1 || checksBody c2
checksBody (Cf.Or c1 c2) = checksBody c1 || checksBody c2
checksBody (Cf.Not c) = checksBody c
checksBody (Cf.CheckBody _) = True
checksBody _ = False
checksAny :: Cf.Cond -> Bool
checksAny c = checksHeader c && checksBody c
generate :: Cf.CExp -> [Pm.PExp]
generate c = map transform (simplify c)