{-# LANGUAGE RecordWildCards #-} module Slim.Sim where import Slim import Data.Map.Strict (Map, (!)) import qualified Data.Map.Strict as Map import Data.Set (Set) import qualified Data.Set as Set import Text.PrettyPrint import Data.IORef import Data.Foldable type Document = (Map ElementId Node, ElementId) data Node = Node { n_elementId :: ElementId , n_namespace :: Namespace , n_tagName :: TagName , n_attributes :: Map AttributeName AttributeValue , n_text :: Maybe String , n_eventSources :: Set EventName , n_children :: [ElementId] } deriving Show newNode :: ElementId -> Namespace -> TagName -> Node newNode n_elementId n_namespace n_tagName = Node { n_attributes = Map.empty , n_text = Nothing , n_eventSources = Set.empty , n_children = [] , .. } applyAction :: Document -> ElementAction -> Document applyAction (nodes, rootId) action = case action of Create ei ns tn -> (Map.insert ei (newNode ei ns tn) nodes, rootId) Replace ei1 ei2 -> let f n = n { n_children = [if ei1 == ei then ei2 else ei | ei <- n_children n] } in (Map.map f nodes, if ei1 == rootId then ei2 else rootId) Destroy ei -> let f n = n { n_children = filter (ei /=) (n_children n) } in (Map.map f (Map.delete ei nodes), rootId) SetAttribute ei an av -> let f n = n { n_attributes = Map.insert an av (n_attributes n) } in (Map.adjust f ei nodes, rootId) UnsetAttribute ei an -> let f n = n { n_attributes = Map.delete an (n_attributes n) } in (Map.adjust f ei nodes, rootId) SetText ei t -> let f n = n { n_text = t } in (Map.adjust f ei nodes, rootId) AddChildren ei eis -> let f n = n { n_children = (n_children n) ++ eis } in (Map.adjust f ei nodes, rootId) Subscribe ei en -> let f n = n { n_eventSources = Set.insert en (n_eventSources n) } in (Map.adjust f ei nodes, rootId) Unsubscribe ei en -> let f n = n { n_eventSources = Set.delete en (n_eventSources n) } in (Map.adjust f ei nodes, rootId) ppDocument :: Document -> String ppDocument (nodes, rootId) = renderStyle style (ppNode $ nodes ! rootId) where ppAttribute (k, v) = text k <> text "=" <> text v ppEventSource k = text ("on" ++ k) ppChildren childIds = vcat [ppNode n | Just n <- map (`Map.lookup` nodes) childIds] ppNode Node { .. } = text "<" <> ppElementName <+> ppAttributes <+> ppEventSources <> text ">" $$ nest 4 (maybe empty text n_text $$ ppChildren n_children) $$ text " text n_tagName <> text "#" <> int n_elementId <> text ">" where ppElementName = text n_tagName <> text "#" <> int n_elementId ppAttributes = hsep (ppAttribute <$> Map.toList n_attributes) ppEventSources = hsep (ppEventSource <$> Set.toList n_eventSources) findNode :: Document -> (Node -> Bool) -> Node findNode = findNodeN 0 findNodeN :: Int -> Document -> (Node -> Bool) -> Node findNodeN x doc f = case drop x (findNodes doc f) of (n:_) -> n [] -> newNode (-1) Nothing "not found" findNodes :: Document -> (Node -> Bool) -> [Node] findNodes (nodes, rootId) f = filter f (bfs [nodes ! rootId]) where bfs [] = [] bfs ns = ns ++ bfs [nodes ! i | n <- ns, i <- n_children n] startSim :: StartComponent void -> IO (Document, Node -> EventName -> EventData -> IO Document) startSim s = do (as, rootId, fire) <- runStartRoot s let doc = foldl' applyAction (Map.empty, rootId) as ref <- newIORef doc let fire' n en ed = do doc <- readIORef ref as <- fire (n_elementId n, en, ed) let doc' = foldl' applyAction doc as writeIORef ref doc' return doc' return (doc, fire')