packages feed

cg-0.0.9.0: CG/Prover/Base.hs

{-# LANGUAGE GADTs, RankNTypes, FlexibleInstances, FlexibleContexts,
    UndecidableInstances, DeriveGeneric, DeriveFunctor, DeriveFoldable,
    DeriveTraversable, StandaloneDeriving, RecordWildCards #-}
module CG.Prover.Base where


import Control.Monad.State (MonadState(..),evalState)
import Control.DeepSeq     (NFData)
import Data.Char           (ord)
import Data.Hashable       (Hashable)
import Data.Serialize      (Serialize)
import Data.IntMap as IM   (IntMap,lookup,empty,insert)
import Data.List           (intersperse)
import Data.Void           (Void,absurd)
import Data.Void.Unsafe    (unsafeVacuous)
import GHC.Generics        (Generic)


-- * Special Show instance for String

class    ToString a      where toString :: a -> String
instance ToString Bool   where toString = (++" ") . show
instance ToString Int    where toString = (++" ") . show
instance ToString Void   where toString = absurd
instance ToString String where toString = (++" ")
instance ToString Char   where toString = (:" ")


-- * Terms

data Term c v where
  Var :: ! v               -> Term c v
  Con :: ! c -> [Term c v] -> Term c v
  deriving (Eq,Ord,Generic)


deriving instance Functor (Term c)
deriving instance Foldable (Term c)
deriving instance Traversable (Term c)
instance (Hashable  c, Hashable  v) => Hashable  (Term c v)
instance (NFData    c, NFData    v) => NFData    (Term c v)
instance (Serialize c, Serialize v) => Serialize (Term c v)


-- |A class which defines a printable operator. The @prec@ function
--  returns the precedence of the operator. The @template@ function
--  is a generalisation of the fixity, where the operator can be
--  anything that knows how to print itself.
class Operator a where
  prec     :: a -> Int
  template :: a -> ([ShowS] -> ShowS)


instance Operator String where
  prec     _    = 0
  template x [] = showString x
  template x xs =
    showParen True (foldr (.) id (intersperse (showChar ' ') (showString x : xs)))


instance (Operator c, ToString v) => Show (Term c v) where
  showsPrec _ (Var i)    =
    showString (toString i)
  showsPrec p (Con x xs)
    | p > 0 && q > 0 && p >= q = showString "( " . ret . showString ") "
    | otherwise                = ret
    where
      q   = prec x
      ret = template x (map (showsPrec q) xs)


-- * Guards

class (Eq c) => Guardable c where
  isAtomic   :: Term c v -> Bool
  isPositive :: Term c v -> Bool
  isNegative :: Term c v -> Bool


data Guard c
  = Any
  | Atomic   (Term c Bool)
  | Positive (Term c Bool)
  | Negative (Term c Bool)
  | And (Guard c) (Guard c)
  deriving (Eq,Ord,Show,Generic)


instance (Hashable  c) => Hashable  (Guard c)
instance (NFData    c) => NFData    (Guard c)
instance (Serialize c) => Serialize (Guard c)


-- |Construct a given primitive guard, together with its arguments, into a
--  guarding function. Used internally in @runGuard@.
mkGuard :: (Guardable c)
           => (forall v'. Term c v' -> Bool)
           -> Term c Bool
           -> (forall v'. Term c v' -> Bool)
mkGuard _      _     (Var _)             = True
mkGuard p (Var True)      y              = p y
mkGuard _ (Var _   )      _              = True
mkGuard p (Con x xs) (Con y ys) | x == y = and (zipWith (mkGuard p) xs ys)
mkGuard _      _          _              = False


-- |Run a guard, checking if the given Term has all the desired properties.
runGuard :: (Guardable c) => Guard c -> (forall v. Term c v -> Bool)
runGuard  Any         = const True
runGuard (Atomic   c) = mkGuard isAtomic   c
runGuard (Positive c) = mkGuard isPositive c
runGuard (Negative c) = mkGuard isNegative c
runGuard (gd `And` gd2) = \x -> runGuard gd x && runGuard gd2 x


-- |Smart constructors for guards.
atomic, positive, negative :: (Eq v) => v -> Term c v -> Guard c
atomic   x = Atomic   . fmap (==x)
positive x = Positive . fmap (==x)
negative x = Negative . fmap (==x)


-- * Rules

type RuleId = String

data Rule c v = Rule
  { name       :: ! RuleId
  , guard      :: ! (Guard c)
  , arity      :: ! Int
  , premises   :: ! [Term c v]
  , conclusion :: ! (Term c v)
  }
  deriving (Eq,Ord,Generic)


deriving instance Functor (Rule c)
deriving instance Foldable (Rule c)
deriving instance Traversable (Rule c)
instance (Hashable  c, Hashable  v) => Hashable  (Rule c v)
instance (NFData    c, NFData    v) => NFData    (Rule c v)
instance (Serialize c, Serialize v) => Serialize (Rule c v)


-- |Construct a @Rule@ from a @RuleId@, a list of premises and a conclusion.
mkRule :: RuleId -> [Term c Char] -> Term c Char -> Rule c Int
mkRule n ps c = Rule n Any (length ps) ps' c'
  where
    (c' : ps') = evalState (mapM (mapM label) (c : ps)) (0, IM.empty)

    label x =
      do (i, vm) <- get
         case IM.lookup (ord x) vm of
          Just j -> return j
          _      -> do put (i + 1, IM.insert (ord x) i vm); return i



instance (Show c, ToString v, Show (Term c v)) => Show (Rule c v) where
  showsPrec _ (Rule n g _ ps c) =
    (showString (n++" : ")) .
    (foldr1 (.) (intersperse (showString "→ ") (map shows (ps ++ [c]))))


-- * Substitutions

type VMap c v = IntMap (Term c v)


-- |Apply the given variable map to a given term. Note: the variable
--  map has to contain a term for every variable used in the given
--  term. The resulting term will be variable-free.
subst :: VMap c Void -> Term c Int -> Maybe (Term c Void)
subst s = app where

  app (Con x xs) = Con x <$> mapM app xs
  app (Var i   ) = IM.lookup i s


-- * Systems

data System c = System
  { finite     :: Bool
  , structural :: Bool
  , rules      :: [Rule c Int]

    -- options related to parsing
  , unaryOp    :: Maybe c
  , binaryOp   :: [c]

    -- options related to Agda generation
  , agdaName   :: Maybe String
  , agdaModule :: Maybe String
  }
  deriving (Eq,Ord,Show,Generic)


instance (Hashable  c) => Hashable  (System c)
instance (NFData    c) => NFData    (System c)
instance (Serialize c) => Serialize (System c)


-- |The minimal system. All it is missing is a binary operator.
emptySystem :: System c
emptySystem = System
  { finite     = False
  , structural = False
  , rules      = []

  , unaryOp    = Nothing
  , binaryOp   = []

  , agdaName   = Nothing
  , agdaModule = Nothing
  }

addUnary :: c -> System c -> System c
addUnary op sys = case unaryOp sys of
  Nothing -> sys { unaryOp = Just op }
  Just _  -> error "Cannot parse using more than one unary operator."

addBinary :: c -> System c -> System c
addBinary op sys@System{..} = sys { binaryOp = binaryOp ++ [op] }

addRule :: Rule c Int -> System c -> System c
addRule rule sys@System{..} = sys { rules = rules ++ [rule] }