cpsa-2.0.0: src/CPSA/Shapes/Shapes.hs
-- Filters the intermediate skeletons out of a CPSA run.
-- 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.Shapes.Shapes (Map, empty, shape) where
import qualified Data.IntMap as I
import Data.IntMap (IntMap)
import CPSA.Lib.CPSA
-- An int map is used to bind a skeleton to its parent.
type Map = IntMap Int
empty :: Map
empty = I.empty
-- Omit S-expressions for skeletons that are not the point-of-view
-- skeleton or a shape of a point-of-view skeleton. The parent of a
-- skeleton is changed to reflect omitted skeletons, and its seen
-- children are deleted.
shape :: Monad m => Map -> SExpr Pos -> m (Map, Maybe (SExpr Pos))
shape map x@(L _ (S _ "defskeleton" : _ : _ : strands)) =
do
label <- nassoc "label" xs
checkLabel label
where
xs = findAList strands -- Association list part of preskeleton
checkLabel Nothing =
return (map, Just x) -- Not labeled, just add it to the answers
checkLabel (Just label) =
do -- Found a label
parent <- nassoc "parent" xs
checkParent label parent
checkParent label Nothing = -- Point of view preskeleton
return (I.singleton label label, Just $ reparent label x)
checkParent label (Just parent) = -- Descendent of POV
lookupParent label $ I.lookup parent map
lookupParent _ Nothing =
fail "parent unknown"
lookupParent label (Just ancestor) =
-- Ancestor is the label of POV preskeleton or of a shape
let shape = maybe False (const True) (assoc "shape" xs) in
if shape then
return (I.insert label label map,
Just $ reparent ancestor x)
else -- Drop S-expression here
return (I.insert label ancestor map, Nothing)
shape map x = return (map, Just x)
findAList :: [SExpr Pos] -> [SExpr Pos]
findAList (L _ (S _ "defstrand" : _) : xs) = findAList xs
findAList (L _ (S _ "deflistener" : _) : xs) = findAList xs
findAList xs = xs
-- Lookup value in alist, appending values with the same key
assoc :: String -> [SExpr Pos] -> Maybe [SExpr Pos]
assoc key alist =
loop alist Nothing
where
loop ((L _ (S _ head : tail)) : rest) vals
| key == head = loop rest (extend tail vals)
| otherwise = loop rest vals
loop _ vals = vals
extend x Nothing = Just x
extend x (Just y) = Just (x ++ y)
-- Look up a value known to be an Int
nassoc :: Monad m => String -> [SExpr Pos] -> m (Maybe Int)
nassoc key xs =
case assoc key xs of
Nothing -> return Nothing
Just [val] ->
do
ns <- num val
return (Just ns)
Just (x:_) -> fail (shows (annotation x) "Expecting one number")
Just [] -> fail (shows (annotation (head xs)) "Expecting one number")
num :: Monad m => SExpr Pos -> m Int
num (N _ n) = return n
num x = fail (shows (annotation x) "Expecting a number")
reparent :: Int -> SExpr Pos -> SExpr Pos
reparent n (L p0 (S p1 "defskeleton" : protocol : vars : strands)) =
L p0 (S p1 "defskeleton" : protocol : vars : updateStrands n strands)
reparent _ x = x
updateStrands :: Int -> [SExpr Pos] -> [SExpr Pos]
updateStrands n (L p0 (S p1 "defstrand" : x) : xs) =
L p0 (S p1 "defstrand" : x) : updateStrands n xs
updateStrands n (L p0 (S p1 "deflistener" : x) : xs) =
L p0 (S p1 "deflistener" : x) : updateStrands n xs
updateStrands n xs = updateParent n xs
updateParent :: Int -> [SExpr Pos] -> [SExpr Pos]
updateParent n (L p0 [tag@(S _ "parent"), N p1 _] : xs) =
L p0 [tag, N p1 n] : updateParent n xs
-- Handle the case of seen in the original problem, and dump it.
updateParent n (L _ (S _ "seen" : _) : xs) = updateParent n xs
updateParent n (x : xs) = x : updateParent n xs
updateParent _ [] = []