{-# LANGUAGE DeriveFunctor      #-}
{-# LANGUAGE DeriveFoldable     #-}
{-# LANGUAGE DeriveTraversable  #-}

module Data.Match where

import Control.Applicative
import Control.Monad
import Data.Foldable
import Data.Traversable

import qualified Data.Logic as Logic

data MatchExpr a
    = Match a
    | Not (MatchExpr a)
    | And (MatchExpr a) (MatchExpr a)
    | Or  (MatchExpr a) (MatchExpr a)
    deriving (Show, Eq, Ord, Functor, Foldable, Traversable)

instance Logic.Logic (MatchExpr a) where
    and = And
    or  = Or
    not = Not

match :: (a -> Bool) -> MatchExpr a -> Bool
match p = eval . test p

matchM :: (Applicative m, Monad m) => (a -> m Bool) -> MatchExpr a -> m Bool
matchM p = liftM eval . testM p

test :: (a -> Bool) -> MatchExpr a -> MatchExpr Bool
test = fmap

testM :: (Applicative m, Monad m) => (a -> m Bool) -> MatchExpr a -> m (MatchExpr Bool)
testM = traverse

eval :: MatchExpr Bool -> Bool
eval (Match x) = x
eval (Not   x) = not (eval x)
eval (And x y) = eval x && eval y
eval (Or  x y) = eval x || eval y
