cpsa-4.4.3: src/CPSA/Query/Loader.hs
-- Loads preskeletons and supplies functions for dealing with the
-- preskeleton's alist. The code is based on what is in
-- CPSA.Graph.Loader.
-- 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.Query.Loader (Preskel, label, parent, seen, alist, strip,
assoc, nassoc, State, loadFirst, loadNext)
where
import qualified Data.List as L
import CPSA.Lib.SExpr
import CPSA.Lib.Entry (gentlyReadSExpr)
-- A view of preskeletons designed for queries.
data Preskel = Preskel
{ label :: Int, -- Label from the input or generated
-- by the loader
parent :: Maybe Int, -- Parent from the input
seen :: [Int], -- Seen preskeletons isomorphic to cohort members
alist :: [SExpr Pos] } -- Body of this preskeleton
deriving Show
instance Eq Preskel where
k0 == k1 = label k0 == label k1
instance Ord Preskel where
compare k0 k1 = compare (label k0) (label k1)
-- The remaing is in support of the loader
-- Load one preskeleton at a time from the input. The state of
-- loading follows.
newtype State = State (PosHandle, Int)
-- Load the initial comments and the first preskeleton. It's an error
-- if there is no preskeleton in the input.
loadFirst :: PosHandle -> IO ([SExpr Pos], Preskel, State)
loadFirst h =
loadComments h []
loadComments :: PosHandle -> [SExpr Pos]
-> IO ([SExpr Pos], Preskel, State)
loadComments h cmts =
do
x <- gentlyReadSExpr h
case x of
Nothing -> fail "Empty input"
Just x ->
case x of
cmt@(L _ (S _ "comment" : _)) ->
loadComments h (cmt:cmts)
cmt@(L _ (S _ "herald" : _)) ->
loadComments h (cmt:cmts)
_ ->
do
n <- loadSExpr (State (h, 0)) x
case n of
Nothing -> fail "Empty input"
Just (p, s) -> return (reverse cmts, p, s)
-- Load the next preskeleton or return Nothing on EOF
loadNext :: State -> IO (Maybe (Preskel, State))
loadNext s@(State (h, _)) =
do
x <- gentlyReadSExpr h
case x of
Nothing -> return Nothing
Just x -> loadSExpr s x
-- Load from one S-expression
loadSExpr :: State -> SExpr Pos -> IO (Maybe (Preskel, State))
loadSExpr (State (h, tag)) x@(L pos (S _ "defskeleton" : xs)) =
do
k <- loadPreskel x pos tag xs
return (Just (k, State (h, 1 + max tag (label k))))
loadSExpr s (L _ (S _ "defprotocol" : _)) =
loadNext s
loadSExpr s (L _ (S _ "comment" : _)) =
loadNext s
loadSExpr _ x = fail (shows (annotation x) "Malformed input")
-- Preskeletons
loadPreskel :: MonadFail m => SExpr Pos -> Pos ->
Int -> [SExpr Pos] -> m Preskel
loadPreskel _ _ tag (S _ _ : (L _ (S _ "vars" : _)) : xs) =
do
checkAlist xs -- Ensure alist syntax
label <- nassoc "label" xs
parent <- nassoc "parent" xs
seen <- nsassoc "seen" xs
return Preskel { label = maybe tag id label,
parent = parent,
seen = maybe [] (L.sort . L.nub) seen,
alist = xs }
loadPreskel _ pos _ _ = fail (shows pos "Malformed skeleton")
-- Strip positions from an S-expression
strip :: SExpr a -> SExpr ()
strip (S _ s) = S () s
strip (Q _ s) = Q () s
strip (N _ n) = N () n
strip (L _ l) = L () (map strip l)
-- Ensure alist has the proper form
checkAlist :: MonadFail m => [SExpr Pos] -> m ()
checkAlist [] = return ()
checkAlist ((L _ (S _ _ : _)) : xs) = checkAlist xs
checkAlist xs = fail (shows (annotation $ head xs) "Malformed association list")
-- 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)
-- assoc key alist =
-- concat [ rest | L _ (S _ head : rest) <- alist, key == head ]
nassoc :: MonadFail 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 :: MonadFail m => SExpr Pos -> m Int
num (N _ n) = return n
num x = fail (shows (annotation x) "Expecting a number")
nsassoc :: MonadFail m => String -> [SExpr Pos] -> m (Maybe [Int])
nsassoc key xs =
case assoc key xs of
Nothing -> return Nothing
Just val ->
do
ns <- mapM num val
return (Just ns)