{-# LANGUAGE UnicodeSyntax #-} module GraphRewriting.GL.Global where import Prelude.Unicode import Graphics.UI.GLUT (addTimerCallback, Window, postRedisplay) import GraphRewriting.Graph import GraphRewriting.Graph.Read import GraphRewriting.Rule import GraphRewriting.Pattern import Data.IORef import GraphRewriting.Layout.RotPortSpec import qualified Data.Set as Set import Data.Set (Set) import Data.List ((\\)) import Control.Monad (when, replicateM_) import Data.Foldable import Data.Functor import Data.Traversable import Prelude hiding (concat, concatMap, or, elem, foldr, any, mapM) data GlobalVars n = GlobalVars {graph ∷ Graph n, paused ∷ Bool, selectedRule ∷ Int, highlighted ∷ Set Node, layoutStep ∷ Node → Rewrite n (), canvas ∷ Window, menu ∷ Window, getRules ∷ RuleTree n} data LabelledTree a = Branch String [LabelledTree a] | Leaf String a data LTZipper a = Root | Child String [LabelledTree a] (LTZipper a) [LabelledTree a] type LTLoc a = (LabelledTree a, LTZipper a) -- depth-first traversal next ∷ LTLoc a → Maybe (LTLoc a) next (Branch b (t:ts), p) = Just (t, Child b [] p ts) next (Leaf l x, p) = right (Leaf l x, p) next _ = Nothing nth ∷ Int → LTLoc a → Maybe (LTLoc a) nth n l = iterate (>>= next) (Just l) !! n right ∷ LTLoc a → Maybe (LTLoc a) right (t, Child c ls p (r:rs)) = Just (r, Child c (ls ⧺ [t]) p rs) right (t, Child c ls p []) = up (t, Child c ls p []) >>= right right _ = Nothing up ∷ LTLoc a → Maybe (LTLoc a) up (t, Child c ls p rs) = Just (Branch c (ls ⧺ [t] ⧺ rs), p) up _ = Nothing put ∷ LTLoc a → LabelledTree a → LTLoc a put (_, p) t = (t, p) top ∷ LTLoc a → LTLoc a top (t, Root) = (t, Root) top (t, Child c ls p rs) = top (Branch c (ls ⧺ [t] ⧺ rs), p) root ∷ LabelledTree a → LTLoc a root t = (t, Root) instance Foldable LabelledTree where foldr f y (Leaf l x) = f x y foldr f y (Branch l ts) = foldr (flip $ foldr f) y ts instance Functor LabelledTree where fmap f (Leaf l x) = Leaf l (f x) fmap f (Branch l ts) = Branch l $ fmap f <$> ts instance Traversable LabelledTree where traverse f (Leaf l x) = Leaf l <$> f x traverse f (Branch l ts) = Branch l <$> traverse (traverse f) ts showRuleTree ∷ RuleTree n → String showRuleTree = showLabelledTree 2 0 (+) . fmap fst showLabelledTree ∷ Show a ⇒ Int → a → (a → a → a) → LabelledTree a → String showLabelledTree indentation init combine = snd . rec where rec (Leaf l x) = (x, l ⧺ " " ⧺ show x) rec (Branch l ts) = (x, l ⧺ " " ⧺ show x ⧺ "\n" ⧺ indent (unlines ls)) where x = foldr combine init xs (xs, ls) = unzip $ map rec ts indent str = unlines $ map (replicate indentation ' ' ⧺) (lines str) unlines [] = "" unlines [x] = x unlines xs = head xs ⧺ "\n" ⧺ unlines (tail xs) instance Show a ⇒ Show (LabelledTree a) where show (Leaf l x) = l ⧺ " " ⧺ show x show (Branch l s) = l ⧺ "\n" ⧺ indent (unlines $ map show s) where indent str = unlines $ map (replicate 2 ' ' ⧺) (lines str) unlines [] = "" unlines [x] = x unlines xs = head xs ⧺ "\n" ⧺ unlines (tail xs) redisplay ∷ Window → IO () redisplay = postRedisplay . Just readGraph = fmap graph . readIORef writeGraph g = modifyGraph (const g) modifyGraph f globalVars = do modifyIORef globalVars $ \v → v {graph = f $ graph v} applyRule ∷ Rule n → IORef (GlobalVars n) → IO () applyRule r globalVars = do layout ← layoutStep <$> readIORef globalVars g ← readGraph globalVars let ns = evalGraph readNodeList g -- we don't use the fist element of the tuple and compute newNodes ourselves due to a bug in the graph-rewriting package (It's completely out of my hands!!!!1) let (_, g') = runGraph (apply r) g let ns' = evalGraph readNodeList g' let newNodes = ns' Data.List.\\ ns writeGraph (execGraph (replicateM_ 15 $ mapM layout newNodes) g') globalVars highlight globalVars selectRule i globalVars = do ruleListLength ← numNodes <$> getRules <$> readIORef globalVars if 0 ≤ i ∧ i < ruleListLength then do modifyIORef globalVars $ \v → v {selectedRule = i} highlight globalVars else return () highlight globalVars = do gv@GlobalVars {graph = g, getRules = rs, selectedRule = r, highlighted = h, canvas = c} ← readIORef globalVars let rule = fold $ fmap snd (subtrees rs !! r) let h' = Set.fromList [head match | (match,rewrite) ← runPattern rule g] writeIORef globalVars $ gv {highlighted = h'} redisplay c layoutLoop globalVars = do gv@GlobalVars {graph = g, paused = p, layoutStep = l, canvas = c} ← readIORef globalVars when (not p) $ do examine position (head $ nodes g) `seq` return () writeIORef globalVars $ gv {graph = execGraph (mapM l =<< readNodeList) g} -- TODO: relayout all nodes at once redisplay c addTimerCallback 40 $ layoutLoop globalVars pause globalVars = modifyIORef globalVars $ \vs → vs {paused = True} resume globalVars = do modifyIORef globalVars $ \vs → vs {paused = False} layoutLoop globalVars subtrees ∷ LabelledTree a → [LabelledTree a] subtrees t = t : case t of Leaf _ _ → [] Branch l ts → concatMap subtrees ts numNodes ∷ LabelledTree a → Int numNodes = length . subtrees type RuleTree n = LabelledTree (Int, Rule n) -- | Traverses the rule tree depth-first and executes all leaf rules it encounters. Rules are -- executed everywhere they match, except if they overlap one of them is chosen at random. -- So this corresponds to a complete development. applyLeafRules ∷ (Rule n → Rule n) → Int → IORef (GlobalVars n) → IO () applyLeafRules restriction idx gvs = do g ← readGraph gvs comptree ← getRules <$> readIORef gvs let pos = nth idx (root comptree) case pos of Nothing → return () Just (tree,p) → do let ns = evalGraph readNodeList g -- first we mark all redexes let rule = restriction $ fold $ fmap snd tree -- then we find a non-overlapping subset let ms = head $ evalPattern (matches rule) g -- then we apply the rules in the leafs while restricting them to that subset let ((_, g'), tree') = mapAccumL applyLeafRules' (ms, g) tree let ns' = evalGraph readNodeList g' let newNodes = ns' Data.List.\\ ns layout ← layoutStep <$> readIORef gvs writeGraph (execGraph (replicateM_ 15 (mapM layout newNodes)) g') gvs modifyIORef gvs $ \x → x {getRules = fst $ top (tree',p)} where -- At every leaf apply the rule restricted to the set of predetermined matches, every time removing the -- the match from the set updating the graph and the counter. -- applyLeafRules' ∷ ([Match], Graph n) → (Int, Rule n) → (([Match], Graph n), (Int, Rule n)) applyLeafRules' (matches, g) (n, r) = let ms = runPattern r' g r' = restrictOverlap (\past future → future `elem` matches) (restriction r) in if null ms then ((matches, g), (n, r)) else let (match, rewrite) = head ms g' = execGraph rewrite g in applyLeafRules' (filter (\m → not $ any (`elem` match) m) matches, g') (n + 1, r)