cpsa-3.6.8: src/CPSA/Annotations/Formulas.hs
-- Formulas used as annotations
-- Copyright (c) 2009 The MITRE Corporation
--
-- This program is free software: you can redistribute it and/or
-- modify it under the terms of the BSD License as published by the
-- University of California.
module CPSA.Annotations.Formulas (Formula, truth, true, says, implies,
freeVars, finstantiate, mapAccumLM, loadDecls, loadFormula,
displayFormula) where
import Control.Monad
import qualified Data.Set as S
import CPSA.Lib.Utilities
import CPSA.Lib.SExpr
import CPSA.Lib.Algebra
-- A CPSA formula is first-order logic augmented with the says modal
-- operator. True is And [], and false is Or [].
-- The formulas are constructed with the property that that every
-- bound variable is quantified once, and no free variable is also
-- quantified within a subformula. During the construction, each
-- variable bound by a forall or exists quantifier is freshly
-- generated.
data Formula t
= Predicate String [FTerm t]
| Not (Formula t)
| And [Formula t]
| Or [Formula t]
| Implies [Formula t] -- Null list not allowed
| Says t (Formula t)
| Forall [t] (Formula t)
| Exists [t] (Formula t)
deriving (Eq, Show)
-- A formula term is an algebra term or an application of a function
-- symbol to list of formula terms.
data FTerm t
= AlgTerm t
| Application String [FTerm t]
deriving (Eq, Show)
true :: Formula t
true = And []
truth :: Eq t => Formula t -> Bool
truth f = f == true
says :: Eq t => t -> Formula t -> Formula t
says t f
| truth f = true
| otherwise = Says t f
implies :: Eq t => [Formula t] -> Formula t -> Formula t
implies antecedents consequence
| truth consequence = true
| null simp = consequence
| otherwise = Implies (simp ++ [consequence])
where
simp = filter (not . truth) antecedents
-- Free variables in a formula
freeVars :: Algebra t p g s e c => Formula t -> S.Set t
freeVars f = freeVarsForm S.empty f
freeVarsForm :: Algebra t p g s e c => S.Set t -> Formula t -> S.Set t
freeVarsForm s (Predicate _ ts) = foldl freeVarsFTerm s ts
freeVarsForm s (Not f) = freeVarsForm s f
freeVarsForm s (And fs) = foldl freeVarsForm s fs
freeVarsForm s (Or fs) = foldl freeVarsForm s fs
freeVarsForm s (Implies fs) = foldl freeVarsForm s fs
freeVarsForm s (Says t f) = freeVarsForm (freeVarsTerm s t) f
freeVarsForm s (Forall ts f) = foldl (flip S.delete) (freeVarsForm s f) ts
freeVarsForm s (Exists ts f) = foldl (flip S.delete) (freeVarsForm s f) ts
freeVarsFTerm :: Algebra t p g s e c => S.Set t -> FTerm t -> S.Set t
freeVarsFTerm s (AlgTerm t) = freeVarsTerm s t
freeVarsFTerm s (Application _ ts) = foldl freeVarsFTerm s ts
freeVarsTerm :: Algebra t p g s e c => S.Set t -> t -> S.Set t
freeVarsTerm s t = foldVars (flip S.insert) s t
-- Formula instantiation
-- The implementations assumes the environment maps no bound
-- variables.
finstantiate :: Algebra t p g s e c => e -> Formula t -> Formula t
finstantiate env (Predicate sym ts) =
Predicate sym (map (tinstantiate env) ts)
finstantiate env (Not f) =
Not (finstantiate env f)
finstantiate env (And fs) =
And (map (finstantiate env) fs)
finstantiate env (Or fs) =
Or (map (finstantiate env) fs)
finstantiate env (Implies fs) =
Implies (map (finstantiate env) fs)
finstantiate env (Says t f) =
Says (instantiate env t) (finstantiate env f)
finstantiate env (Forall ts f) =
Forall ts (finstantiate env f)
finstantiate env (Exists ts f) =
Exists ts (finstantiate env f)
tinstantiate :: Algebra t p g s e c => e -> FTerm t -> FTerm t
tinstantiate env (AlgTerm t) =
AlgTerm (instantiate env t)
tinstantiate env (Application sym ts) =
Application sym (map (tinstantiate env) ts)
-- The follow translations are used on input and output so as to
-- reduce the number of cases used internally.
-- f == (or)
-- t == (and)
-- The follow translation is used on input to get rid of the form
-- (iff a b) == (and (implies a b) (implies b a))
-- Keywords used in the S-Expression representation of formulas
-- The following keywords may not be used as variables, function
-- symbols, or predicate symbols.
keywords :: [String]
keywords = ["forall", "exists", "says", "not",
"or", "and", "implies", "iff"]
-- Load a formula. The vars list is the list of variables that are in
-- scope. The variable generator for the algebra is threaded through
-- the loading process so that every bound variable is quantified
-- once, and no free variable is bound within a formula. Use
-- loadDecls to ensure vars are not keywords.
loadFormula :: (Algebra t p g s e c, MonadFail m) => [t] -> g ->
SExpr Pos -> m (g, Formula t)
loadFormula vars gen (L _ (S _ pred : xs)) -- Atomic formula
| notElem pred keywords =
do
fs <- loadFTerms vars xs
return (gen, Predicate pred fs)
loadFormula vars gen (L _ (S _ "or" : xs)) = -- Disjunction
do
(gen', ts) <- mapAccumLM (loadFormula vars) gen xs
return (gen', Or ts)
loadFormula vars gen (L _ (S _ "and" : xs)) = -- Conjunction
do
(gen', ts) <- mapAccumLM (loadFormula vars) gen xs
return (gen', And ts)
loadFormula vars gen (L _ [S _ "not", x]) = -- Negation
do
(gen', t) <- loadFormula vars gen x
return (gen', Not t)
loadFormula vars gen (L _ (S _ "implies" : x : xs)) = -- Implication
do
(gen', ts) <- mapAccumLM (loadFormula vars) gen (x : xs)
return (gen', Implies ts)
loadFormula vars gen (L pos [S pos' "iff", x, y]) = -- If and only if
loadFormula vars gen (L pos [S pos' "and",
L pos [S pos' "implies", x, y],
L pos [S pos' "implies", y, x]])
loadFormula vars gen (L _ [S _ "says", x, y]) = -- Says modal operator
do
t <- loadTerm vars False x
(gen', f) <- loadFormula vars gen y
return (gen', Says t f)
loadFormula vars gen (L _ [S _ "forall", L _ xs, x]) = -- Universal
loadQuantified Forall vars gen xs x -- quantification
loadFormula vars gen (L _ [S _ "exists", L _ xs, x]) = -- Existential
loadQuantified Exists vars gen xs x -- quantification
loadFormula _ _ x =
fail (shows (annotation x) "Malformed formula")
loadFTerms :: (Algebra t p g s e c, MonadFail m) => [t] ->
[SExpr Pos] -> m [FTerm t]
loadFTerms vars xs =
foldM f [] (reverse xs)
where
f acc x =
case loadFTerm vars x of
Return t -> return (t : acc)
Fail msg -> fail msg
-- Load a formula term
loadFTerm :: Algebra t p g s e c => [t] -> SExpr Pos ->
ReturnFail (FTerm t)
loadFTerm vars x =
case loadTerm vars False x of
Return t -> return (AlgTerm t)
Fail msg -> -- If x is not an algebra term
case x of -- see if it's an application
L _ (S pos fun : xs)
| elem fun keywords ->
Fail (shows pos $
"Keyword " ++ fun ++ " used as a function")
| otherwise ->
case mapM (loadFTerm vars) xs of
Return ts -> Return (Application fun ts)
Fail _ -> Fail msg
_ -> Fail msg
-- A monad version of map accumulation from the left
mapAccumLM :: Monad m => (a -> b -> m (a, c)) -> a -> [b] -> m (a, [c])
mapAccumLM _ z [] =
return (z, [])
mapAccumLM f z (x : xs) =
do
(z', y) <- f z x
(z'', ys) <- mapAccumLM f z' xs
return (z'', y : ys)
-- Load a quantified formula. Note that loadVars returns variables in
-- reverse order, so list append is the correct way to extend the list
-- of variables that are in scope.
loadQuantified :: (Algebra t p g s e c, MonadFail m) =>
([t] -> Formula t -> Formula t) ->
[t] -> g -> [SExpr Pos] -> SExpr Pos ->
m (g, Formula t)
loadQuantified build vars gen decls body =
do
(gen', vars') <- loadDecls gen decls
(gen'', f) <- loadFormula (vars' ++ vars) gen' body
return (gen'', build (reverse vars') f)
loadDecls :: (Algebra t p g s e c, MonadFail m) => g ->
[SExpr Pos] -> m (g, [t])
loadDecls gen decls =
do
mapM_ checkDecl decls -- Check for keywords
loadVars gen decls
-- Fail if a variable is used as a keyword
checkDecl :: MonadFail m => SExpr Pos -> m ()
checkDecl (L _ (S pos sym : _))
| elem sym keywords =
fail (shows pos "Keyword " ++ sym ++ " used as a variable")
checkDecl _ = return ()
-- Display a formula
displayFormula :: Algebra t p g s e c => [t] ->
Formula t -> SExpr ()
displayFormula vars form =
displayForm ctx' form
where -- Add quantified vars last so they are
ctx = addToContext emptyContext vars -- most like to be renamed.
ctx' = addToContext ctx (quantified form)
-- Collect the quantified variables
quantified :: Algebra t p g s e c => Formula t -> [t]
quantified (Predicate _ _) = []
quantified (Not f) = quantified f
quantified (And fs) = concatMap quantified fs
quantified (Or fs) = concatMap quantified fs
quantified (Implies fs) = concatMap quantified fs
quantified (Says _ f) = quantified f
quantified (Forall ts f) = ts ++ quantified f
quantified (Exists ts f) = ts ++ quantified f
displayForm :: Algebra t p g s e c => c -> Formula t -> SExpr ()
displayForm ctx (Predicate sym ts) =
L () (S () sym : map (displayFTerm ctx) ts)
displayForm ctx (Not f) =
L () [S () "not", displayForm ctx f]
displayForm ctx (And fs) =
L () (S () "and" : map (displayForm ctx) fs)
displayForm ctx (Or fs) =
L () (S () "or" : map (displayForm ctx) fs)
displayForm ctx (Implies fs) =
L () (S () "implies" : map (displayForm ctx) fs)
displayForm ctx (Says t f) =
L () [S () "says", displayTerm ctx t, displayForm ctx f]
displayForm ctx (Forall ts f) =
L () [S () "forall", L () (displayVars ctx ts), displayForm ctx f]
displayForm ctx (Exists ts f) =
L () [S () "exists", L () (displayVars ctx ts), displayForm ctx f]
displayFTerm :: Algebra t p g s e c => c -> FTerm t -> SExpr ()
displayFTerm ctx (AlgTerm t) =
displayTerm ctx t
displayFTerm ctx (Application sym ts) =
L () (S () sym : map (displayFTerm ctx) ts)