cpsa-4.4.3: src/CPSA/Query/Query.hs
-- Reads and runs a query on a derivation tree.
-- 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.Query (Query, loadQuery, execQuery) where
import System.IO
import Data.List (nub, sort)
import CPSA.Lib.SExpr
import CPSA.Lib.Entry
import CPSA.Query.Tree
import CPSA.Query.Loader (Preskel (..), assoc)
data Query
= HasKey String
| Null String
| Member (SExpr Pos) String
| HasChildren
| HasDuplicates
| Not Query
| And [Query]
| Or [Query]
deriving Show
loadQuery :: String -> IO (Query, [Int])
loadQuery file =
do
input <- openFile file ReadMode
p <- posHandle file input
q <- readSExpr p
case q of
Nothing -> fail "no query in query input"
Just x ->
do
pq <- parseQuery x
ints <- getInts p
return (pq, ints)
parseQuery :: SExpr Pos -> IO Query
parseQuery (L _ [S _ "has-key", S _ sym]) =
return (HasKey sym)
parseQuery (L _ [S _ "null", S _ sym]) =
return (Null sym)
parseQuery (L _ [S _ "member", x, S _ sym]) =
return (Member x sym)
parseQuery (L _ [S _ "has-children"]) =
return HasChildren
parseQuery (L _ [S _ "has-duplicates"]) =
return HasDuplicates
parseQuery (L _ [S _ "not", x]) =
do
q <- parseQuery x
return (Not q)
parseQuery (L _ (S _ "and" : xs)) =
do
qs <- mapM parseQuery xs
return (And qs)
parseQuery (L _ (S _ "or" : xs)) =
do
qs <- mapM parseQuery xs
return (Or qs)
parseQuery _ = fail "query does not parse"
getInts :: PosHandle -> IO [Int]
getInts p =
do
i <- readSExpr p
case i of
Nothing -> return []
Just x ->
case x of
N _ n ->
do
rest <- getInts p
return (n:rest)
_ -> fail "bad int in query file"
execQuery :: (Query, [Int]) -> Forest -> IO [Int]
execQuery (q, []) ts =
return (sort $ concatMap (execQueryTree q) ts)
execQuery (q, ints) ts =
do
ts <- mapM (getTree ts) (nub ints)
return (sort $ concatMap (execQueryTree q) ts)
getTree :: Forest -> Int -> IO Tree
getTree _ int | int < 0 = fail ("Bad tree index " ++ show int)
getTree ts int =
loop ts int
where
loop [] _ = fail ("Cannot find tree " ++ show int)
loop (t:_) 0 = return t
loop (_:ts) i = loop ts (i - 1)
execQueryTree :: Query -> Tree -> [Int]
execQueryTree q t =
ans ++ concatMap (execQueryTree q) (children t)
where
ans = if runQuery q t then [label (vertex t)] else []
runQuery :: Query -> Tree -> Bool
runQuery (HasKey sym) t =
maybe False (const True) (assoc sym (alist (vertex t)))
runQuery (Null sym) t =
maybe False null (assoc sym (alist (vertex t)))
runQuery (Member x sym) t =
case assoc sym (alist (vertex t)) of
Nothing -> False
Just l -> elem x l
runQuery HasChildren t =
not (null (children t))
runQuery HasDuplicates t =
not (null (duplicates t))
runQuery (Not q) t = not (runQuery q t)
runQuery (And qs) t = all (\ q -> runQuery q t) qs
runQuery (Or qs) t = any (\ q -> runQuery q t) qs