module TclTrie where import TrieIO import IOExts import Monad import Utils import List import Remover instance Show (IORef a) where show a = "" data TrieObj = TrieObj { isalive :: ! Bool, deleteObj :: ! Remover, menuObjs :: ! (Int,[String]), boundArgs :: ! [(String,[String],Remover)] } type TrieObjV = IORef TrieObj initTrieObj :: Bool -> TrieObj initTrieObj b = TrieObj b (return ()) (0,[]) [] newTrieObjV :: IO TrieObjV newTrieObjV = newIORef $! initTrieObj True insertItem :: [String] -> String -> Int -> [String] insertItem xs s 0 = s:xs insertItem (x:xs) s n = x:insertItem xs s (n-1) insertItem [] s _ = [s] removeItem :: [String] -> String -> ([String],Maybe Int) removeItem xs s = remove [] xs s 0 where remove bef (x:xs) s n = if x == s then (reverse bef ++ xs,Just n) else remove (x:bef) xs s (n + 1) remove bef [] _ _ = (reverse bef,Nothing) sizeItems :: TrieObjV -> IO Int sizeItems r = fmap (fst . menuObjs) $ readIORef r indexItem :: [String] -> String -> Maybe Int indexItem xs s = findIndex (== s) xs insertItemObj :: TrieObjV -> String -> Int -> IO () insertItemObj ref s n = do updIORef ref (\o -> o {menuObjs = case menuObjs o of (num,xs) -> (num+1,insertItem xs s n)}) removeItemObj :: TrieObjV -> String -> IO (Maybe Int) removeItemObj ref s = do o <- readIORef ref case menuObjs o of (num,xs) -> case removeItem xs s of (xs,Nothing) -> do return Nothing (xs,just) -> do writeIORef ref (o {menuObjs = (num - 1,xs)}) return just indexItemObj :: TrieObjV -> String -> IO (Maybe Int) indexItemObj ref s = do o <- readIORef ref return $! indexItem (snd $ menuObjs o) s atIndexItemObj :: TrieObjV -> Int -> IO (Maybe String) atIndexItemObj ref n = do (num,os) <- fmap menuObjs $ readIORef ref return $! if n + 1 > num then Nothing else Just $ os !! n addFinaliserW :: TrieObjV -> Remover -> IO () addFinaliserW ref rm = updIORef ref (\o -> o {deleteObj = rm >> deleteObj o}) checkAlive :: TrieObjV -> IO Bool checkAlive ref = fmap isalive $ readIORef ref type TclTrie = IORef (TrieIO TrieObjV) mkTclTrie :: IO TclTrie mkTclTrie = do o <- newTrieObjV t <- initTrie "." o newIORef t clearTclTrie :: TclTrie -> IO () clearTclTrie trie = do o <- newIORef $ initTrieObj False t <- initTrie "." o writeIORef trie t register_ :: TclTrie -> String -> IO TrieObjV register_ trie s = do o <- newTrieObjV t <- readIORef trie o <- registerInTrie t s o return o lookupTrieObj' :: TclTrie -> String -> IO (Maybe TrieObjV) lookupTrieObj' trie s = do t <- readIORef trie lookupTrie t s checkAlive_ :: TclTrie -> String -> IO Bool checkAlive_ trie s = lookupTrieObj' trie s >>= maybe (return False) (fmap isalive . readIORef) updateTrieObj :: TclTrie -> String -> (TrieObjV -> IO ()) -> IO () updateTrieObj trie s f = lookupTrieObj' trie s >>= maybe (return ()) f addFinaliserW_ :: TclTrie -> String -> Remover -> IO () addFinaliserW_ trie s r = updateTrieObj trie s $ flip addFinaliserW r doDelete :: TclTrie -> String -> IO Bool doDelete trie s = do t <- readIORef trie (isdead,dead,ds) <- deleteFromTrie s t when dead $ clearTclTrie trie if isdead then do mapM_ (\ref -> readIORef ref >>= dodelete ref) ds return True else do when (not (null ds)) $ putStrLn $ "error: "++s++" already dead but still "++ show (length ds) ++ " children" return False where dodelete ref t@(TrieObj {deleteObj = act,boundArgs = bs}) = do mapM_ (\ (_,_,die) -> die) bs act writeIORef ref $ initTrieObj False return () updBinding :: TrieObjV -> String -> String -> Bool -> Maybe Remover -> IO (Maybe Remover) updBinding v s evval clearall rm = do o <- readIORef v case partition (\(a,_,_) -> a == s) (boundArgs o) of ([(a,all,rm')],rest) -> if clearall then do writeIORef v $ o {boundArgs = maybe rest (\rm -> (s,[evval],rm):rest) rm} return $ Just rm' else do writeIORef v $ o {boundArgs = maybe (boundArgs o) (\rm -> (s,evval:all,rm >> rm'):rest) rm} return Nothing ([],rest) -> do writeIORef v $ o {boundArgs = maybe rest (\rm -> (s,[evval],rm):rest) rm} return Nothing getBoundArgs :: TrieObjV -> IO [String] getBoundArgs v = do fmap (get . boundArgs) $ readIORef v where get vs = case find (\ (a,_,_) -> a == "command") vs of Nothing -> [] Just (_,cs,_) -> cs