{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ViewPatterns #-}
module Data.Array.Accelerate.Pretty.Graphviz.Type
where
import Data.Maybe
import Data.Hashable
import Data.List
import Text.Printf
import Text.PrettyPrint
data Tree a = Leaf a
| Forest [Tree a]
instance Functor Tree where
fmap f (Leaf x) = Leaf (f x)
fmap f (Forest xs) = Forest (map (fmap f) xs)
data Graph = Graph Label [Statement]
data Statement = N Node | E Edge | G Graph
data Node = Node (Maybe Label) NodeId (Tree (Maybe Port, Doc))
data NodeId = NodeId !Int
type Label = String
type Port = String
data Vertex = Vertex NodeId (Maybe Port)
data Edge = Edge Vertex
Vertex
deriving instance Eq NodeId
deriving instance Eq Vertex
instance Hashable NodeId where
hashWithSalt salt (NodeId ident) = hashWithSalt salt ident
instance Show Graph where
show = show . ppGraph
ppGraph :: Graph -> Doc
ppGraph (Graph l ss) =
vcat [ text "digraph" <+> text l <+> lbrace
, nest 4 $ vcat
$ punctuate semi
$ text "graph [compound=true]"
: text "node [shape=record,fontsize=10]"
: map ppStatement ss
, rbrace
]
ppSubgraph :: Graph -> Doc
ppSubgraph (Graph l ss) =
vcat [ text "subgraph cluster_" <> text l <+> lbrace
, nest 4 $ vcat
$ punctuate semi
$ text "label" <> equals <> text l
: map ppStatement ss
, rbrace
]
ppStatement :: Statement -> Doc
ppStatement (N n) = ppNode n
ppStatement (E e) = ppEdge e
ppStatement (G g) = ppSubgraph g
ppEdge :: Edge -> Doc
ppEdge (Edge from to) = ppVertex from <+> text "->" <+> ppVertex to
ppVertex :: Vertex -> Doc
ppVertex (Vertex n p) = ppNodeId n <> maybe empty (colon<>) (fmap text p)
ppNode :: Node -> Doc
ppNode (Node label nid body) =
hcat [ ppNodeId nid
, brackets
$ hcat
$ punctuate comma
$ catMaybes [ fmap ((text "xlabel" <> equals <>) . doubleQuotes . text) label
, Just ( text "label" <> equals <> doubleQuotes (ppNodeTree body))
]
]
ppNodeTree :: Tree (Maybe Port, Doc) -> Doc
ppNodeTree (Forest trees) = braces $ hcat (punctuate (char '|') (map ppNodeTree trees))
ppNodeTree (Leaf (port, body)) = maybe empty (\p -> char '<' <> p <> char '>') (fmap text port) <> pp body
where
pp :: Doc -> Doc
pp (lines . concatMap escape . renderStyle wide -> doc) =
case doc of
[] -> empty
[x] -> text x
xs -> text (intercalate "\\l" xs) <> text "\\l"
wide :: Style
wide = style { lineLength = 200 }
escape :: Char -> String
escape ' ' = "\\ "
escape '>' = "\\>"
escape '<' = "\\<"
escape '|' = "\\|"
escape c = [c]
ppNodeId :: NodeId -> Doc
ppNodeId (NodeId nid) = text (printf "Node_%#0x" nid)