Description : Static analysis of CspCASL
Copyright : (c) Andy Gimblett, Markus Roggenbach, Swansea University 2008
Maintainer : a.m.gimblett@swansea.ac.uk
Static analysis of CSP-CASL specifications, following "Tool Support
for CSP-CASL", MPhil thesis by Andy Gimblett, 2008.
resolveFormula, resolveMixfix, unite)
import
Common.Id (getRange, Id, simpleIdToId, nullRange, mkSimpleId)
basicAnalysisCspCASL :: (CspBasicSpec, CspCASLSign, GlobalAnnos)
-> Result (CspBasicSpec, ExtSign CspCASLSign (), [Named CspCASLSentence])
basicAnalysisCspCASL (cc, sigma, ga) = do
let Result es mga = mergeGlobalAnnos ga $ globAnnos sigma
(_, accSig) = runState (ana_BASIC_CSP cc) $ case mga of
Just nga -> sigma { globAnnos = nga }
ds = reverse $ envDiags accSig
Result (es ++ ds) (Just ()) -- insert diagnostics
return (cc, mkExtSign accSig, [makeNamed "empty_sentence" emptyCCSentence, makeNamed "name_testProc" testProc])
where testProc = CspCASLSentence (mkSimpleId "testProc") [] (ExternalChoice (Stop nullRange) (Stop nullRange) nullRange )
ana_BASIC_CSP :: CspBasicSpec -> State CspCASLSign ()
ana_BASIC_CSP cc = do checkLocalTops
mapM anaChanDecl (channels cc)
mapM anaProcItem (proc_items cc)
-- Analysis of local top elements
-- | Check a CspCASL signature for local top elements in its subsort
checkLocalTops :: State CspCASLSign ()
addDiags (map lteError obs)
-- | Add diagnostic error message for every unmet local top element
lteError :: Obligation SORT -> Diagnosis
lteError (Obligation x y z) = mkDiag Error msg ()
where msg = ("local top element obligation ("
++ (show x) ++ "<" ++ (show y) ++ "," ++ (show z)
-- Static analysis of channel declarations
-- | Statically analyse a CspCASL channel declaration.
anaChanDecl :: CHANNEL_DECL -> State CspCASLSign CHANNEL_DECL
anaChanDecl (ChannelDecl chanNames chanSort) = do
checkSorts [chanSort] -- check channel sort is known
let ext = extendedInfo sig
newChanMap <-
Monad.foldM (anaChannelName chanSort) oldChanMap chanNames
put sig { extendedInfo = ext { chans = newChanMap }
return (ChannelDecl chanNames chanSort)
-- | Statically analyse a CspCASL channel name.
anaChannelName :: SORT -> ChanNameMap -> CHANNEL_NAME ->
State CspCASLSign ChanNameMap
anaChannelName s m chanName = do
then do let err = "channel name already in use as a sort name"
addDiags [mkDiag Error err chanName]
Nothing -> return (
Map.insert chanName s m) -- insert new.
then do let warn = "channel redeclared with same sort"
addDiags [mkDiag Warning warn chanName]
return m -- already declared with this sort.
else do let err = "channel declared with multiple sorts"
addDiags [mkDiag Error err chanName]
-- Static analysis of process items
-- | Statically analyse a CspCASL process item.
anaProcItem :: PROC_ITEM -> State CspCASLSign ()
(Proc_Decl name argSorts alpha) -> anaProcDecl name argSorts alpha
(Proc_Eq parmProcName procTerm) -> anaProcEq parmProcName procTerm
-- Static analysis of process declarations
-- | Statically analyse a CspCASL process declaration.
anaProcDecl :: PROCESS_NAME -> PROC_ARGS -> PROC_ALPHABET
anaProcDecl name argSorts (ProcAlphabet commTypes _) = do
let ext = extendedInfo sig
oldProcDecls = procSet ext
then do -- duplicate process declaration
let err = "process name declared more than once"
addDiags [mkDiag Error err name]
else do -- new process declation
checkSorts argSorts -- check argument sorts are known
-- build alphabet: set of CommType values
let profile = (ProcProfile argSorts alpha)
put sig { extendedInfo = ext {procSet = newProcDecls }
-- Static analysis of process equations
-- | Statically analyse a CspCASL process equation.
anaProcEq :: PARM_PROCNAME -> PROCESS -> State CspCASLSign ()
anaProcEq (ParmProcname pn vs) proc = do
let ext = extendedInfo sig
-- Only analyse a process if its name (and thus profile) is known
Just (ProcProfile procArgs procAlpha) ->
do gVars <- anaProcVars pn procArgs vs -- compute global vars
termAlpha <- anaProcTerm proc gVars
Map.empty checkCommAlphaSub termAlpha procAlpha proc "process equation"
do addDiags [mkDiag Error "process equation for unknown process" pn]
put sig { envDiags = vds }
-- | Statically analyse a CspCASL process equation's global variable
anaProcVars :: PROCESS_NAME -> [SORT] -> [VAR] -> State CspCASLSign ProcVarMap
anaProcVars pn ss vs = do
case (compare (length ss) (length vs)) of
LT -> do addDiags [mkDiag Error "too many process arguments" pn]
GT -> do addDiags [mkDiag Error "not enough process arguments" pn]
-- | Statically analyse a CspCASL process-global variable name.
anaProcVar :: ProcVarMap -> (VAR, SORT) -> State CspCASLSign ProcVarMap
anaProcVar old (v, s) = do
then do addDiags [mkDiag Error "process arg declared more than once" v]
-- Static analysis of process terms
-- | Statically analyse a CspCASL process term.
anaProcTerm :: PROCESS -> ProcVarMap -> ProcVarMap -> State CspCASLSign CommAlpha
anaProcTerm proc gVars lVars = case proc of
NamedProcess name args _ ->
do addDiags [mkDiag Debug "Named process" proc]
al <- anaNamedProc proc name args (lVars `
Map.union` gVars)
do addDiags [mkDiag Debug "Skip" proc]
do addDiags [mkDiag Debug "Stop" proc]
do addDiags [mkDiag Debug "Div" proc]
do addDiags [mkDiag Debug "Run" proc]
do addDiags [mkDiag Debug "Chaos" proc]
do addDiags [mkDiag Debug "Prefix" proc]
(evComms, rcvMap) <- anaEvent e (lVars `
Map.union` gVars)
comms <- anaProcTerm p gVars (rcvMap `
Map.union` lVars)
InternalPrefixProcess v s p _ ->
do addDiags [mkDiag Debug "Internal prefix" proc]
checkSorts [s] -- check sort is known
comms <- anaProcTerm p gVars (
Map.insert v s lVars)
return (
S.insert (CommTypeSort s) comms)
ExternalPrefixProcess v s p _ ->
do addDiags [mkDiag Debug "External prefix" proc]
checkSorts [s] -- check sort is known
comms <- anaProcTerm p gVars (
Map.insert v s lVars)
return (
S.insert (CommTypeSort s) comms)
do addDiags [mkDiag Debug "Sequential" proc]
pComms <- anaProcTerm p gVars lVars
do addDiags [mkDiag Debug "InternalChoice" proc]
pComms <- anaProcTerm p gVars lVars
qComms <- anaProcTerm q gVars lVars
do addDiags [mkDiag Debug "ExternalChoice" proc]
pComms <- anaProcTerm p gVars lVars
qComms <- anaProcTerm q gVars lVars
do addDiags [mkDiag Debug "Interleaving" proc]
pComms <- anaProcTerm p gVars lVars
qComms <- anaProcTerm q gVars lVars
SynchronousParallel p q _ ->
do addDiags [mkDiag Debug "Synchronous" proc]
pComms <- anaProcTerm p gVars lVars
qComms <- anaProcTerm q gVars lVars
GeneralisedParallel p es q _ ->
do addDiags [mkDiag Debug "Generalised parallel" proc]
pComms <- anaProcTerm p gVars lVars
synComms <- anaEventSet es
qComms <- anaProcTerm q gVars lVars
return (
S.unions [pComms, qComms, synComms])
AlphabetisedParallel p esp esq q _ ->
do addDiags [mkDiag Debug "Alphabetised parallel" proc]
pComms <- anaProcTerm p gVars lVars
pSynComms <- anaEventSet esp
checkCommAlphaSub pSynComms pComms proc "alphabetised parallel, left"
qSynComms <- anaEventSet esq
qComms <- anaProcTerm q gVars lVars
checkCommAlphaSub qSynComms qComms proc "alphabetised parallel, right"
do addDiags [mkDiag Debug "Hiding" proc]
pComms <- anaProcTerm p gVars lVars
hidComms <- anaEventSet es
do addDiags [mkDiag Debug "Renaming" proc]
pComms <- anaProcTerm p gVars lVars
renAlpha <- anaRenaming r
ConditionalProcess f p q _ ->
do addDiags [mkDiag Debug "Conditional" proc]
pComms <- anaProcTerm p gVars lVars
qComms <- anaProcTerm q gVars lVars
mfs <- anaFormulaCspCASL (gVars `
Map.union` lVars) f
Just fs -> formulaComms fs
return (
S.unions [pComms, qComms, fComms])
-- | Statically analyse a CspCASL "named process" term.
anaNamedProc :: PROCESS -> PROCESS_NAME -> [TERM ()] -> ProcVarMap ->
State CspCASLSign CommAlpha
anaNamedProc proc pn terms procVars = do
let ext = extendedInfo sig
Just (ProcProfile varSorts permAlpha) ->
if (length terms) == (length varSorts)
then do mapM (anaNamedProcTerm procVars) (zip terms varSorts)
else do let err = "wrong number of arguments in named process"
addDiags [mkDiag Error err proc]
do addDiags [mkDiag Error "unknown process name" proc]
-- | Statically analysis a CASL term occurring in a CspCASL "named
anaNamedProcTerm :: ProcVarMap -> ((TERM ()), SORT) -> State CspCASLSign ()
anaNamedProcTerm pm (t, expSort) = do
mt <- anaTermCspCASL pm t
Nothing -> return () -- CASL term analysis failed
(Just at) -> do ccTermCast at expSort -- attempt cast; don't need result
-- Static analysis of event sets and communication types
-- | Statically analyse a CspCASL event set.
anaEventSet :: EVENT_SET -> State CspCASLSign CommAlpha
anaEventSet (EventSet es _) = do
put sig { envDiags = vds }
-- | Statically analyse a CspCASL communication type.
anaCommType :: CspCASLSign -> CommAlpha -> COMM_TYPE -> State CspCASLSign CommAlpha
anaCommType sig alpha ct =
then -- ct is a sort name; insert sort into alpha
do return (
S.insert (CommTypeSort ctSort) alpha)
else -- ct not a sort name, so should be a channel name
Just s -> -- ct is a channel name; insert typed chan name into alpha
return (
S.insert (mkTypedChan ct s) alpha)
Nothing -> do let err = "not a sort or channel name"
addDiags [mkDiag Error err ct]
where ctSort = simpleIdToId ct
mkTypedChan c s = CommTypeChan $ TypedChanName c s
-- Static analysis of events
-- | Statically analyse a CspCASL event.
anaEvent :: EVENT -> ProcVarMap -> State CspCASLSign (CommAlpha, ProcVarMap)
anaEvent e vars = case e of
TermEvent t _ -> anaTermEvent t vars
ChanSend c t _ -> anaChanSend c t vars
ChanNonDetSend c v s _ -> anaChanBinding c v s
ChanRecv c v s _ -> anaChanBinding c v s
-- | Statically analyse a CspCASL term event.
anaTermEvent :: (TERM ()) -> ProcVarMap ->
State CspCASLSign (CommAlpha, ProcVarMap)
mt <- anaTermCspCASL vars t
Just at -> [(CommTypeSort (sortOfTerm at))]
-- | Statically analyse a CspCASL channel send event.
anaChanSend :: CHANNEL_NAME -> (TERM ()) -> ProcVarMap ->
State CspCASLSign (CommAlpha, ProcVarMap)
anaChanSend c t vars = do
let ext = extendedInfo sig
addDiags [mkDiag Error "unknown channel" c]
mt <- anaTermCspCASL vars t
Nothing -> -- CASL analysis failed
do mc <- ccTermCast at chanSort
Nothing -> -- cast failed
do let castSort = sortOfTerm ct
alpha = [CommTypeSort castSort
,CommTypeChan $ TypedChanName c castSort
-- | Statically analyse a CspCASL "binding" channel event (which is
-- either a channel nondeterministic send event or a channel receive
anaChanBinding :: CHANNEL_NAME -> VAR -> SORT ->
State CspCASLSign (CommAlpha, ProcVarMap)
anaChanBinding c v s = do
checkSorts [s] -- check sort is known
let ext = extendedInfo sig
addDiags [mkDiag Error "unknown channel" c]
then do let alpha = [CommTypeSort s
,CommTypeChan (TypedChanName c s)]
else do let err = "sort not a subsort of channel's sort"
addDiags [mkDiag Error err s]
-- Static analysis of renaming and renaming items
-- | Statically analyse a CspCASL renaming.
anaRenaming :: RENAMING -> State CspCASLSign CommAlpha
-- | Statically analyse a CspCASL renaming item.
anaRenamingItem :: CommAlpha -> Id -> State CspCASLSign CommAlpha
anaRenamingItem inAl ri = do
totOps <- getUnaryOpsById ri Total
then return (inAl `
S.union` totOps)
else do parOps <- getUnaryOpsById ri Partial
then return (inAl `
S.union` parOps)
else do preds <- getBinPredsById ri
else do let err = ("renaming item not a binary " ++
"operation or predicate name")
addDiags [mkDiag Error err ri]
-- | Given a CASL identifier and a `function kind' (total or partial),
-- find all unary operations of that kind with that name in the CASL
-- signature, and return a set of corresponding communication types
getUnaryOpsById :: Id -> FunKind -> State CspCASLSign (
S.Set CommType)
getUnaryOpsById ri kind = do
binOpsKind =
S.filter (isBin kind) opsWithId
where isBin k ot = (k == opKind ot) && (1 == (length (opArgs ot)))
-- | Given a CASL identifier find all binary predicates with that name
-- in the CASL signature, and return a set of corresponding
-- communication types for those predicates.
getBinPredsById :: Id -> State CspCASLSign (
S.Set CommType)
where isBin ot = (2 == (length (predArgs ot)))
-- | Given two CspCASL communication alphabets, check that the first's
-- subsort closure is a subset of the second's subsort closure.
checkCommAlphaSub :: CommAlpha -> CommAlpha -> PROCESS -> String ->
checkCommAlphaSub sub super proc context = do
(closeCspCommAlpha sig super))
else do let err = ("Communication alphabet subset violations (" ++
context ++ "): " ++ (show $
S.toList extras))
addDiags [mkDiag Error err proc]
-- Static analysis of CASL terms occurring in CspCASL process terms.
-- | Statically analyse a CASL term appearing in a CspCASL process;
-- any in-scope process variables are added to the signature before
-- performing the analysis.
anaTermCspCASL :: ProcVarMap -> (TERM ()) ->
State CspCASLSign (Maybe (TERM ()))
sigext = sig { varMap = newVars }
Result ds mt = anaTermCspCASL' sigext t
-- | Statically analyse a CASL term in the context of a CspCASL
-- signature. If successful, returns a fully-qualified term.
anaTermCspCASL' :: CspCASLSign -> TERM () -> Result (TERM ())
anaTermCspCASL' sig trm = do
let allIds = unite [mkIdSets (allOpIds sig) $ allPredIds sig]
mix = emptyMix { mixRules = makeRules ga allIds }
resT <- resolveMixfix (putParen mix) (mixResolve mix)
oneExpTerm (const return) sig resT
-- | Attempt to cast a CASL term to a particular CASL sort.
ccTermCast :: (TERM ()) -> SORT -> State CspCASLSign (Maybe (TERM ()))
then do let err = "upcast term to " ++ (show cSort)
addDiags [mkDiag Debug err t]
return (Just (Sorted_term t cSort (getRange t)))
then do let err = "downcast term to " ++ (show cSort)
addDiags [mkDiag Debug err t]
return (Just (Cast t cSort (getRange t)))
else do let err = "can't cast term to sort " ++
addDiags [mkDiag Error err t]
where termSort = (sortOfTerm t)
-- Static analysis of CASL formulae occurring in CspCASL process
-- | Statically analyse a CASL formula appearing in a CspCASL process;
-- any in-scope process variables are added to the signature before
-- performing the analysis.
anaFormulaCspCASL :: ProcVarMap -> (FORMULA ()) ->
State CspCASLSign (Maybe (FORMULA ()))
anaFormulaCspCASL pm f = do
addDiags [mkDiag Debug "anaFormulaCspCASL" ()]
sigext = sig { varMap = newVars }
Result ds mt = anaFormulaCspCASL' sigext f
-- | Statically analyse a CASL formula in the context of a CspCASL
-- signature. If successful, returns a fully-qualified formula.
anaFormulaCspCASL' :: CspCASLSign -> FORMULA () -> Result (FORMULA ())
anaFormulaCspCASL' sig frm = do
let allIds = unite [mkIdSets (allOpIds sig) $ allPredIds sig]
mix = emptyMix { mixRules = makeRules ga allIds }
resF <- resolveFormula (putParen mix) (mixResolve mix)
minExpFORMULA (const return) sig resF
-- | Compute the communication alphabet arising from a formula
-- occurring in a CspCASL process term.
formulaComms :: (FORMULA ()) -> CommAlpha
formulaComms f = case f of
Quantification _ varDecls f' _ ->
where vdSorts = (map (CommTypeSort . vdSort) varDecls)
vdSort (Var_decl _ s _) = s
Conjunction fs _ ->
S.unions (map formulaComms fs)
Disjunction fs _ ->
S.unions (map formulaComms fs)
Implication f1 f2 _ _ -> (formulaComms f1) `
S.union` (formulaComms f2)
Equivalence f1 f2 _ -> (formulaComms f1) `
S.union` (formulaComms f2)
Negation f' _ -> formulaComms f'
Definedness t _ ->
S.singleton (CommTypeSort (sortOfTerm t))
Existl_equation t1 t2 _ ->
S.fromList [CommTypeSort (sortOfTerm t1),
CommTypeSort (sortOfTerm t2)]
Strong_equation t1 t2 _ ->
S.fromList [CommTypeSort (sortOfTerm t1),
CommTypeSort (sortOfTerm t2)]
Membership t s _ ->
S.fromList [CommTypeSort (sortOfTerm t),
Mixfix_formula t ->
S.singleton (CommTypeSort (sortOfTerm t))