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] }