packages feed

logic-classes-0.44: Data/Logic/Classes/Literal.hs

{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables #-}
{-# OPTIONS -Wwarn #-}
module Data.Logic.Classes.Literal
    ( Literal(..)
    , PredicateLit(..)
    , fromFirstOrder
    , prettyLit
    ) where

import Data.Generics (Data)
import Data.List (intersperse)
import Data.Logic.Classes.Boolean
import Data.Logic.Classes.FirstOrder
import Data.Logic.Classes.Negatable
import Data.Logic.Classes.Propositional (Combine(..))
import Data.Logic.Classes.Term
import Text.PrettyPrint (Doc, (<>), (<+>), text, empty, parens, hcat, nest)

-- |Literals are the building blocks of the clause and implicative normal
-- |forms.  They support negation and must include True and False elements.
class ( Ord lit
      , Eq p
      , Ord p
      , Boolean p
      , Term term v f
      , Negatable lit
      , Data lit
      , Show lit
      ) => Literal lit term v p f | lit -> term, lit -> v, lit -> p, lit -> f where
    equals :: term -> term -> lit
    pAppLiteral :: p -> [term] -> lit
    foldLiteral :: (lit -> r) -> (PredicateLit p term -> r) -> lit -> r
    zipLiterals :: (lit -> lit -> Maybe r) -> (PredicateLit p term -> PredicateLit p term -> Maybe r) -> lit -> lit -> Maybe r

-- |Helper type to implement the fold function for 'Literal'.
data PredicateLit p term
    = ApplyLit p [term]
    | EqualLit term term

-- |Just like Logic.FirstOrder.convertFOF except it rejects anything
-- with a construct unsupported in a normal logic formula,
-- i.e. quantifiers and formula combinators other than negation.
fromFirstOrder :: forall formula term v p f lit term2 v2 p2 f2.
                  (FirstOrderFormula formula term v p f, Literal lit term2 v2 p2 f2) =>
                  (v -> v2) -> (p -> p2) -> (f -> f2) -> formula -> lit
fromFirstOrder cv cp cf formula =
    foldFirstOrder (\ _ _ _ -> error "toLiteral q") c p formula
    where
      c :: Combine formula -> lit
      c ((:~:) f) =  (.~.) (fromFirstOrder cv cp cf f)
      c _ = error "fromFirstOrder"
      p :: Predicate p term -> lit
      p (Equal a b) = ct a `equals` ct b
      p (NotEqual a b) = (.~.) (ct a `equals` ct b)
      p (Apply pr ts) = pAppLiteral (cp pr) (map ct ts)
      p (Constant b) = error $ "fromFirstOrder " ++ show b
      ct = convertTerm cv cf

prettyLit :: forall lit term v p f. (Literal lit term v p f) =>
              (v -> Doc)
           -> (p -> Doc)
           -> (f -> Doc)
           -> Int
           -> lit
           -> Doc
prettyLit pv pp pf prec lit =
    foldLiteral c p lit
    where
      c x = if negated x then text {-"¬"-} "~" <> prettyLit pv pp pf 5 x else prettyLit pv pp pf 5 x
      p (ApplyLit pr ts) =
          pp pr <> case ts of
                        [] -> empty
                        _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts)))
      p (EqualLit t1 t2) = parensIf (prec > 6) (prettyTerm pv pf t1 <+> text "=" <+> prettyTerm pv pf t2)
      parensIf False = id
      parensIf _ = parens . nest 1