---------------------------------------------------------------------------- -- | -- Module : CSPM.Interpreter.ClosureSets -- Copyright : (c) Fontaine 2009 -- License : BSD -- -- Maintainer : Fontaine@cs.uni-duesseldorf.de -- Stability : experimental -- Portability : GHC-only -- -- Utility functions dealing with closure sets. -- ---------------------------------------------------------------------------- {- todo: redo this todo: add testcases this is all much to complex -} module CSPM.Interpreter.ClosureSet where import CSPM.Interpreter.Types as Types import CSPM.Interpreter.SSet as SSet import CSPM.Interpreter.Hash as Hash import Data.List as List import Data.Set (Set) import qualified Data.Set as Set import qualified Data.Map as Map import Data.Maybe import Data.Ord memberPrefixTrie :: [Field] -> PrefixTrie -> Bool memberPrefixTrie [] PTNil = True memberPrefixTrie _ (PTClosure _) = True memberPrefixTrie [] t = throwInternalError ("memberPrefix : number of fields mismatch" ++ show t) Nothing Nothing memberPrefixTrie (_h:r) (PTAny t) = memberPrefixTrie r t memberPrefixTrie (h:r) (PTMap m) = case Map.lookup h m of Just t -> memberPrefixTrie r t Nothing -> False memberPrefixTrie (h:r) (PTRec set t) = if h `Set.member` set then memberPrefixTrie r t else False memberPrefixTrie (h:r) (PTSingle v t) = if h == v then memberPrefixTrie r t else False prefixTrieNext :: PrefixTrie -> Field -> Maybe PrefixTrie prefixTrieNext t field = case t of PTNil -> throwInternalError ("prefixTrieNext number of fields mismatch PTNil" ++ show field) Nothing Nothing PTAny new -> Just new PTMap m -> case Map.lookup field m of Just new -> Just new Nothing -> Nothing PTRec s new -> if field `Set.member` s then Just new else Nothing PTSingle v new -> if field == v then Just new else Nothing closureStateNext :: ClosureState -> Field -> ClosureState closureStateNext closure field = case closure of ClosureStateFailed {} -> closure ClosureStateSucc {} -> closure {currentPrefixTrie = fromJust ptNext } ClosureStateNormal {} -> case prefixTrieNext (currentPrefixTrie closure) field of Nothing -> ClosureStateFailed {origClosureSet = origClosureSet closure} Just (PTClosure p) -> ClosureStateSucc {currentPrefixTrie = p, origClosureSet = origClosureSet closure} Just pt -> closure { currentPrefixTrie = pt} where ptNext = prefixTrieNext (currentPrefixTrie closure) field setToClosure :: Set Value -> ClosureSet setToClosure = mkClosureSet . setToPrefixTrie mkClosureSet :: PrefixTrie -> ClosureSet mkClosureSet x = ClosureSet { closureSetTrie = x ,closureSetDigest = mix (hs "ClosureSet") $ hash x } setToPrefixTrie :: Set Value -> PrefixTrie setToPrefixTrie = worker . map fromTuple . Set.toList where fromTuple (VDotTuple l ) = l fromTuple x = [x] -- channel a and [|{a}|] apears in one evans example worker :: [[Value]] -> PrefixTrie worker [] = throwInternalError "setToPrefixTrie worker []" Nothing Nothing worker [[]] = PTNil worker l = let sl = sortBy (comparing head) l grps :: [[[Value]]] grps = groupBy (\a b -> (head a) == (head b)) sl withkeys :: [(Value,PrefixTrie)] withkeys = map (\g -> (head $ head g, worker $ map tail g)) grps in PTMap $ Map.fromList withkeys closureToSet :: ClosureSet -> Set Value closureToSet = prefixTrieToSet . closureSetTrie hackValueToEvent :: Value -> Event hackValueToEvent (VDotTuple l ) = l hackValueToEvent x = [x] -- channel a and [|{a}|] apears in one evans example {- todo : this is too lowlevel -} prefixTrieToSet :: PrefixTrie -> Set Value prefixTrieToSet trie = Set.fromList $ worker [] [] trie where worker :: [Value] -> [Value] -> PrefixTrie -> [Value] worker acc path t = case t of PTNil -> (VDotTuple $ reverse path) : acc PTAny {} -> throwFeatureNotImplemented "cannot enumerate PTAny (Set,Seq,INT)" Nothing Nothing PTMap m -> foldl' (add path) acc $ Map.assocs m PTRec s r -> foldl' (add path) acc $ zip (Set.elems s) $ repeat r PTSingle v r -> worker acc (v:path) r PTClosure l -> worker acc path l add :: [Value] -> [Value] -> (Value,PrefixTrie) -> [Value] add path acc (val,t) = worker acc (val:path) t {- {|a,b,c|} -} mkEventClosure :: [Value] -> EM ClosureSet mkEventClosure l = if List.null l then throwScriptError "mkEventClosure : empty ClosureSet" Nothing Nothing else return $ mkClosureSet $ ptUnions $ map valueToPT l {- convert the things inside a {| |} to a prefix trie -} valueToPT :: Value -> PrefixTrie valueToPT v = case v of VChannel c -> fieldsToPT [v] $ (SSet.Total : chanFields c) VDotTuple [] -> throwScriptError "valueToPT : empty dot-tuple" Nothing Nothing VDotTuple l@(VChannel c : _) -> fieldsToPT l (SSet.Total : chanFields c) VDotTuple _ -> throwScriptError "valueToPT : dot-tuple does not start with a channel" Nothing $ Just v _ -> throwScriptError "valueToPT: cannot make a event-closure of value" Nothing $ Just v {- fieldsToPT is a kind of zip -} fieldsToPT :: [Value] -> [FieldSet] -> PrefixTrie fieldsToPT (v:vr) (f:fr) = if v `SSet.member` f then PTSingle v $ fieldsToPT vr fr else throwScriptError "fieldsToPT : value outside channel definition" Nothing (Just v) fieldsToPT [] f = PTClosure $ foldr ( \(SSet.Proper s) pt -> PTRec s pt) PTNil f fieldsToPT v [] = throwScriptError "fieldsToPT : more fields than declared in channel definition" Nothing (Just $ VDotTuple v) {- todo : more efficent, direkt implementation without converting to intermediate sets -} ptUnions :: [PrefixTrie] -> PrefixTrie ptUnions = setToPrefixTrie . Set.unions . map prefixTrieToSet singleEventToClosureSet :: Event -> ClosureSet singleEventToClosureSet e = mkClosureSet $ foldr PTSingle PTNil $ e