diff options
author | Twan van Laarhoven <twanvl@gmail.com> | 2013-09-11 15:32:33 +0200 |
---|---|---|
committer | Herbert Valerio Riedel <hvr@gnu.org> | 2013-09-18 09:53:18 +0200 |
commit | bd42c9dfd1c9ce19672e1d63871a237c268e0212 (patch) | |
tree | 0d9e6e3fdedae39176751cc62c1458344a0cf781 /compiler/utils/BooleanFormula.hs | |
parent | b6bc3263d0099e79b437ac5f3c053452c608c710 (diff) | |
download | haskell-bd42c9dfd1c9ce19672e1d63871a237c268e0212.tar.gz |
Implement checkable "minimal complete definitions" (#7633)
This commit adds a `{-# MINIMAL #-}` pragma, which defines the possible
minimal complete definitions for a class. The body of the pragma is a
boolean formula of names.
The old warning for missing methods is replaced with this new one.
Note: The interface file format is changed to store the minimal complete
definition.
Authored-by: Twan van Laarhoven <twanvl@gmail.com>
Signed-off-by: Herbert Valerio Riedel <hvr@gnu.org>
Diffstat (limited to 'compiler/utils/BooleanFormula.hs')
-rw-r--r-- | compiler/utils/BooleanFormula.hs | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/compiler/utils/BooleanFormula.hs b/compiler/utils/BooleanFormula.hs new file mode 100644 index 0000000000..3e82e75b1f --- /dev/null +++ b/compiler/utils/BooleanFormula.hs @@ -0,0 +1,167 @@ +-------------------------------------------------------------------------------- +-- | Boolean formulas without negation (qunatifier free) + +{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, DeriveFoldable, + DeriveTraversable #-} + +module BooleanFormula ( + BooleanFormula(..), + mkFalse, mkTrue, mkAnd, mkOr, mkVar, + isFalse, isTrue, + eval, simplify, isUnsatisfied, + implies, impliesAtom, + pprBooleanFormula, pprBooleanFormulaNice + ) where + +import Data.List ( nub, intersperse ) +import Data.Data +import Data.Foldable ( Foldable ) +import Data.Traversable ( Traversable ) + +import MonadUtils +import Outputable +import Binary + +---------------------------------------------------------------------- +-- Boolean formula type and smart constructors +---------------------------------------------------------------------- + +data BooleanFormula a = Var a | And [BooleanFormula a] | Or [BooleanFormula a] + deriving (Eq, Data, Typeable, Functor, Foldable, Traversable) + +mkVar :: a -> BooleanFormula a +mkVar = Var + +mkFalse, mkTrue :: BooleanFormula a +mkFalse = Or [] +mkTrue = And [] + +mkBool :: Bool -> BooleanFormula a +mkBool False = mkFalse +mkBool True = mkTrue + +mkAnd :: Eq a => [BooleanFormula a] -> BooleanFormula a +mkAnd = maybe mkFalse (mkAnd' . nub) . concatMapM fromAnd + where + fromAnd :: BooleanFormula a -> Maybe [BooleanFormula a] + fromAnd (And xs) = Just xs + -- assume that xs are already simplified + -- otherwise we would need: fromAnd (And xs) = concat <$> traverse fromAnd xs + fromAnd (Or []) = Nothing -- in case of False we bail out, And [..,mkFalse,..] == mkFalse + fromAnd x = Just [x] + mkAnd' [x] = x + mkAnd' xs = And xs + +mkOr :: Eq a => [BooleanFormula a] -> BooleanFormula a +mkOr = maybe mkTrue (mkOr' . nub) . concatMapM fromOr + where + fromOr (Or xs) = Just xs + fromOr (And []) = Nothing + fromOr x = Just [x] + mkOr' [x] = x + mkOr' xs = Or xs + +---------------------------------------------------------------------- +-- Evaluation and simplificiation +---------------------------------------------------------------------- + +isFalse :: BooleanFormula a -> Bool +isFalse (Or []) = True +isFalse _ = False + +isTrue :: BooleanFormula a -> Bool +isTrue (And []) = True +isTrue _ = False + +eval :: (a -> Bool) -> BooleanFormula a -> Bool +eval f (Var x) = f x +eval f (And xs) = all (eval f) xs +eval f (Or xs) = any (eval f) xs + +-- Simplify a boolean formula. +-- The argument function should give the truth of the atoms, or Nothing if undecided. +simplify :: Eq a => (a -> Maybe Bool) -> BooleanFormula a -> BooleanFormula a +simplify f (Var a) = case f a of + Nothing -> Var a + Just b -> mkBool b +simplify f (And xs) = mkAnd (map (simplify f) xs) +simplify f (Or xs) = mkOr (map (simplify f) xs) + +-- Test if a boolean formula is satisfied when the given values are assigned to the atoms +-- if it is, returns Nothing +-- if it is not, return (Just remainder) +isUnsatisfied :: Eq a => (a -> Bool) -> BooleanFormula a -> Maybe (BooleanFormula a) +isUnsatisfied f bf + | isTrue bf' = Nothing + | otherwise = Just bf' + where + f' x = if f x then Just True else Nothing + bf' = simplify f' bf + +-- prop_simplify: +-- eval f x == True <==> isTrue (simplify (Just . f) x) +-- eval f x == False <==> isFalse (simplify (Just . f) x) + +-- If the boolean formula holds, does that mean that the given atom is always true? +impliesAtom :: Eq a => BooleanFormula a -> a -> Bool +Var x `impliesAtom` y = x == y +And xs `impliesAtom` y = any (`impliesAtom` y) xs -- we have all of xs, so one of them implying y is enough +Or xs `impliesAtom` y = all (`impliesAtom` y) xs + +implies :: Eq a => BooleanFormula a -> BooleanFormula a -> Bool +x `implies` Var y = x `impliesAtom` y +x `implies` And ys = all (x `implies`) ys +x `implies` Or ys = any (x `implies`) ys + +---------------------------------------------------------------------- +-- Pretty printing +---------------------------------------------------------------------- + +pprBooleanFormula' :: (Rational -> a -> SDoc) + -> (Rational -> [SDoc] -> SDoc) + -> (Rational -> [SDoc] -> SDoc) + -> Rational -> BooleanFormula a -> SDoc +pprBooleanFormula' pprVar pprAnd pprOr = go + where + go p (Var x) = pprVar p x + go p (And []) = cparen (p > 0) $ empty + go p (And xs) = pprAnd p (map (go 3) xs) + go _ (Or []) = keyword $ text "FALSE" + go p (Or xs) = pprOr p (map (go 2) xs) + +-- Pretty print in source syntax, "a | b | c,d,e" +pprBooleanFormula :: (Rational -> a -> SDoc) -> Rational -> BooleanFormula a -> SDoc +pprBooleanFormula pprVar = pprBooleanFormula' pprVar pprAnd pprOr + where + pprAnd p = cparen (p > 3) . fsep . punctuate comma + pprOr p = cparen (p > 2) . fsep . intersperse (text "|") + +-- Pretty print human in readable format, "either `a' or `b' or (`c', `d' and `e')"? +pprBooleanFormulaNice :: Outputable a => BooleanFormula a -> SDoc +pprBooleanFormulaNice = pprBooleanFormula' pprVar pprAnd pprOr 0 + where + pprVar _ = quotes . ppr + pprAnd p = cparen (p > 1) . pprAnd' + pprAnd' [] = empty + pprAnd' [x,y] = x <+> text "and" <+> y + pprAnd' xs@(_:_) = fsep (punctuate comma (init xs)) <> text ", and" <+> last xs + pprOr p xs = cparen (p > 1) $ text "either" <+> sep (intersperse (text "or") xs) + +instance Outputable a => Outputable (BooleanFormula a) where + pprPrec = pprBooleanFormula pprPrec + +---------------------------------------------------------------------- +-- Binary +---------------------------------------------------------------------- + +instance Binary a => Binary (BooleanFormula a) where + put_ bh (Var x) = putByte bh 0 >> put_ bh x + put_ bh (And xs) = putByte bh 1 >> put_ bh xs + put_ bh (Or xs) = putByte bh 2 >> put_ bh xs + + get bh = do + h <- getByte bh + case h of + 0 -> Var <$> get bh + 1 -> And <$> get bh + _ -> Or <$> get bh |