{-#LANGUAGE GADTs #-}
module Data.Katydid.Relapse.Simplify (
simplify
) where
import qualified Data.Set as S
import Data.Katydid.Relapse.Ast
import Data.Katydid.Relapse.Expr
import Data.Katydid.Relapse.Exprs.Logic
simplify :: Grammar -> Pattern -> Pattern
simplify g pat =
let simp = simplify' g
in case pat of
Empty -> Empty
ZAny -> ZAny
(Node v p) -> simplifyNode v (simp p)
(Concat p1 p2) -> simplifyConcat (simp p1) (simp p2)
(Or p1 p2) -> simplifyOr g (simp p1) (simp p2)
(And p1 p2) -> simplifyAnd g (simp p1) (simp p2)
(ZeroOrMore p) -> simplifyZeroOrMore (simp p)
(Not p) -> simplifyNot (simp p)
(Optional p) -> simplifyOptional (simp p)
(Interleave p1 p2) -> simplifyInterleave (simp p1) (simp p2)
(Contains p) -> simplifyContains (simp p)
p@(Reference _) -> p
simplify' :: Grammar -> Pattern -> Pattern
simplify' g p = checkRef g $ simplify g p
simplifyNode :: Expr Bool -> Pattern -> Pattern
simplifyNode v p = case evalConst v of
(Just False) -> Not ZAny
_ -> Node v p
simplifyConcat :: Pattern -> Pattern -> Pattern
simplifyConcat (Not ZAny) _ = Not ZAny
simplifyConcat _ (Not ZAny) = Not ZAny
simplifyConcat (Concat p1 p2) p3 =
simplifyConcat p1 (Concat p2 p3)
simplifyConcat Empty p = p
simplifyConcat p Empty = p
simplifyConcat ZAny (Concat p ZAny) = Contains p
simplifyConcat p1 p2 = Concat p1 p2
simplifyOr :: Grammar -> Pattern -> Pattern -> Pattern
simplifyOr _ (Not ZAny) p = p
simplifyOr _ p (Not ZAny) = p
simplifyOr _ ZAny _ = ZAny
simplifyOr _ _ ZAny = ZAny
simplifyOr _ (Node v1 Empty) (Node v2 Empty) = Node (orExpr v1 v2) Empty
simplifyOr g Empty p
| nullable g p == Right True = p
| otherwise = Or Empty p
simplifyOr g p Empty
| nullable g p == Right True = p
| otherwise = Or Empty p
simplifyOr _ p1 p2 = bin Or $ simplifyChildren Or $ S.toAscList $ setOfOrs p1 `S.union` setOfOrs p2
simplifyChildren :: (Pattern -> Pattern -> Pattern) -> [Pattern] -> [Pattern]
simplifyChildren _ [] = []
simplifyChildren _ [p] = [p]
simplifyChildren op (p1@(Node v1 c1):(p2@(Node v2 c2):ps))
| v1 == v2 = simplifyChildren op $ Node v1 (op c1 c2):ps
| otherwise = p1:simplifyChildren op (p2:ps)
simplifyChildren op (p:ps) = p:simplifyChildren op ps
bin :: (Pattern -> Pattern -> Pattern) -> [Pattern] -> Pattern
bin op [p] = p
bin op [p1,p2] = op p1 p2
bin op (p:ps) = op p (bin op ps)
setOfOrs :: Pattern -> S.Set Pattern
setOfOrs (Or p1 p2) = setOfOrs p1 `S.union` setOfOrs p2
setOfOrs p = S.singleton p
simplifyAnd :: Grammar -> Pattern -> Pattern -> Pattern
simplifyAnd _ (Not ZAny) _ = Not ZAny
simplifyAnd _ _ (Not ZAny) = Not ZAny
simplifyAnd _ ZAny p = p
simplifyAnd _ p ZAny = p
simplifyAnd _ (Node v1 Empty) (Node v2 Empty) = Node (andExpr v1 v2) Empty
simplifyAnd g Empty p
| nullable g p == Right True = Empty
| otherwise = Not ZAny
simplifyAnd g p Empty
| nullable g p == Right True = Empty
| otherwise = Not ZAny
simplifyAnd _ p1 p2 = bin And $ simplifyChildren And $ S.toAscList $ setOfAnds p1 `S.union` setOfAnds p2
setOfAnds :: Pattern -> S.Set Pattern
setOfAnds (And p1 p2) = setOfAnds p1 `S.union` setOfAnds p2
setOfAnds p = S.singleton p
simplifyZeroOrMore :: Pattern -> Pattern
simplifyZeroOrMore (ZeroOrMore p) = ZeroOrMore p
simplifyZeroOrMore p = ZeroOrMore p
simplifyNot :: Pattern -> Pattern
simplifyNot (Not p) = p
simplifyNot p = Not p
simplifyOptional :: Pattern -> Pattern
simplifyOptional Empty = Empty
simplifyOptional p = Optional p
simplifyInterleave :: Pattern -> Pattern -> Pattern
simplifyInterleave (Not ZAny) _ = Not ZAny
simplifyInterleave _ (Not ZAny) = Not ZAny
simplifyInterleave Empty p = p
simplifyInterleave p Empty = p
simplifyInterleave ZAny ZAny = ZAny
simplifyInterleave p1 p2 = bin Interleave $ S.toAscList $ setOfInterleaves p1 `S.union` setOfInterleaves p2
setOfInterleaves :: Pattern -> S.Set Pattern
setOfInterleaves (Interleave p1 p2) = setOfInterleaves p1 `S.union` setOfInterleaves p2
setOfInterleaves p = S.singleton p
simplifyContains :: Pattern -> Pattern
simplifyContains Empty = ZAny
simplifyContains ZAny = ZAny
simplifyContains (Not ZAny) = Not ZAny
simplifyContains p = Contains p
checkRef :: Grammar -> Pattern -> Pattern
checkRef g p = case reverseLookupRef p g of
Nothing -> p
(Just k) -> Reference k