signals-0.2.0.1: src/Signal/Compiler/Sorter.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Signal.Compiler.Sorter (
Ordered(..)
, sorter
)
where
import Signal.Core hiding (lift)
import Signal.Core.Reify
import Signal.Core.Witness
import Control.Arrow
import Control.Monad.State
import Data.List (sortBy)
import Data.Function (on)
import Data.Typeable (Typeable)
import Data.Ref
import Data.Ref.Map (Map, Name)
import qualified Data.Ref.Map as M
import System.Mem.StableName (eqStableName)
import Unsafe.Coerce -- !
import Prelude hiding (Left, Right)
--------------------------------------------------------------------------------
-- * Sorting constructs
--------------------------------------------------------------------------------
-- | During the sorting process a node can either be sorted or unvisited
data Status = Visited | Unvisited deriving Eq
-- | The ordering assigned to a node after being sorted
type Order = Int
-- | Nodes tagged with extra bookeeping labels
data Tagged i a where
Tagged :: Witness i a
=> Status
-> Order
-> Name (S Symbol i a)
-> Node i (S Symbol i a)
-> Tagged i (S Symbol i a)
--------------------------------------------------------------------------------
-- ** Sorting helpers
-- | Monad used during sorting
type M i = State (Order, Map (Tagged i))
-- | Returns a new and unique ordering
new :: M i Order
new = do
(i, m) <- get
put (i + 1, m)
return i
-- | Updates the order and name-tag of a node
tag :: forall i a. Name (S Symbol i a) -> Order -> M i ()
tag r o = modify $ second $ M.adjust update r
where
update :: Tagged i (S Symbol i a) -> Tagged i (S Symbol i a)
update (Tagged s _ _ n) = Tagged s o r n
-- | Marks a node as visited
visited :: Name (S Symbol i a) -> M i ()
visited r = modify $ second $ M.adjust update r
where
update :: Tagged i (S Symbol i a) -> Tagged i (S Symbol i a)
update (Tagged _ o k n) = Tagged Visited o k n
-- | Gets the status of a node
status :: Name (S Symbol i a) -> M i Status
status r = gets $ (\(Tagged s _ _ _) -> s) . (M.! r) . snd
-- | Gets the node's constructor typ
node :: Name (S Symbol i a) -> M i (S Key i a)
node r = gets $ (\(Tagged _ _ k (Node n)) -> n) . (M.! r) . snd
--------------------------------------------------------------------------------
-- * Sorter
--------------------------------------------------------------------------------
-- | Sorting of individual nodes: mark as visited, follow children, tag with order.
sort' :: Name (S Symbol i a) -> M i ()
sort' r =
do visited r
n <- node r
case n of
(Var _) -> return ()
(Repeat _) -> return ()
(Map _ s) -> visit s
(Join l r) -> visit l >> visit r
(Left l) -> visit l
(Right r) -> visit r
(Delay _ s) -> visit s
(Mux s c) -> visit s >> mapM_ (visit . snd) c
o <- new
r `tag` o
where
visit :: Key i a -> M i ()
visit (Key k) =
do s <- status k
when (s /= Visited) (sort' k)
--------------------------------------------------------------------------------
-- | Ordered keys with a witness constraint for well-formedness
data Ordered i
where
Ordered :: Witness i a => Key i a -> Ordered i
-- | Comparing ordered keys is the same as comparing the keys
instance Eq (Ordered i) where
Ordered (Key l) == Ordered (Key r) = l `eqStableName` r
-- | Given a root and a set of graph nodes, a topological ordering is produced
sorter :: Key i a -> Nodes i -> [Ordered i]
sorter (Key n) nodes = reduce $ snd $ flip execState (1, init nodes) $ sort' n
where
init :: Nodes i -> Map (Tagged i)
init = M.hmap repack
where
repack :: forall i a. Node i a -> Tagged i a
repack node@(Node (keys :: S Key i b)) = Tagged Unvisited 0 undefined node
reduce :: Map (Tagged i) -> [Ordered i]
reduce = fmap snd . sortBy (compare `on` fst) . fmap repack . M.elems
where
-- Haskell is strange sometimes...
repack :: forall i. M.Entry (Tagged i) -> (Order, Ordered i)
repack (M.Entry name (Tagged _ o k (Node _))) = (o, Ordered (Key k))
--------------------------------------------------------------------------------