graphviz 2999.1.0.2 → 2999.5.0.0
raw patch · 12 files changed
+3549/−2387 lines, 12 filesdep +prettyPVP ok
version bump matches the API change (PVP)
Dependencies added: pretty
API changes (from Hackage documentation)
- Data.GraphViz.Attributes: DEBool :: Bool -> DEConstraints
- Data.GraphViz.Attributes: EDiamond :: ArrowType
- Data.GraphViz.Attributes: Empty :: ArrowType
- Data.GraphViz.Attributes: HalfOpen :: ArrowType
- Data.GraphViz.Attributes: InvDot :: ArrowType
- Data.GraphViz.Attributes: InvEmpty :: ArrowType
- Data.GraphViz.Attributes: InvODot :: ArrowType
- Data.GraphViz.Attributes: LP :: Point -> Attribute
- Data.GraphViz.Attributes: OBox :: ArrowType
- Data.GraphViz.Attributes: ODiamond :: ArrowType
- Data.GraphViz.Attributes: ODot :: ArrowType
- Data.GraphViz.Attributes: Open :: ArrowType
- Data.GraphViz.Attributes: QS :: String -> QuotedString
- Data.GraphViz.Attributes: Random :: STStyle
- Data.GraphViz.Attributes: Resolution :: Double -> Attribute
- Data.GraphViz.Attributes: ST :: (Maybe STStyle) -> (Maybe Int) -> StartType
- Data.GraphViz.Attributes: Self :: STStyle
- Data.GraphViz.Attributes: Stl :: StyleName -> (Maybe String) -> Style
- Data.GraphViz.Attributes: bool :: a -> a -> Bool -> a
- Data.GraphViz.Attributes: data ArrowType
- Data.GraphViz.Attributes: data Style
- Data.GraphViz.Attributes: defLayerSep :: [Char]
- Data.GraphViz.Attributes: instance Eq QuotedString
- Data.GraphViz.Attributes: instance Eq Style
- Data.GraphViz.Attributes: instance Parseable ArrowType
- Data.GraphViz.Attributes: instance Parseable AspectType
- Data.GraphViz.Attributes: instance Parseable Attribute
- Data.GraphViz.Attributes: instance Parseable ClusterMode
- Data.GraphViz.Attributes: instance Parseable Color
- Data.GraphViz.Attributes: instance Parseable CompassPoint
- Data.GraphViz.Attributes: instance Parseable DEConstraints
- Data.GraphViz.Attributes: instance Parseable DPoint
- Data.GraphViz.Attributes: instance Parseable DirType
- Data.GraphViz.Attributes: instance Parseable EdgeType
- Data.GraphViz.Attributes: instance Parseable FocusType
- Data.GraphViz.Attributes: instance Parseable Justification
- Data.GraphViz.Attributes: instance Parseable Label
- Data.GraphViz.Attributes: instance Parseable LayerID
- Data.GraphViz.Attributes: instance Parseable LayerList
- Data.GraphViz.Attributes: instance Parseable LayerRange
- Data.GraphViz.Attributes: instance Parseable OutputMode
- Data.GraphViz.Attributes: instance Parseable Overlap
- Data.GraphViz.Attributes: instance Parseable Pack
- Data.GraphViz.Attributes: instance Parseable PackMode
- Data.GraphViz.Attributes: instance Parseable PageDir
- Data.GraphViz.Attributes: instance Parseable Point
- Data.GraphViz.Attributes: instance Parseable PortPos
- Data.GraphViz.Attributes: instance Parseable Pos
- Data.GraphViz.Attributes: instance Parseable QuadType
- Data.GraphViz.Attributes: instance Parseable QuotedString
- Data.GraphViz.Attributes: instance Parseable RankDir
- Data.GraphViz.Attributes: instance Parseable RankType
- Data.GraphViz.Attributes: instance Parseable Ratios
- Data.GraphViz.Attributes: instance Parseable Rect
- Data.GraphViz.Attributes: instance Parseable Root
- Data.GraphViz.Attributes: instance Parseable STStyle
- Data.GraphViz.Attributes: instance Parseable ScaleType
- Data.GraphViz.Attributes: instance Parseable Shape
- Data.GraphViz.Attributes: instance Parseable SmoothType
- Data.GraphViz.Attributes: instance Parseable Spline
- Data.GraphViz.Attributes: instance Parseable StartType
- Data.GraphViz.Attributes: instance Parseable Style
- Data.GraphViz.Attributes: instance Parseable StyleName
- Data.GraphViz.Attributes: instance Parseable URL
- Data.GraphViz.Attributes: instance Parseable VerticalPlacement
- Data.GraphViz.Attributes: instance Parseable ViewPort
- Data.GraphViz.Attributes: instance Read QuotedString
- Data.GraphViz.Attributes: instance Read Style
- Data.GraphViz.Attributes: instance Show QuotedString
- Data.GraphViz.Attributes: instance Show Style
- Data.GraphViz.Attributes: newtype QuotedString
- Data.GraphViz.Attributes: parseColor :: Parse Color
- Data.GraphViz.Attributes: parseLayerName :: Parse String
- Data.GraphViz.Attributes: parseLayerSep :: Parse String
- Data.GraphViz.Attributes: parsePoint :: Parse Point
- Data.GraphViz.Attributes: parseSpline :: Parse Spline
- Data.GraphViz.Attributes: qStr :: QuotedString -> String
- Data.GraphViz.Attributes: showColor :: Color -> String
- Data.GraphViz.Attributes: showPoint :: Point -> String
- Data.GraphViz.Attributes: showSpline :: Spline -> String
- Data.GraphViz.Attributes: showWord8Pad :: Word8 -> String -> String
- Data.GraphViz.ParserCombinators: char :: Char -> Parse Char
- Data.GraphViz.ParserCombinators: class Parseable a
- Data.GraphViz.ParserCombinators: commaSep :: (Parseable a, Parseable b) => Parse (a, b)
- Data.GraphViz.ParserCombinators: commaSep' :: Parse a -> Parse b -> Parse (a, b)
- Data.GraphViz.ParserCombinators: hasString :: String -> Parse Bool
- Data.GraphViz.ParserCombinators: instance (Parseable a) => Parseable [a]
- Data.GraphViz.ParserCombinators: instance Parseable Bool
- Data.GraphViz.ParserCombinators: instance Parseable Char
- Data.GraphViz.ParserCombinators: instance Parseable Double
- Data.GraphViz.ParserCombinators: instance Parseable Int
- Data.GraphViz.ParserCombinators: newline :: Parse String
- Data.GraphViz.ParserCombinators: optionalQuoted :: Parse a -> Parse a
- Data.GraphViz.ParserCombinators: optionalQuotedString :: String -> Parse String
- Data.GraphViz.ParserCombinators: parse :: (Parseable a) => Parse a
- Data.GraphViz.ParserCombinators: parseAndSpace :: Parse a -> Parse a
- Data.GraphViz.ParserCombinators: parseBoolField :: String -> Parse Bool
- Data.GraphViz.ParserCombinators: parseField :: (Parseable a) => String -> Parse a
- Data.GraphViz.ParserCombinators: parseFieldDef :: (Parseable a) => a -> String -> Parse a
- Data.GraphViz.ParserCombinators: parseList :: (Parseable a) => Parse [a]
- Data.GraphViz.ParserCombinators: quotedParse :: Parse a -> Parse a
- Data.GraphViz.ParserCombinators: quotedString :: Parse String
- Data.GraphViz.ParserCombinators: skipToNewline :: Parse ()
- Data.GraphViz.ParserCombinators: string :: String -> Parse String
- Data.GraphViz.ParserCombinators: stringBlock :: Parse String
- Data.GraphViz.ParserCombinators: strings :: [String] -> Parse String
- Data.GraphViz.ParserCombinators: type Parse a = Parser Char a
- Data.GraphViz.ParserCombinators: whitespace :: Parse String
- Data.GraphViz.ParserCombinators: whitespace' :: Parse String
- Data.GraphViz.Types: DotCluster :: String -> [Attribute] -> [DotNode] -> DotNode
- Data.GraphViz.Types: Num :: Double -> GraphID
- Data.GraphViz.Types: QStr :: QuotedString -> GraphID
- Data.GraphViz.Types: clusterAttributes :: DotNode -> [Attribute]
- Data.GraphViz.Types: clusterElems :: DotNode -> [DotNode]
- Data.GraphViz.Types: clusterID :: DotNode -> String
- Data.GraphViz.Types: edgeHeadNodeID :: DotEdge -> Int
- Data.GraphViz.Types: edgeTailNodeID :: DotEdge -> Int
- Data.GraphViz.Types: graphAttributes :: DotGraph -> [Attribute]
- Data.GraphViz.Types: instance Eq DotEdge
- Data.GraphViz.Types: instance Eq DotGraph
- Data.GraphViz.Types: instance Eq DotNode
- Data.GraphViz.Types: instance Parseable DotEdge
- Data.GraphViz.Types: instance Parseable DotGraph
- Data.GraphViz.Types: instance Parseable DotNode
- Data.GraphViz.Types: instance Parseable GraphID
- Data.GraphViz.Types: instance Read DotEdge
- Data.GraphViz.Types: instance Read DotGraph
- Data.GraphViz.Types: instance Read DotNode
- Data.GraphViz.Types: instance Show DotEdge
- Data.GraphViz.Types: instance Show DotGraph
- Data.GraphViz.Types: instance Show DotNode
- Data.GraphViz.Types: invalidAttributes :: DotGraph -> ([Attribute], [(DotNode, Attribute)], [(DotEdge, Attribute)])
+ Data.GraphViz: clusterGraphToDot' :: (Ord c, Ord b, Graph gr) => gr a b -> [GlobalAttributes] -> (LNode a -> NodeCluster c a) -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes]) -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> DotGraph Node
+ Data.GraphViz: clusterGraphToGraph :: (Ord c, Graph gr) => Bool -> gr a b -> [GlobalAttributes] -> (LNode a -> NodeCluster c a) -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes]) -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> IO (gr (AttributeNode a) (AttributeEdge b))
+ Data.GraphViz: clusterGraphToGraph' :: (Ord b, Ord c, Graph gr) => gr a b -> [GlobalAttributes] -> (LNode a -> NodeCluster c a) -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes]) -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> IO (gr (AttributeNode a) (AttributeEdge b))
+ Data.GraphViz: dotizeClusterGraph :: (Ord c, Graph gr) => Bool -> gr a b -> (LNode a -> NodeCluster c a) -> gr (AttributeNode a) (AttributeEdge b)
+ Data.GraphViz: dotizeClusterGraph' :: (Ord b, Ord c, Graph gr) => gr a b -> (LNode a -> NodeCluster c a) -> gr (AttributeNode a) (AttributeEdge b)
+ Data.GraphViz: dotizeGraph' :: (Graph gr, Ord b) => gr a b -> gr (AttributeNode a) (AttributeEdge b)
+ Data.GraphViz: graphToDot' :: (Ord b, Graph gr) => gr a b -> [GlobalAttributes] -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> DotGraph Node
+ Data.GraphViz: graphToGraph' :: (Ord b, Graph gr) => gr a b -> [GlobalAttributes] -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> IO (gr (AttributeNode a) (AttributeEdge b))
+ Data.GraphViz.Attributes: AType :: [(ArrowModifier, ArrowShape)] -> ArrowType
+ Data.GraphViz.Attributes: ArrMod :: ArrowFill -> ArrowSide -> ArrowModifier
+ Data.GraphViz.Attributes: BothSides :: ArrowSide
+ Data.GraphViz.Attributes: Circuit :: Model
+ Data.GraphViz.Attributes: DD :: String -> StyleName
+ Data.GraphViz.Attributes: EdgeConstraints :: DEConstraints
+ Data.GraphViz.Attributes: FilledArrow :: ArrowFill
+ Data.GraphViz.Attributes: HierConstraints :: DEConstraints
+ Data.GraphViz.Attributes: IpSep :: ModeType
+ Data.GraphViz.Attributes: KK :: ModeType
+ Data.GraphViz.Attributes: LPos :: Point -> Attribute
+ Data.GraphViz.Attributes: LeftSide :: ArrowSide
+ Data.GraphViz.Attributes: Major :: ModeType
+ Data.GraphViz.Attributes: NoConstraints :: DEConstraints
+ Data.GraphViz.Attributes: OpenArrow :: ArrowFill
+ Data.GraphViz.Attributes: RandomStyle :: STStyle
+ Data.GraphViz.Attributes: RightSide :: ArrowSide
+ Data.GraphViz.Attributes: SItem :: StyleName -> [String] -> StyleItem
+ Data.GraphViz.Attributes: SelfStyle :: STStyle
+ Data.GraphViz.Attributes: ShortPath :: Model
+ Data.GraphViz.Attributes: StartSeed :: Int -> StartType
+ Data.GraphViz.Attributes: StartStyle :: STStyle -> StartType
+ Data.GraphViz.Attributes: StartStyleSeed :: STStyle -> Int -> StartType
+ Data.GraphViz.Attributes: SubSet :: Model
+ Data.GraphViz.Attributes: arrowFill :: ArrowModifier -> ArrowFill
+ Data.GraphViz.Attributes: arrowSide :: ArrowModifier -> ArrowSide
+ Data.GraphViz.Attributes: box :: ArrowType
+ Data.GraphViz.Attributes: crow :: ArrowType
+ Data.GraphViz.Attributes: data ArrowFill
+ Data.GraphViz.Attributes: data ArrowModifier
+ Data.GraphViz.Attributes: data ArrowShape
+ Data.GraphViz.Attributes: data ArrowSide
+ Data.GraphViz.Attributes: data ModeType
+ Data.GraphViz.Attributes: data Model
+ Data.GraphViz.Attributes: data StyleItem
+ Data.GraphViz.Attributes: diamond :: ArrowType
+ Data.GraphViz.Attributes: dotArrow :: ArrowType
+ Data.GraphViz.Attributes: eDiamond :: ArrowType
+ Data.GraphViz.Attributes: emptyArr :: ArrowType
+ Data.GraphViz.Attributes: halfOpen :: ArrowType
+ Data.GraphViz.Attributes: instance Eq ArrowFill
+ Data.GraphViz.Attributes: instance Eq ArrowModifier
+ Data.GraphViz.Attributes: instance Eq ArrowShape
+ Data.GraphViz.Attributes: instance Eq ArrowSide
+ Data.GraphViz.Attributes: instance Eq ModeType
+ Data.GraphViz.Attributes: instance Eq Model
+ Data.GraphViz.Attributes: instance Eq StyleItem
+ Data.GraphViz.Attributes: instance ParseDot ArrowFill
+ Data.GraphViz.Attributes: instance ParseDot ArrowModifier
+ Data.GraphViz.Attributes: instance ParseDot ArrowShape
+ Data.GraphViz.Attributes: instance ParseDot ArrowSide
+ Data.GraphViz.Attributes: instance ParseDot ArrowType
+ Data.GraphViz.Attributes: instance ParseDot AspectType
+ Data.GraphViz.Attributes: instance ParseDot Attribute
+ Data.GraphViz.Attributes: instance ParseDot ClusterMode
+ Data.GraphViz.Attributes: instance ParseDot Color
+ Data.GraphViz.Attributes: instance ParseDot CompassPoint
+ Data.GraphViz.Attributes: instance ParseDot DEConstraints
+ Data.GraphViz.Attributes: instance ParseDot DPoint
+ Data.GraphViz.Attributes: instance ParseDot DirType
+ Data.GraphViz.Attributes: instance ParseDot EdgeType
+ Data.GraphViz.Attributes: instance ParseDot FocusType
+ Data.GraphViz.Attributes: instance ParseDot Justification
+ Data.GraphViz.Attributes: instance ParseDot Label
+ Data.GraphViz.Attributes: instance ParseDot LayerID
+ Data.GraphViz.Attributes: instance ParseDot LayerList
+ Data.GraphViz.Attributes: instance ParseDot LayerRange
+ Data.GraphViz.Attributes: instance ParseDot ModeType
+ Data.GraphViz.Attributes: instance ParseDot Model
+ Data.GraphViz.Attributes: instance ParseDot OutputMode
+ Data.GraphViz.Attributes: instance ParseDot Overlap
+ Data.GraphViz.Attributes: instance ParseDot Pack
+ Data.GraphViz.Attributes: instance ParseDot PackMode
+ Data.GraphViz.Attributes: instance ParseDot PageDir
+ Data.GraphViz.Attributes: instance ParseDot Point
+ Data.GraphViz.Attributes: instance ParseDot PortPos
+ Data.GraphViz.Attributes: instance ParseDot Pos
+ Data.GraphViz.Attributes: instance ParseDot QuadType
+ Data.GraphViz.Attributes: instance ParseDot RankDir
+ Data.GraphViz.Attributes: instance ParseDot RankType
+ Data.GraphViz.Attributes: instance ParseDot Ratios
+ Data.GraphViz.Attributes: instance ParseDot Rect
+ Data.GraphViz.Attributes: instance ParseDot Root
+ Data.GraphViz.Attributes: instance ParseDot STStyle
+ Data.GraphViz.Attributes: instance ParseDot ScaleType
+ Data.GraphViz.Attributes: instance ParseDot Shape
+ Data.GraphViz.Attributes: instance ParseDot SmoothType
+ Data.GraphViz.Attributes: instance ParseDot Spline
+ Data.GraphViz.Attributes: instance ParseDot StartType
+ Data.GraphViz.Attributes: instance ParseDot StyleItem
+ Data.GraphViz.Attributes: instance ParseDot StyleName
+ Data.GraphViz.Attributes: instance ParseDot URL
+ Data.GraphViz.Attributes: instance ParseDot VerticalPlacement
+ Data.GraphViz.Attributes: instance ParseDot ViewPort
+ Data.GraphViz.Attributes: instance PrintDot ArrowFill
+ Data.GraphViz.Attributes: instance PrintDot ArrowModifier
+ Data.GraphViz.Attributes: instance PrintDot ArrowShape
+ Data.GraphViz.Attributes: instance PrintDot ArrowSide
+ Data.GraphViz.Attributes: instance PrintDot ArrowType
+ Data.GraphViz.Attributes: instance PrintDot AspectType
+ Data.GraphViz.Attributes: instance PrintDot Attribute
+ Data.GraphViz.Attributes: instance PrintDot ClusterMode
+ Data.GraphViz.Attributes: instance PrintDot Color
+ Data.GraphViz.Attributes: instance PrintDot CompassPoint
+ Data.GraphViz.Attributes: instance PrintDot DEConstraints
+ Data.GraphViz.Attributes: instance PrintDot DPoint
+ Data.GraphViz.Attributes: instance PrintDot DirType
+ Data.GraphViz.Attributes: instance PrintDot EdgeType
+ Data.GraphViz.Attributes: instance PrintDot FocusType
+ Data.GraphViz.Attributes: instance PrintDot Justification
+ Data.GraphViz.Attributes: instance PrintDot Label
+ Data.GraphViz.Attributes: instance PrintDot LayerID
+ Data.GraphViz.Attributes: instance PrintDot LayerList
+ Data.GraphViz.Attributes: instance PrintDot LayerRange
+ Data.GraphViz.Attributes: instance PrintDot ModeType
+ Data.GraphViz.Attributes: instance PrintDot Model
+ Data.GraphViz.Attributes: instance PrintDot OutputMode
+ Data.GraphViz.Attributes: instance PrintDot Overlap
+ Data.GraphViz.Attributes: instance PrintDot Pack
+ Data.GraphViz.Attributes: instance PrintDot PackMode
+ Data.GraphViz.Attributes: instance PrintDot PageDir
+ Data.GraphViz.Attributes: instance PrintDot Point
+ Data.GraphViz.Attributes: instance PrintDot PortPos
+ Data.GraphViz.Attributes: instance PrintDot Pos
+ Data.GraphViz.Attributes: instance PrintDot QuadType
+ Data.GraphViz.Attributes: instance PrintDot RankDir
+ Data.GraphViz.Attributes: instance PrintDot RankType
+ Data.GraphViz.Attributes: instance PrintDot Ratios
+ Data.GraphViz.Attributes: instance PrintDot Rect
+ Data.GraphViz.Attributes: instance PrintDot Root
+ Data.GraphViz.Attributes: instance PrintDot STStyle
+ Data.GraphViz.Attributes: instance PrintDot ScaleType
+ Data.GraphViz.Attributes: instance PrintDot Shape
+ Data.GraphViz.Attributes: instance PrintDot SmoothType
+ Data.GraphViz.Attributes: instance PrintDot Spline
+ Data.GraphViz.Attributes: instance PrintDot StartType
+ Data.GraphViz.Attributes: instance PrintDot StyleItem
+ Data.GraphViz.Attributes: instance PrintDot StyleName
+ Data.GraphViz.Attributes: instance PrintDot URL
+ Data.GraphViz.Attributes: instance PrintDot VerticalPlacement
+ Data.GraphViz.Attributes: instance PrintDot ViewPort
+ Data.GraphViz.Attributes: instance Read ArrowFill
+ Data.GraphViz.Attributes: instance Read ArrowModifier
+ Data.GraphViz.Attributes: instance Read ArrowShape
+ Data.GraphViz.Attributes: instance Read ArrowSide
+ Data.GraphViz.Attributes: instance Read ModeType
+ Data.GraphViz.Attributes: instance Read Model
+ Data.GraphViz.Attributes: instance Read StyleItem
+ Data.GraphViz.Attributes: instance Show ArrowFill
+ Data.GraphViz.Attributes: instance Show ArrowModifier
+ Data.GraphViz.Attributes: instance Show ArrowShape
+ Data.GraphViz.Attributes: instance Show ArrowSide
+ Data.GraphViz.Attributes: instance Show ModeType
+ Data.GraphViz.Attributes: instance Show Model
+ Data.GraphViz.Attributes: instance Show StyleItem
+ Data.GraphViz.Attributes: inv :: ArrowType
+ Data.GraphViz.Attributes: invDot :: ArrowType
+ Data.GraphViz.Attributes: invEmpty :: ArrowType
+ Data.GraphViz.Attributes: invODot :: ArrowType
+ Data.GraphViz.Attributes: newtype ArrowType
+ Data.GraphViz.Attributes: noArrow :: ArrowType
+ Data.GraphViz.Attributes: noMods :: ArrowModifier
+ Data.GraphViz.Attributes: normal :: ArrowType
+ Data.GraphViz.Attributes: oBox :: ArrowType
+ Data.GraphViz.Attributes: oDiamond :: ArrowType
+ Data.GraphViz.Attributes: oDot :: ArrowType
+ Data.GraphViz.Attributes: openArr :: ArrowType
+ Data.GraphViz.Attributes: openMod :: ArrowModifier
+ Data.GraphViz.Attributes: tee :: ArrowType
+ Data.GraphViz.Attributes: type Attributes = [Attribute]
+ Data.GraphViz.Attributes: type EscString = String
+ Data.GraphViz.Attributes: vee :: ArrowType
+ Data.GraphViz.Types: Dbl :: Double -> GraphID
+ Data.GraphViz.Types: DotSG :: Bool -> Maybe GraphID -> DotStatements a -> DotSubGraph a
+ Data.GraphViz.Types: DotStmts :: [GlobalAttributes] -> [DotSubGraph a] -> [DotNode a] -> [DotEdge a] -> DotStatements a
+ Data.GraphViz.Types: EdgeAttrs :: Attributes -> GlobalAttributes
+ Data.GraphViz.Types: EdgeError :: (Maybe (a, a)) -> Attribute -> DotError a
+ Data.GraphViz.Types: GraphAttrs :: Attributes -> GlobalAttributes
+ Data.GraphViz.Types: GraphError :: Attribute -> DotError a
+ Data.GraphViz.Types: Int :: Int -> GraphID
+ Data.GraphViz.Types: NodeAttrs :: Attributes -> GlobalAttributes
+ Data.GraphViz.Types: NodeError :: (Maybe a) -> Attribute -> DotError a
+ Data.GraphViz.Types: attrStmts :: DotStatements a -> [GlobalAttributes]
+ Data.GraphViz.Types: attrs :: GlobalAttributes -> Attributes
+ Data.GraphViz.Types: data DotError a
+ Data.GraphViz.Types: data DotStatements a
+ Data.GraphViz.Types: data DotSubGraph a
+ Data.GraphViz.Types: data GlobalAttributes
+ Data.GraphViz.Types: edgeFromNodeID :: DotEdge a -> a
+ Data.GraphViz.Types: edgeStmts :: DotStatements a -> [DotEdge a]
+ Data.GraphViz.Types: edgeToNodeID :: DotEdge a -> a
+ Data.GraphViz.Types: graphErrors :: DotGraph a -> [DotError a]
+ Data.GraphViz.Types: graphStatements :: DotGraph a -> DotStatements a
+ Data.GraphViz.Types: instance (Eq a) => Eq (DotEdge a)
+ Data.GraphViz.Types: instance (Eq a) => Eq (DotError a)
+ Data.GraphViz.Types: instance (Eq a) => Eq (DotGraph a)
+ Data.GraphViz.Types: instance (Eq a) => Eq (DotNode a)
+ Data.GraphViz.Types: instance (Eq a) => Eq (DotStatements a)
+ Data.GraphViz.Types: instance (Eq a) => Eq (DotSubGraph a)
+ Data.GraphViz.Types: instance (ParseDot a) => ParseDot (DotEdge a)
+ Data.GraphViz.Types: instance (ParseDot a) => ParseDot (DotGraph a)
+ Data.GraphViz.Types: instance (ParseDot a) => ParseDot (DotNode a)
+ Data.GraphViz.Types: instance (ParseDot a) => ParseDot (DotStatements a)
+ Data.GraphViz.Types: instance (ParseDot a) => ParseDot (DotSubGraph a)
+ Data.GraphViz.Types: instance (PrintDot a) => PrintDot (DotEdge a)
+ Data.GraphViz.Types: instance (PrintDot a) => PrintDot (DotGraph a)
+ Data.GraphViz.Types: instance (PrintDot a) => PrintDot (DotNode a)
+ Data.GraphViz.Types: instance (PrintDot a) => PrintDot (DotStatements a)
+ Data.GraphViz.Types: instance (PrintDot a) => PrintDot (DotSubGraph a)
+ Data.GraphViz.Types: instance (Read a) => Read (DotEdge a)
+ Data.GraphViz.Types: instance (Read a) => Read (DotError a)
+ Data.GraphViz.Types: instance (Read a) => Read (DotGraph a)
+ Data.GraphViz.Types: instance (Read a) => Read (DotNode a)
+ Data.GraphViz.Types: instance (Read a) => Read (DotStatements a)
+ Data.GraphViz.Types: instance (Read a) => Read (DotSubGraph a)
+ Data.GraphViz.Types: instance (Show a) => Show (DotEdge a)
+ Data.GraphViz.Types: instance (Show a) => Show (DotError a)
+ Data.GraphViz.Types: instance (Show a) => Show (DotGraph a)
+ Data.GraphViz.Types: instance (Show a) => Show (DotNode a)
+ Data.GraphViz.Types: instance (Show a) => Show (DotStatements a)
+ Data.GraphViz.Types: instance (Show a) => Show (DotSubGraph a)
+ Data.GraphViz.Types: instance Eq GlobalAttributes
+ Data.GraphViz.Types: instance Functor DotEdge
+ Data.GraphViz.Types: instance Functor DotGraph
+ Data.GraphViz.Types: instance Functor DotNode
+ Data.GraphViz.Types: instance Functor DotStatements
+ Data.GraphViz.Types: instance Functor DotSubGraph
+ Data.GraphViz.Types: instance ParseDot GlobalAttributes
+ Data.GraphViz.Types: instance ParseDot GraphID
+ Data.GraphViz.Types: instance PrintDot GlobalAttributes
+ Data.GraphViz.Types: instance PrintDot GraphID
+ Data.GraphViz.Types: instance Read GlobalAttributes
+ Data.GraphViz.Types: instance Show GlobalAttributes
+ Data.GraphViz.Types: isCluster :: DotSubGraph a -> Bool
+ Data.GraphViz.Types: nodeStmts :: DotStatements a -> [DotNode a]
+ Data.GraphViz.Types: printDotGraph :: (PrintDot a) => DotGraph a -> String
+ Data.GraphViz.Types: subGraphID :: DotSubGraph a -> Maybe GraphID
+ Data.GraphViz.Types: subGraphStmts :: DotSubGraph a -> DotStatements a
+ Data.GraphViz.Types: subGraphs :: DotStatements a -> [DotSubGraph a]
+ Data.GraphViz.Types.Parsing: character :: Char -> Parse Char
+ Data.GraphViz.Types.Parsing: class ParseDot a
+ Data.GraphViz.Types.Parsing: commaSep :: (ParseDot a, ParseDot b) => Parse (a, b)
+ Data.GraphViz.Types.Parsing: commaSep' :: Parse a -> Parse b -> Parse (a, b)
+ Data.GraphViz.Types.Parsing: hasString :: String -> Parse Bool
+ Data.GraphViz.Types.Parsing: instance (ParseDot a) => ParseDot [a]
+ Data.GraphViz.Types.Parsing: instance ParseDot Bool
+ Data.GraphViz.Types.Parsing: instance ParseDot Char
+ Data.GraphViz.Types.Parsing: instance ParseDot Double
+ Data.GraphViz.Types.Parsing: instance ParseDot Int
+ Data.GraphViz.Types.Parsing: newline :: Parse String
+ Data.GraphViz.Types.Parsing: newline' :: Parse ()
+ Data.GraphViz.Types.Parsing: noneOf :: (Eq a) => [a] -> Parser a a
+ Data.GraphViz.Types.Parsing: numString :: Parse String
+ Data.GraphViz.Types.Parsing: optionalQuoted :: Parse a -> Parse a
+ Data.GraphViz.Types.Parsing: optionalQuotedString :: String -> Parse String
+ Data.GraphViz.Types.Parsing: parse :: (ParseDot a) => Parse a
+ Data.GraphViz.Types.Parsing: parseAndSpace :: Parse a -> Parse a
+ Data.GraphViz.Types.Parsing: parseComma :: Parse Char
+ Data.GraphViz.Types.Parsing: parseField :: (ParseDot a) => String -> Parse a
+ Data.GraphViz.Types.Parsing: parseFieldBool :: String -> Parse Bool
+ Data.GraphViz.Types.Parsing: parseFieldDef :: (ParseDot a) => a -> String -> Parse a
+ Data.GraphViz.Types.Parsing: parseFields :: (ParseDot a) => [String] -> Parse a
+ Data.GraphViz.Types.Parsing: parseFieldsBool :: [String] -> Parse Bool
+ Data.GraphViz.Types.Parsing: parseFieldsDef :: (ParseDot a) => a -> [String] -> Parse a
+ Data.GraphViz.Types.Parsing: parseList :: (ParseDot a) => Parse [a]
+ Data.GraphViz.Types.Parsing: parseUnqt :: (ParseDot a) => Parse a
+ Data.GraphViz.Types.Parsing: parseUnqtList :: (ParseDot a) => Parse [a]
+ Data.GraphViz.Types.Parsing: quotedParse :: Parse a -> Parse a
+ Data.GraphViz.Types.Parsing: quotedString :: Parse String
+ Data.GraphViz.Types.Parsing: skipToNewline :: Parse ()
+ Data.GraphViz.Types.Parsing: string :: String -> Parse String
+ Data.GraphViz.Types.Parsing: stringBlock :: Parse String
+ Data.GraphViz.Types.Parsing: stringRep :: a -> String -> Parse a
+ Data.GraphViz.Types.Parsing: strings :: [String] -> Parse String
+ Data.GraphViz.Types.Parsing: tryParseList :: (ParseDot a) => Parse [a]
+ Data.GraphViz.Types.Parsing: tryParseList' :: Parse [a] -> Parse [a]
+ Data.GraphViz.Types.Parsing: type Parse a = Parser Char a
+ Data.GraphViz.Types.Parsing: whitespace :: Parse String
+ Data.GraphViz.Types.Parsing: whitespace' :: Parse String
+ Data.GraphViz.Types.Printing: class PrintDot a
+ Data.GraphViz.Types.Printing: commaDel :: (PrintDot a, PrintDot b) => a -> b -> DotCode
+ Data.GraphViz.Types.Printing: instance (PrintDot a) => PrintDot [a]
+ Data.GraphViz.Types.Printing: instance PrintDot Bool
+ Data.GraphViz.Types.Printing: instance PrintDot Char
+ Data.GraphViz.Types.Printing: instance PrintDot Double
+ Data.GraphViz.Types.Printing: instance PrintDot Int
+ Data.GraphViz.Types.Printing: listToDot :: (PrintDot a) => [a] -> DotCode
+ Data.GraphViz.Types.Printing: printField :: (PrintDot a) => String -> a -> DotCode
+ Data.GraphViz.Types.Printing: renderDot :: DotCode -> String
+ Data.GraphViz.Types.Printing: toDot :: (PrintDot a) => a -> DotCode
+ Data.GraphViz.Types.Printing: type DotCode = Doc
+ Data.GraphViz.Types.Printing: unqtDot :: (PrintDot a) => a -> DotCode
+ Data.GraphViz.Types.Printing: unqtListToDot :: (PrintDot a) => [a] -> DotCode
+ Data.GraphViz.Types.Printing: wrap :: DotCode -> DotCode -> DotCode -> DotCode
- Data.GraphViz: clusterGraphToDot :: (Ord c, Ord b, Graph gr) => gr a b -> [Attribute] -> (LNode a -> NodeCluster c a) -> (c -> [Attribute]) -> (LNode a -> [Attribute]) -> (LEdge b -> [Attribute]) -> DotGraph
+ Data.GraphViz: clusterGraphToDot :: (Ord c, Graph gr) => Bool -> gr a b -> [GlobalAttributes] -> (LNode a -> NodeCluster c a) -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes]) -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> DotGraph Node
- Data.GraphViz: dotizeGraph :: (DynGraph gr, Ord b) => gr a b -> gr (AttributeNode a) (AttributeEdge b)
+ Data.GraphViz: dotizeGraph :: (Graph gr) => Bool -> gr a b -> gr (AttributeNode a) (AttributeEdge b)
- Data.GraphViz: graphToDot :: (Ord b, Graph gr) => gr a b -> [Attribute] -> (LNode a -> [Attribute]) -> (LEdge b -> [Attribute]) -> DotGraph
+ Data.GraphViz: graphToDot :: (Graph gr) => Bool -> gr a b -> [GlobalAttributes] -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> DotGraph Node
- Data.GraphViz: graphToGraph :: (Ord b, Graph gr) => gr a b -> [Attribute] -> (LNode a -> [Attribute]) -> (LEdge b -> [Attribute]) -> IO (gr (AttributeNode a) (AttributeEdge b))
+ Data.GraphViz: graphToGraph :: (Graph gr) => Bool -> gr a b -> [GlobalAttributes] -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> IO (gr (AttributeNode a) (AttributeEdge b))
- Data.GraphViz: type AttributeEdge b = ([Attribute], b)
+ Data.GraphViz: type AttributeEdge b = (Attributes, b)
- Data.GraphViz: type AttributeNode a = ([Attribute], a)
+ Data.GraphViz: type AttributeNode a = (Attributes, a)
- Data.GraphViz.Attributes: Box :: ArrowType
+ Data.GraphViz.Attributes: Box :: ArrowShape
- Data.GraphViz.Attributes: Crow :: ArrowType
+ Data.GraphViz.Attributes: Crow :: ArrowShape
- Data.GraphViz.Attributes: Diamond :: ArrowType
+ Data.GraphViz.Attributes: Diamond :: ArrowShape
- Data.GraphViz.Attributes: DotArrow :: ArrowType
+ Data.GraphViz.Attributes: DotArrow :: ArrowShape
- Data.GraphViz.Attributes: EdgeTarget :: String -> Attribute
+ Data.GraphViz.Attributes: EdgeTarget :: EscString -> Attribute
- Data.GraphViz.Attributes: EdgeTooltip :: String -> Attribute
+ Data.GraphViz.Attributes: EdgeTooltip :: EscString -> Attribute
- Data.GraphViz.Attributes: HSV :: Int -> Int -> Int -> Color
+ Data.GraphViz.Attributes: HSV :: Double -> Double -> Double -> Color
- Data.GraphViz.Attributes: HeadTarget :: QuotedString -> Attribute
+ Data.GraphViz.Attributes: HeadTarget :: EscString -> Attribute
- Data.GraphViz.Attributes: HeadTooltip :: QuotedString -> Attribute
+ Data.GraphViz.Attributes: HeadTooltip :: EscString -> Attribute
- Data.GraphViz.Attributes: Hier :: DEConstraints
+ Data.GraphViz.Attributes: Hier :: ModeType
- Data.GraphViz.Attributes: Inv :: ArrowType
+ Data.GraphViz.Attributes: Inv :: ArrowShape
- Data.GraphViz.Attributes: LabelTarget :: String -> Attribute
+ Data.GraphViz.Attributes: LabelTarget :: EscString -> Attribute
- Data.GraphViz.Attributes: LabelTooltip :: String -> Attribute
+ Data.GraphViz.Attributes: LabelTooltip :: EscString -> Attribute
- Data.GraphViz.Attributes: Mode :: String -> Attribute
+ Data.GraphViz.Attributes: Mode :: ModeType -> Attribute
- Data.GraphViz.Attributes: Model :: String -> Attribute
+ Data.GraphViz.Attributes: Model :: Model -> Attribute
- Data.GraphViz.Attributes: NoArrow :: ArrowType
+ Data.GraphViz.Attributes: NoArrow :: ArrowShape
- Data.GraphViz.Attributes: Normal :: ArrowType
+ Data.GraphViz.Attributes: Normal :: ArrowShape
- Data.GraphViz.Attributes: StrLabel :: String -> Label
+ Data.GraphViz.Attributes: StrLabel :: EscString -> Label
- Data.GraphViz.Attributes: Style :: Style -> Attribute
+ Data.GraphViz.Attributes: Style :: [StyleItem] -> Attribute
- Data.GraphViz.Attributes: TailTarget :: String -> Attribute
+ Data.GraphViz.Attributes: TailTarget :: EscString -> Attribute
- Data.GraphViz.Attributes: TailTooltip :: String -> Attribute
+ Data.GraphViz.Attributes: TailTooltip :: EscString -> Attribute
- Data.GraphViz.Attributes: Target :: String -> Attribute
+ Data.GraphViz.Attributes: Target :: EscString -> Attribute
- Data.GraphViz.Attributes: Tee :: ArrowType
+ Data.GraphViz.Attributes: Tee :: ArrowShape
- Data.GraphViz.Attributes: Tooltip :: String -> Attribute
+ Data.GraphViz.Attributes: Tooltip :: EscString -> Attribute
- Data.GraphViz.Attributes: UStr :: String -> URL
+ Data.GraphViz.Attributes: UStr :: EscString -> URL
- Data.GraphViz.Attributes: Vee :: ArrowType
+ Data.GraphViz.Attributes: Vee :: ArrowShape
- Data.GraphViz.Attributes: hue :: Color -> Int
+ Data.GraphViz.Attributes: hue :: Color -> Double
- Data.GraphViz.Attributes: saturation :: Color -> Int
+ Data.GraphViz.Attributes: saturation :: Color -> Double
- Data.GraphViz.Attributes: urlString :: URL -> String
+ Data.GraphViz.Attributes: urlString :: URL -> EscString
- Data.GraphViz.Attributes: value :: Color -> Int
+ Data.GraphViz.Attributes: value :: Color -> Double
- Data.GraphViz.Commands: commandFor :: DotGraph -> GraphvizCommand
+ Data.GraphViz.Commands: commandFor :: DotGraph a -> GraphvizCommand
- Data.GraphViz.Commands: graphvizWithHandle :: (Show a) => GraphvizCommand -> DotGraph -> GraphvizOutput -> (Handle -> IO a) -> IO (Maybe a)
+ Data.GraphViz.Commands: graphvizWithHandle :: (PrintDot n, Show a) => GraphvizCommand -> DotGraph n -> GraphvizOutput -> (Handle -> IO a) -> IO (Maybe a)
- Data.GraphViz.Commands: runGraphviz :: DotGraph -> GraphvizOutput -> FilePath -> IO Bool
+ Data.GraphViz.Commands: runGraphviz :: (PrintDot n) => DotGraph n -> GraphvizOutput -> FilePath -> IO Bool
- Data.GraphViz.Commands: runGraphvizCommand :: GraphvizCommand -> DotGraph -> GraphvizOutput -> FilePath -> IO Bool
+ Data.GraphViz.Commands: runGraphvizCommand :: (PrintDot n) => GraphvizCommand -> DotGraph n -> GraphvizOutput -> FilePath -> IO Bool
- Data.GraphViz.Types: DotEdge :: Int -> Int -> [Attribute] -> Bool -> DotEdge
+ Data.GraphViz.Types: DotEdge :: a -> a -> Bool -> Attributes -> DotEdge a
- Data.GraphViz.Types: DotGraph :: Bool -> Bool -> Maybe GraphID -> [Attribute] -> [DotNode] -> [DotEdge] -> DotGraph
+ Data.GraphViz.Types: DotGraph :: Bool -> Bool -> Maybe GraphID -> DotStatements a -> DotGraph a
- Data.GraphViz.Types: DotNode :: Int -> [Attribute] -> DotNode
+ Data.GraphViz.Types: DotNode :: a -> Attributes -> DotNode a
- Data.GraphViz.Types: data DotEdge
+ Data.GraphViz.Types: data DotEdge a
- Data.GraphViz.Types: data DotGraph
+ Data.GraphViz.Types: data DotGraph a
- Data.GraphViz.Types: data DotNode
+ Data.GraphViz.Types: data DotNode a
- Data.GraphViz.Types: directedEdge :: DotEdge -> Bool
+ Data.GraphViz.Types: directedEdge :: DotEdge a -> Bool
- Data.GraphViz.Types: directedGraph :: DotGraph -> Bool
+ Data.GraphViz.Types: directedGraph :: DotGraph a -> Bool
- Data.GraphViz.Types: edgeAttributes :: DotEdge -> [Attribute]
+ Data.GraphViz.Types: edgeAttributes :: DotEdge a -> Attributes
- Data.GraphViz.Types: graphEdges :: DotGraph -> [DotEdge]
+ Data.GraphViz.Types: graphEdges :: DotGraph a -> [DotEdge a]
- Data.GraphViz.Types: graphID :: DotGraph -> Maybe GraphID
+ Data.GraphViz.Types: graphID :: DotGraph a -> Maybe GraphID
- Data.GraphViz.Types: graphNodes :: DotGraph -> [DotNode]
+ Data.GraphViz.Types: graphNodes :: DotGraph a -> [DotNode a]
- Data.GraphViz.Types: isValidGraph :: DotGraph -> Bool
+ Data.GraphViz.Types: isValidGraph :: DotGraph a -> Bool
- Data.GraphViz.Types: makeStrict :: DotGraph -> DotGraph
+ Data.GraphViz.Types: makeStrict :: DotGraph a -> DotGraph a
- Data.GraphViz.Types: nodeAttributes :: DotNode -> [Attribute]
+ Data.GraphViz.Types: nodeAttributes :: DotNode a -> Attributes
- Data.GraphViz.Types: nodeID :: DotNode -> Int
+ Data.GraphViz.Types: nodeID :: DotNode a -> a
- Data.GraphViz.Types: parseDotGraph :: Parse DotGraph
+ Data.GraphViz.Types: parseDotGraph :: (ParseDot a) => String -> DotGraph a
- Data.GraphViz.Types: setID :: GraphID -> DotGraph -> DotGraph
+ Data.GraphViz.Types: setID :: GraphID -> DotGraph a -> DotGraph a
- Data.GraphViz.Types: strictGraph :: DotGraph -> Bool
+ Data.GraphViz.Types: strictGraph :: DotGraph a -> Bool
Files
- Changelog +53/−0
- Data/GraphViz.hs +213/−62
- Data/GraphViz/Attributes.hs +2195/−1849
- Data/GraphViz/Commands.hs +9/−7
- Data/GraphViz/ParserCombinators.hs +0/−239
- Data/GraphViz/Types.hs +526/−173
- Data/GraphViz/Types/Clustering.hs +45/−44
- Data/GraphViz/Types/Internal.hs +63/−0
- Data/GraphViz/Types/Parsing.hs +285/−0
- Data/GraphViz/Types/Printing.hs +151/−0
- TODO +2/−9
- graphviz.cabal +7/−4
Changelog view
@@ -1,3 +1,56 @@+Changes since 2999.1.0.2+========================++A major re-write occured; these are the highlights:++* Re-write parsing and printing of Dot code using the new ParseDot and+ PrintDot classes. This should finally fix all quoting issues, as+ well as leaving Show as the code representation for hacking+ purposes. As part of this, the Data.GraphViz.ParserCombinators+ module has been moved to Data.GraphViz.Types.Parsing.++* Re-write the various Dot* datatypes in Data.GraphViz.Types.+ Sub-graphs/clusters are now their own entity rather than being part+ of DotNode and the Node ID type is now a type parameter rather than+ being just Int. Sub-graphs/clusters can now also be parsed.++* The various conversion functions in Data.GraphViz now come in two+ flavours: the unprimed versions take in a Bool indicating if the+ graph is directed or not; the primed versions attempt to+ automatically detect this. Also add cluster support for the graph+ -> dot -> graph conversion-style functions, as requested by Nikolas+ Mayr.++* Allow custom arrow types as supported by GraphViz; as requested by+ Han Joosten.++* Fixed a bug in HSV-style Color values where Int was used instead of+ Double; spotted by Michael deLorimier.++* Properly resolved the situation initially spotted by Neil Brown:+ Matthew Sackman was following Dot terminology for an edge `a -> b'+ when using "head" for `b' and "tail" for `a' (this is presumably+ because the head/tail of the arrow are at those nodes). DotEdge now+ uses "from" and "to" avoid ambiguity; the various Attribute values+ still follow upstream usage.++Changes since 2999.1.0.1+========================++* Fix a bug spotted by Srihari Ramanathan where Color values were+ double-quoted.++Changes since 2999.1.0.0+========================++* The Color Attribute should take [Color], not just a single Color.++Changes since 2999.0.0.0+========================++* Stop using Either for composite Attributes and use a custom type:+ this avoids problems with the Show instance.+ Changes since 2009.5.1 ======================
Data/GraphViz.hs view
@@ -6,22 +6,44 @@ Maintainer : Ivan.Miljenovic@gmail.com This is the top-level module for the graphviz library. It provides- functions to convert 'Data.Graph.Inductive.Graph.Graph's into- the /Dot/ language used by the /GraphViz/ program (as well as a- limited ability to perform the reverse operation).+ functions to convert 'Data.Graph.Inductive.Graph.Graph's into the+ /Dot/ language used by the /GraphViz/ suite of programs (as well as+ a limited ability to perform the reverse operation). Information about GraphViz and the Dot language can be found at: <http://graphviz.org/>- -} + Commands for converting graphs to Dot format have two options: one+ in which the user specifies whether the graph is directed or+ undirected, and a primed version which attempts to automatically+ infer if the graph is directed or not. Note that these conversion+ functions assume that undirected graphs have every edge being+ duplicated (or at least that if there exists an edge from /n1/ to+ /n2/, then /n1 <= n2/).+ -} module Data.GraphViz- ( graphToDot- , clusterGraphToDot- , graphToGraph- , dotizeGraph+ ( -- * Conversion from graphs to /Dot/ format.+ graphToDot+ , graphToDot'+ -- ** Conversion with support for clusters. , NodeCluster(..)+ , clusterGraphToDot+ , clusterGraphToDot'+ -- * Passing the graph through GraphViz.+ -- ** Type aliases for @Node@ and @Edge@ labels. , AttributeNode , AttributeEdge+ -- ** For normal graphs.+ , graphToGraph+ , graphToGraph'+ , dotizeGraph+ , dotizeGraph'+ -- ** For clustered graphs.+ , clusterGraphToGraph+ , clusterGraphToGraph'+ , dotizeClusterGraph+ , dotizeClusterGraph'+ -- * Re-exporting other modules. , module Data.GraphViz.Types , module Data.GraphViz.Attributes , module Data.GraphViz.Commands@@ -31,110 +53,239 @@ import Data.GraphViz.Types.Clustering import Data.GraphViz.Attributes import Data.GraphViz.Commands-import Data.GraphViz.ParserCombinators(runParser) import Data.Graph.Inductive.Graph import qualified Data.Set as Set import Control.Arrow((&&&))-import Data.Maybe+import Data.Maybe(mapMaybe, fromJust) import qualified Data.Map as Map import System.IO(hGetContents) import System.IO.Unsafe(unsafePerformIO) -- ----------------------------------------------------------------------------- --- | Determine if the given graph is undirected or directed.-isUndir :: (Ord b, Graph g) => g a b -> Bool-isUndir g = all hasFlip es+-- | Determine if the given graph is undirected.+isUndirected :: (Ord b, Graph g) => g a b -> Bool+isUndirected g = all hasFlip es where es = labEdges g eSet = Set.fromList es hasFlip e = Set.member (flippedEdge e) eSet flippedEdge (f,t,l) = (t,f,l) +-- | Determine if the given graph is directed.+isDirected :: (Ord b, Graph g) => g a b -> Bool+isDirected = not . isUndirected+ -- ----------------------------------------------------------------------------- --- | Convert a graph to GraphViz's /Dot/ format.-graphToDot :: (Ord b, Graph gr) => gr a b -> [Attribute]- -> (LNode a -> [Attribute]) -> (LEdge b -> [Attribute]) -> DotGraph-graphToDot graph gAttributes fmtNode fmtEdge- = clusterGraphToDot graph gAttributes clusterBy fmtCluster fmtNode fmtEdge+-- | Convert a graph to GraphViz's /Dot/ format. The 'Bool' value is+-- 'True' for directed graphs, 'False' otherwise.+graphToDot :: (Graph gr) => Bool -> gr a b -> [GlobalAttributes]+ -> (LNode a -> Attributes) -> (LEdge b -> Attributes)+ -> DotGraph Node+graphToDot isDir graph gAttributes+ = clusterGraphToDot isDir graph gAttributes clustBy cID fmtClust where- clusterBy :: LNode a -> NodeCluster () a- clusterBy = N- fmtCluster _ = []+ clustBy :: LNode a -> NodeCluster () a+ clustBy = N+ cID = const Nothing+ fmtClust = const [] +-- | Convert a graph to GraphViz's /Dot/ format with automatic+-- direction detection.+graphToDot' :: (Ord b, Graph gr) => gr a b -> [GlobalAttributes]+ -> (LNode a -> Attributes) -> (LEdge b -> Attributes)+ -> DotGraph Node+graphToDot' graph = graphToDot (isDirected graph) graph+ -- | Convert a graph to /Dot/ format, using the specified clustering function -- to group nodes into clusters. -- Clusters can be nested to arbitrary depth.-clusterGraphToDot :: (Ord c, Ord b, Graph gr) => gr a b- -> [Attribute] -> (LNode a -> NodeCluster c a)- -> (c -> [Attribute]) -> (LNode a -> [Attribute])- -> (LEdge b -> [Attribute]) -> DotGraph-clusterGraphToDot graph gAttrs clusterBy fmtCluster fmtNode fmtEdge+-- The 'Bool' argument is 'True' for directed graphs, 'False' otherwise.+clusterGraphToDot :: (Ord c, Graph gr) => Bool -> gr a b+ -> [GlobalAttributes] -> (LNode a -> NodeCluster c a)+ -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes])+ -> (LNode a -> Attributes) -> (LEdge b -> Attributes)+ -> DotGraph Node+clusterGraphToDot dirGraph graph gAttrs clusterBy cID fmtCluster fmtNode fmtEdge = DotGraph { strictGraph = False , directedGraph = dirGraph , graphID = Nothing- , graphAttributes = gAttrs- , graphNodes = ns- , graphEdges = es+ , graphStatements = stmts } where- dirGraph = not $ isUndir graph- ns = clustersToNodes clusterBy fmtCluster fmtNode graph+ stmts = DotStmts { attrStmts = gAttrs+ , subGraphs = cs+ , nodeStmts = ns+ , edgeStmts = es+ }+ (cs, ns) = clustersToNodes clusterBy cID fmtCluster fmtNode graph es = mapMaybe mkDotEdge . labEdges $ graph mkDotEdge e@(f,t,_) = if dirGraph || f <= t- then Just DotEdge {edgeHeadNodeID = f- ,edgeTailNodeID = t- ,edgeAttributes = fmtEdge e- ,directedEdge = dirGraph}+ then Just DotEdge { edgeFromNodeID = f+ , edgeToNodeID = t+ , edgeAttributes = fmtEdge e+ , directedEdge = dirGraph+ } else Nothing +-- | Convert a graph to /Dot/ format, using the specified clustering function+-- to group nodes into clusters.+-- Clusters can be nested to arbitrary depth.+-- Graph direction is automatically inferred.+clusterGraphToDot' :: (Ord c, Ord b, Graph gr) => gr a b+ -> [GlobalAttributes] -> (LNode a -> NodeCluster c a)+ -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes])+ -> (LNode a -> Attributes) -> (LEdge b -> Attributes)+ -> DotGraph Node+clusterGraphToDot' gr = clusterGraphToDot (isDirected gr) gr+ -- ----------------------------------------------------------------------------- -type AttributeNode a = ([Attribute], a)-type AttributeEdge b = ([Attribute], b)+type AttributeNode a = (Attributes, a)+type AttributeEdge b = (Attributes, b) --- | Run the graph via dot to get positional information and then--- combine that information back into the original graph.--- Note that this doesn't support graphs with clusters.-graphToGraph :: (Ord b, Graph gr) => gr a b -> [Attribute]- -> (LNode a -> [Attribute]) -> (LEdge b -> [Attribute])+-- | Run the appropriate GraphViz command on the graph to get+-- positional information and then combine that information back+-- into the original graph. Note that for the edge information to+-- be parsed properly when using multiple edges, each edge between+-- two nodes needs to have a unique label.+--+-- The 'Bool' argument is 'True' for directed graphs, 'False'+-- otherwise. Directed graphs are passed through /dot/, and+-- undirected graphs through /neato/.+graphToGraph :: (Graph gr) => Bool -> gr a b -> [GlobalAttributes]+ -> (LNode a -> Attributes) -> (LEdge b -> Attributes) -> IO (gr (AttributeNode a) (AttributeEdge b))-graphToGraph gr gAttributes fmtNode fmtEdge+graphToGraph isDir gr gAttributes fmtNode fmtEdge+ = dotAttributes isDir gr dot+ where+ dot = graphToDot isDir gr gAttributes fmtNode fmtEdge++dotAttributes :: (Graph gr) => Bool -> gr a b -> DotGraph Node+ -> IO (gr (AttributeNode a) (AttributeEdge b))+dotAttributes isDir gr dot = do output <- graphvizWithHandle command dot DotOutput hGetContents let res = fromJust output length res `seq` return () return $ rebuildGraphWithAttributes res where- undirected = isUndir gr- command = if undirected then undirCommand else dirCommand- dot = graphToDot gr gAttributes fmtNode fmtEdge+ command = if isDir then dirCommand else undirCommand rebuildGraphWithAttributes dotResult = mkGraph lnodes ledges where lnodes = map (\(n, l) -> (n, (fromJust $ Map.lookup n nodeMap, l))) $ labNodes gr ledges = map createEdges $ labEdges gr- createEdges (f, t, l) = if undirected && f > t- then (f, t, getLabel (t,f))- else (f, t, getLabel (f,t))+ createEdges (f, t, l) = if isDir || f <= t+ then (f, t, getLabel (f,t))+ else (f, t, getLabel (t,f)) where getLabel c = (fromJust $ Map.lookup c edgeMap, l)- DotGraph { graphNodes = ns, graphEdges = es}- = fst . runParser parseDotGraph $ dotResult+ g' = parseDotGraph dotResult+ ns = graphNodes g'+ es = graphEdges g' nodeMap = Map.fromList $ map (nodeID &&& nodeAttributes) ns- edgeMap = Map.fromList $ map (\e -> ( ( edgeTailNodeID e- , edgeHeadNodeID e)- , edgeAttributes e)- ) es+ edgeMap = Map.fromList $ map ( (edgeFromNodeID &&& edgeToNodeID)+ &&& edgeAttributes) es --- | Pass the plain graph through 'graphToGraph'. This is an @IO@ action,--- however since the state doesn't change it's safe to use 'unsafePerformIO'--- to convert this to a normal function.-dotizeGraph :: (DynGraph gr, Ord b) => gr a b- -> gr (AttributeNode a) (AttributeEdge b)-dotizeGraph g = unsafePerformIO- $ graphToGraph g gAttrs noAttrs noAttrs+-- | Run the appropriate GraphViz command on the graph to get+-- positional information and then combine that information back+-- into the original graph.+--+-- Graph direction is automatically inferred.+graphToGraph' :: (Ord b, Graph gr) => gr a b -> [GlobalAttributes]+ -> (LNode a -> Attributes) -> (LEdge b -> Attributes)+ -> IO (gr (AttributeNode a) (AttributeEdge b))+graphToGraph' gr = graphToGraph (isDirected gr) gr++-- | Run the appropriate GraphViz command on the clustered graph to+-- get positional information and then combine that information back+-- into the original graph. Note that for the edge information to+-- be parsed properly when using multiple edges, each edge between+-- two nodes needs to have a unique label.+--+-- The 'Bool' argument is 'True' for directed graphs, 'False'+-- otherwise. Directed graphs are passed through /dot/, and+-- undirected graphs through /neato/.+clusterGraphToGraph :: (Ord c, Graph gr) => Bool -> gr a b+ -> [GlobalAttributes] -> (LNode a -> NodeCluster c a)+ -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes])+ -> (LNode a -> Attributes) -> (LEdge b -> Attributes)+ -> IO (gr (AttributeNode a) (AttributeEdge b))+clusterGraphToGraph isDir gr gAtts clBy cID fmtClust fmtNode fmtEdge+ = dotAttributes isDir gr dot where+ dot = clusterGraphToDot isDir gr gAtts clBy cID fmtClust fmtNode fmtEdge++-- | Run the appropriate GraphViz command on the clustered graph to+-- get positional information and then combine that information back+-- into the original graph.+--+-- Graph direction is automatically inferred.+clusterGraphToGraph' :: (Ord b, Ord c, Graph gr) => gr a b+ -> [GlobalAttributes] -> (LNode a -> NodeCluster c a)+ -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes])+ -> (LNode a -> Attributes) -> (LEdge b -> Attributes)+ -> IO (gr (AttributeNode a) (AttributeEdge b))+clusterGraphToGraph' gr = clusterGraphToGraph (isDirected gr) gr+++-- | Pass the graph through 'graphToGraph' with no 'Attribute's. This+-- is an @'IO'@ action, however since the state doesn't change it's+-- safe to use 'unsafePerformIO' to convert this to a normal+-- function.+--+-- The 'Bool' argument is 'True' for directed graphs, 'False'+-- otherwise. Directed graphs are passed through /dot/, and+-- undirected graphs through /neato/.+dotizeGraph :: (Graph gr) => Bool -> gr a b+ -> gr (AttributeNode a) (AttributeEdge b)+dotizeGraph isDir g = unsafePerformIO+ $ graphToGraph isDir g gAttrs noAttrs noAttrs+ where gAttrs = [] noAttrs = const []++-- | Pass the graph through 'graphToGraph' with no 'Attribute's. This+-- is an @'IO'@ action, however since the state doesn't change it's+-- safe to use 'unsafePerformIO' to convert this to a normal+-- function.+--+-- The graph direction is automatically inferred.+dotizeGraph' :: (Graph gr, Ord b) => gr a b+ -> gr (AttributeNode a) (AttributeEdge b)+dotizeGraph' g = dotizeGraph (isDirected g) g++-- | Pass the clustered graph through 'clusterGraphToGraph' with no+-- 'Attribute's. This is an @'IO'@ action, however since the state+-- doesn't change it's safe to use 'unsafePerformIO' to convert this+-- to a normal function.+--+-- The 'Bool' argument is 'True' for directed graphs, 'False'+-- otherwise. Directed graphs are passed through /dot/, and+-- undirected graphs through /neato/.+dotizeClusterGraph :: (Ord c, Graph gr) => Bool -> gr a b+ -> (LNode a -> NodeCluster c a)+ -> gr (AttributeNode a) (AttributeEdge b)+dotizeClusterGraph isDir g clustBy = unsafePerformIO+ $ clusterGraphToGraph isDir g+ gAttrs clustBy+ cID cAttrs+ noAttrs noAttrs+ where+ gAttrs = []+ cID = const Nothing+ cAttrs = const gAttrs+ noAttrs = const []++-- | Pass the clustered graph through 'graphToGraph' with no+-- 'Attribute's. This is an @'IO'@ action, however since the state+-- doesn't change it's safe to use 'unsafePerformIO' to convert this+-- to a normal function.+--+-- The graph direction is automatically inferred.+dotizeClusterGraph' :: (Ord b, Ord c, Graph gr) => gr a b+ -> (LNode a -> NodeCluster c a)+ -> gr (AttributeNode a) (AttributeEdge b)+dotizeClusterGraph' g = dotizeClusterGraph (isDirected g) g
Data/GraphViz/Attributes.hs view
@@ -8,1852 +8,2198 @@ This module defines the various attributes that different parts of a GraphViz graph can have. These attributes are based on the documentation found at:-- <http://graphviz.org/doc/info/attrs.html>-- For more information on usage, etc. please see that document.-- A summary of known current constraints\/limitations\/differences:-- * Parsing of quoted strings might not always work if they are a- sub-part of another Attribute (e.g. a quoted name in 'LayerList').- In fact, parsing with quotes is iffy for everything; specifically- when they are and aren't allowed.-- * 'ColorScheme' is ignored when parsing 'Color' values-- * ColorList and PointfList are defined as actual lists (but- 'LayerList' is not).-- * A lot of values have a possible value of @"none"@. These now- have custom constructors. In fact, most constructors have been- expanded upon to give an idea of what they represent rather than- using generic terms.-- * @PointF@ and 'Point' have been combined, and feature support for pure- 'Int'-based co-ordinates as well as 'Double' ones (i.e. no floating- point-only points for Point). The optional '!' and third value- for Point are not available.-- * 'Rect' uses two 'Point' values to denote the lower-left and- top-right corners.-- * The two 'LabelLoc' attributes have been combined.-- * The defined 'LayerSep' is not used to parse 'LayerRange' or- 'LayerList'; the default (@[' ', ':', '\t']@) is instead used.-- * @SplineType@ has been replaced with @['Spline']@.-- * Only polygon-based 'Shape's are available.-- * Device-dependent 'StyleName' values are not available.-- * 'PortPos' only has the 'CompassPoint' option, not- @PortName[:CompassPoint]@ (since record shapes aren't allowed,- and parsing HTML-like labels could be problematic).-- * Not every 'Attribute' is fully documented/described. In- particular, a lot of them are listed as having a 'String' value,- when actually only certain Strings are allowed.-- * Deprecated 'Overlap' algorithms are not defined.-- -}--module Data.GraphViz.Attributes where--import Data.GraphViz.ParserCombinators--import Data.Char(isDigit, isHexDigit)-import Data.Word-import Numeric-import Control.Monad-import Data.Maybe---- -------------------------------------------------------------------------------{- |-- These attributes have been implemented in a /permissive/ manner:- that is, rather than split them up based on which type of value- they are allowed, they have all been included in the one data type,- with functions to determine if they are indeed valid for what- they're being applied to.-- To interpret the /Valid for/ listings:-- [@G@] Valid for Graphs.-- [@C@] Valid for Clusters.-- [@S@] Valid for Sub-Graphs (and also Clusters).-- [@N@] Valid for Nodes.-- [@E@] Valid for Edges.-- Note also that the default values are taken from the specification- page listed above, and might not correspond fully with the names of- the permitted values.--}-data Attribute- = Damping Double -- ^ /Valid for/: G; /Default/: 0.99; /Minimum/: 0.0; /Notes/: neato only- | K Double -- ^ /Valid for/: GC; /Default/: 0.3; /Minimum/: 0; /Notes/: sfdp, fdp only- | URL URL -- ^ /Valid for/: ENGC; /Default/: \<none\>; /Notes/: svg, postscript, map only- | ArrowHead ArrowType -- ^ /Valid for/: E; /Default/: Normal- | ArrowSize Double -- ^ /Valid for/: E; /Default/: 1.0; /Minimum/: 0.0- | ArrowTail ArrowType -- ^ /Valid for/: E; /Default/: Normal- | Aspect AspectType -- ^ /Valid for/: G; /Notes/: dot only- | Bb Rect -- ^ /Valid for/: G; /Notes/: write only- | BgColor Color -- ^ /Valid for/: GC; /Default/: \<none\>- | Center Bool -- ^ /Valid for/: G; /Default/: false- | Charset String -- ^ /Valid for/: G; /Default/: \"UTF-8\"- | ClusterRank ClusterMode -- ^ /Valid for/: G; /Default/: local; /Notes/: dot only- | Color [Color] -- ^ /Valid for/: ENC; /Default/: black- | ColorScheme String -- ^ /Valid for/: ENCG; /Default/: \"\"- | Comment String -- ^ /Valid for/: ENG; /Default/: \"\"- | Compound Bool -- ^ /Valid for/: G; /Default/: false; /Notes/: dot only- | Concentrate Bool -- ^ /Valid for/: G; /Default/: false- | Constraint Bool -- ^ /Valid for/: E; /Default/: true; /Notes/: dot only- | Decorate Bool -- ^ /Valid for/: E; /Default/: false- | DefaultDist Double -- ^ /Valid for/: G; /Default/: 1+(avg. len)*sqrt(|V|); /Minimum/: epsilon; /Notes/: neato only- | Dim Int -- ^ /Valid for/: G; /Default/: 2; /Minimum/: 2; /Notes/: sfdp, fdp, neato only- | Dimen Int -- ^ /Valid for/: G; /Default/: 2; /Minimum/: 2; /Notes/: sfdp, fdp, neato only- | Dir DirType -- ^ /Valid for/: E; /Default/: forward(directed)/none(undirected)- | DirEdgeConstraints DEConstraints -- ^ /Valid for/: G; /Default/: false; /Notes/: neato only- | Distortion Double -- ^ /Valid for/: N; /Default/: 0.0; /Minimum/: -100.0- | DPI Double -- ^ /Valid for/: G; /Default/: 96.0 | 0.0; /Notes/: svg, bitmap output only- | EdgeURL URL -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: svg, map only- | EdgeTarget String -- ^ /Valid for/: E; /Default/: \<none\>; /Notes/: svg, map only- | EdgeTooltip String -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: svg, cmap only- | Epsilon Double -- ^ /Valid for/: G; /Default/: .0001 * # nodes(mode == KK) | .0001(mode == major); /Notes/: neato only- | ESep DPoint -- ^ /Valid for/: G; /Default/: +3; /Notes/: not dot- | FillColor Color -- ^ /Valid for/: NC; /Default/: lightgrey(nodes) | black(clusters)- | FixedSize Bool -- ^ /Valid for/: N; /Default/: false- | FontColor Color -- ^ /Valid for/: ENGC; /Default/: black- | FontName String -- ^ /Valid for/: ENGC; /Default/: \"Times-Roman\"- | FontNames String -- ^ /Valid for/: G; /Default/: \"\"; /Notes/: svg only- | FontPath String -- ^ /Valid for/: G; /Default/: system-dependent- | FontSize Double -- ^ /Valid for/: ENGC; /Default/: 14.0; /Minimum/: 1.0- | Group String -- ^ /Valid for/: N; /Default/: \"\"; /Notes/: dot only- | HeadURL URL -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: svg, map only- | HeadClip Bool -- ^ /Valid for/: E; /Default/: true- | HeadLabel Label -- ^ /Valid for/: E; /Default/: \"\"- | HeadPort PortPos -- ^ /Valid for/: E; /Default/: center- | HeadTarget QuotedString -- ^ /Valid for/: E; /Default/: \<none\>; /Notes/: svg, map only- | HeadTooltip QuotedString -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: svg, cmap only- | Height Double -- ^ /Valid for/: N; /Default/: 0.5; /Minimum/: 0.02- | ID Label -- ^ /Valid for/: GNE; /Default/: \"\"; /Notes/: svg, postscript, map only- | Image String -- ^ /Valid for/: N; /Default/: \"\"- | ImageScale ScaleType -- ^ /Valid for/: N; /Default/: false- | Label Label -- ^ /Valid for/: ENGC; /Default/: \"\N\" (nodes) Nothing | \"\" (otherwise) Nothing- | LabelURL URL -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: svg, map only- | LabelAngle Double -- ^ /Valid for/: E; /Default/: -25.0; /Minimum/: -180.0- | LabelDistance Double -- ^ /Valid for/: E; /Default/: 1.0; /Minimum/: 0.0- | LabelFloat Bool -- ^ /Valid for/: E; /Default/: false- | LabelFontColor Color -- ^ /Valid for/: E; /Default/: black- | LabelFontName String -- ^ /Valid for/: E; /Default/: \"Times-Roman\"- | LabelFontSize Double -- ^ /Valid for/: E; /Default/: 14.0; /Minimum/: 1.0- | LabelJust Justification -- ^ /Valid for/: GC; /Default/: \"c\"- | LabelLoc VerticalPlacement -- ^ /Valid for/: GCN; /Default/: \"t\"(clusters) | \"b\"(root graphs) | \"c\"(clusters)- | LabelTarget String -- ^ /Valid for/: E; /Default/: \<none\>; /Notes/: svg, map only- | LabelTooltip String -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: svg, cmap only- | Landscape Bool -- ^ /Valid for/: G; /Default/: false- | Layer LayerRange -- ^ /Valid for/: EN; /Default/: \"\"- | Layers LayerList -- ^ /Valid for/: G; /Default/: \"\"- | LayerSep String -- ^ /Valid for/: G; /Default/: \" :\t\"- | Layout String -- ^ /Valid for/: G; /Default/: \"\"- | Len Double -- ^ /Valid for/: E; /Default/: 1.0(neato)/0.3(fdp); /Notes/: fdp, neato only- | Levels Int -- ^ /Valid for/: G; /Default/: MAXINT; /Minimum/: 0.0; /Notes/: sfdp only- | LevelsGap Double -- ^ /Valid for/: G; /Default/: 0.0; /Notes/: neato only- | LHead String -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: dot only- | LP Point -- ^ /Valid for/: EGC; /Notes/: write only- | LTail String -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: dot only- | Margin DPoint -- ^ /Valid for/: NG; /Default/: \<device-dependent\>- | MaxIter Int -- ^ /Valid for/: G; /Default/: 100 * # nodes(mode == KK) | 200(mode == major) | 600(fdp); /Notes/: fdp, neato only- | MCLimit Double -- ^ /Valid for/: G; /Default/: 1.0; /Notes/: dot only- | MinDist Double -- ^ /Valid for/: G; /Default/: 1.0; /Minimum/: 0.0; /Notes/: circo only- | MinLen Int -- ^ /Valid for/: E; /Default/: 1; /Minimum/: 0; /Notes/: dot only- | Mode String -- ^ /Valid for/: G; /Default/: \"major\"; /Notes/: neato only- | Model String -- ^ /Valid for/: G; /Default/: \"shortpath\"; /Notes/: neato only- | Mosek Bool -- ^ /Valid for/: G; /Default/: false; /Notes/: neato only; requires the Mosek software- | NodeSep Double -- ^ /Valid for/: G; /Default/: 0.25; /Minimum/: 0.02; /Notes/: dot only- | NoJustify Bool -- ^ /Valid for/: GCNE; /Default/: false- | Normalize Bool -- ^ /Valid for/: G; /Default/: false; /Notes/: not dot- | Nslimit Double -- ^ /Valid for/: G; /Notes/: dot only- | Nslimit1 Double -- ^ /Valid for/: G; /Notes/: dot only- | Ordering String -- ^ /Valid for/: G; /Default/: \"\"; /Notes/: dot only- | Orientation Double -- ^ /Valid for/: N; /Default/: 0.0; /Minimum/: 360.0- | OrientationGraph String -- ^ /Valid for/: G; /Default/: \"\"; /Notes/: Landscape if \"[lL]*\" and rotate not defined- | OutputOrder OutputMode -- ^ /Valid for/: G; /Default/: breadthfirst- | Overlap Overlap -- ^ /Valid for/: G; /Default/: true; /Notes/: not dot- | OverlapScaling Double -- ^ /Valid for/: G; /Default/: -4; /Minimum/: -1.0e10; /Notes/: prism only- | Pack Pack -- ^ /Valid for/: G; /Default/: false; /Notes/: not dot- | PackMode PackMode -- ^ /Valid for/: G; /Default/: node; /Notes/: not dot- | Pad DPoint -- ^ /Valid for/: G; /Default/: 0.0555 (4 points)- | Page Point -- ^ /Valid for/: G- | PageDir PageDir -- ^ /Valid for/: G; /Default/: BL- | PenColor Color -- ^ /Valid for/: C; /Default/: black- | PenWidth Double -- ^ /Valid for/: CNE; /Default/: 1.0; /Minimum/: 0.0- | Peripheries Int -- ^ /Valid for/: NC; /Default/: shape default(nodes) | 1(clusters); /Minimum/: 0- | Pin Bool -- ^ /Valid for/: N; /Default/: false; /Notes/: fdp, neato only- | Pos Pos -- ^ /Valid for/: EN- | QuadTree QuadType -- ^ /Valid for/: G; /Default/: \"normal\"; /Notes/: sfdp only- | Quantum Double -- ^ /Valid for/: G; /Default/: 0.0; /Minimum/: 0.0- | Rank RankType -- ^ /Valid for/: S; /Notes/: dot only- | RankDir RankDir -- ^ /Valid for/: G; /Default/: TB; /Notes/: dot only- | Ranksep Double -- ^ /Valid for/: G; /Default/: 0.5(dot) | 1.0(twopi); /Minimum/: 0.02; /Notes/: twopi, dot only- | Ratio Ratios -- ^ /Valid for/: G- | Rects Rect -- ^ /Valid for/: N; /Notes/: write only- | Regular Bool -- ^ /Valid for/: N; /Default/: false- | ReMinCross Bool -- ^ /Valid for/: G; /Default/: false; /Notes/: dot only- | RepulsiveForce Double -- ^ /Valid for/: G; /Default/: 1.0; /Minimum/: 0.0; /Notes/: sfdp only- | Resolution Double -- ^ /Valid for/: G; /Default/: 96.0 | 0.0; /Notes/: svg, bitmap output only- | Root Root -- ^ /Valid for/: GN; /Default/: \"\"(graphs) | false(nodes); /Notes/: circo, twopi only- | Rotate Int -- ^ /Valid for/: G; /Default/: 0- | SameHead String -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: dot only- | SameTail String -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: dot only- | SamplePoints Int -- ^ /Valid for/: N; /Default/: 8(output) | 20(overlap and image maps)- | SearchSize Int -- ^ /Valid for/: G; /Default/: 30; /Notes/: dot only- | Sep DPoint -- ^ /Valid for/: G; /Default/: +4; /Notes/: not dot- | Shape Shape -- ^ /Valid for/: N; /Default/: ellipse- | ShapeFile String -- ^ /Valid for/: N; /Default/: \"\"- | ShowBoxes Int -- ^ /Valid for/: ENG; /Default/: 0; /Minimum/: 0; /Notes/: dot only- | Sides Int -- ^ /Valid for/: N; /Default/: 4; /Minimum/: 0- | Size Point -- ^ /Valid for/: G- | Skew Double -- ^ /Valid for/: N; /Default/: 0.0; /Minimum/: -100.0- | Smoothing SmoothType -- ^ /Valid for/: G; /Default/: \"none\"; /Notes/: sfdp only- | SortV Int -- ^ /Valid for/: GCN; /Default/: 0; /Minimum/: 0- | Splines EdgeType -- ^ /Valid for/: G- | Start StartType -- ^ /Valid for/: G; /Default/: \"\"; /Notes/: fdp, neato only- | Style Style -- ^ /Valid for/: ENC- | StyleSheet String -- ^ /Valid for/: G; /Default/: \"\"; /Notes/: svg only- | TailURL URL -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: svg, map only- | TailClip Bool -- ^ /Valid for/: E; /Default/: true- | TailLabel Label -- ^ /Valid for/: E; /Default/: \"\"- | TailPort PortPos -- ^ /Valid for/: E; /Default/: center- | TailTarget String -- ^ /Valid for/: E; /Default/: \<none\>; /Notes/: svg, map only- | TailTooltip String -- ^ /Valid for/: E; /Default/: \"\"; /Notes/: svg, cmap only- | Target String -- ^ /Valid for/: ENGC; /Default/: \<none\>; /Notes/: svg, map only- | Tooltip String -- ^ /Valid for/: NEC; /Default/: \"\"; /Notes/: svg, cmap only- | TrueColor Bool -- ^ /Valid for/: G; /Notes/: bitmap output only- | Vertices [Point] -- ^ /Valid for/: N; /Notes/: write only- | ViewPort ViewPort -- ^ /Valid for/: G; /Default/: \"\"- | VoroMargin Double -- ^ /Valid for/: G; /Default/: 0.05; /Minimum/: 0.0; /Notes/: not dot- | Weight Double -- ^ /Valid for/: E; /Default/: 1.0; /Minimum/: 0(dot) | 1(neato,fdp,sfdp)- | Width Double -- ^ /Valid for/: N; /Default/: 0.75; /Minimum/: 0.01- | Z Double -- ^ /Valid for/: N; /Default/: 0.0; /Minimum/: -MAXFLOAT | -1000- deriving (Eq, Read)--instance Show Attribute where- show (Damping v) = "Damping=" ++ show v- show (K v) = "K=" ++ show v- show (URL v) = "URL=" ++ show v- show (ArrowHead v) = "arrowhead=" ++ show v- show (ArrowSize v) = "arrowsize=" ++ show v- show (ArrowTail v) = "arrowtail=" ++ show v- show (Aspect v) = "aspect=" ++ show v- show (Bb v) = "bb=" ++ show v- show (BgColor v) = "bgcolor=" ++ show v- show (Center v) = "center=" ++ show v- show (Charset v) = "charset=" ++ v- show (ClusterRank v) = "clusterrank=" ++ show v- show (Color v) = "color=" ++ show v- show (ColorScheme v) = "colorscheme=" ++ v- show (Comment v) = "comment=" ++ v- show (Compound v) = "compound=" ++ show v- show (Concentrate v) = "concentrate=" ++ show v- show (Constraint v) = "constraint=" ++ show v- show (Decorate v) = "decorate=" ++ show v- show (DefaultDist v) = "defaultdist=" ++ show v- show (Dim v) = "dim=" ++ show v- show (Dimen v) = "dimen=" ++ show v- show (Dir v) = "dir=" ++ show v- show (DirEdgeConstraints v) = "diredgeconstraints=" ++ show v- show (Distortion v) = "distortion=" ++ show v- show (DPI v) = "dpi=" ++ show v- show (EdgeURL v) = "edgeURL=" ++ show v- show (EdgeTarget v) = "edgetarget=" ++ v- show (EdgeTooltip v) = "edgetooltip=" ++ v- show (Epsilon v) = "epsilon=" ++ show v- show (ESep v) = "esep=" ++ show v- show (FillColor v) = "fillcolor=" ++ show v- show (FixedSize v) = "fixedsize=" ++ show v- show (FontColor v) = "fontcolor=" ++ show v- show (FontName v) = "fontname=" ++ v- show (FontNames v) = "fontnames=" ++ v- show (FontPath v) = "fontpath=" ++ v- show (FontSize v) = "fontsize=" ++ show v- show (Group v) = "group=" ++ v- show (HeadURL v) = "headURL=" ++ show v- show (HeadClip v) = "headclip=" ++ show v- show (HeadLabel v) = "headlabel=" ++ show v- show (HeadPort v) = "headport=" ++ show v- show (HeadTarget v) = "headtarget=" ++ show v- show (HeadTooltip v) = "headtooltip=" ++ show v- show (Height v) = "height=" ++ show v- show (ID v) = "id=" ++ show v- show (Image v) = "image=" ++ v- show (ImageScale v) = "imagescale=" ++ show v- show (Label v) = "label=" ++ show v- show (LabelURL v) = "labelURL=" ++ show v- show (LabelAngle v) = "labelangle=" ++ show v- show (LabelDistance v) = "labeldistance=" ++ show v- show (LabelFloat v) = "labelfloat=" ++ show v- show (LabelFontColor v) = "labelfontcolor=" ++ show v- show (LabelFontName v) = "labelfontname=" ++ v- show (LabelFontSize v) = "labelfontsize=" ++ show v- show (LabelJust v) = "labeljust=" ++ show v- show (LabelLoc v) = "labelloc=" ++ show v- show (LabelTarget v) = "labeltarget=" ++ v- show (LabelTooltip v) = "labeltooltip=" ++ v- show (Landscape v) = "landscape=" ++ show v- show (Layer v) = "layer=" ++ show v- show (Layers v) = "layers=" ++ show v- show (LayerSep v) = "layersep=" ++ v- show (Layout v) = "layout=" ++ v- show (Len v) = "len=" ++ show v- show (Levels v) = "levels=" ++ show v- show (LevelsGap v) = "levelsgap=" ++ show v- show (LHead v) = "lhead=" ++ v- show (LP v) = "lp=" ++ show v- show (LTail v) = "ltail=" ++ v- show (Margin v) = "margin=" ++ show v- show (MaxIter v) = "maxiter=" ++ show v- show (MCLimit v) = "mclimit=" ++ show v- show (MinDist v) = "mindist=" ++ show v- show (MinLen v) = "minlen=" ++ show v- show (Mode v) = "mode=" ++ v- show (Model v) = "model=" ++ v- show (Mosek v) = "mosek=" ++ show v- show (NodeSep v) = "nodesep=" ++ show v- show (NoJustify v) = "nojustify=" ++ show v- show (Normalize v) = "normalize=" ++ show v- show (Nslimit v) = "nslimit=" ++ show v- show (Nslimit1 v) = "nslimit1=" ++ show v- show (Ordering v) = "ordering=" ++ v- show (Orientation v) = "orientation=" ++ show v- show (OrientationGraph v) = "orientation=" ++ v- show (OutputOrder v) = "outputorder=" ++ show v- show (Overlap v) = "overlap=" ++ show v- show (OverlapScaling v) = "overlap_scaling=" ++ show v- show (Pack v) = "pack=" ++ show v- show (PackMode v) = "packmode=" ++ show v- show (Pad v) = "pad=" ++ show v- show (Page v) = "page=" ++ show v- show (PageDir v) = "pagedir=" ++ show v- show (PenColor v) = "pencolor=" ++ show v- show (PenWidth v) = "penwidth=" ++ show v- show (Peripheries v) = "peripheries=" ++ show v- show (Pin v) = "pin=" ++ show v- show (Pos v) = "pos=" ++ show v- show (QuadTree v) = "quadtree=" ++ show v- show (Quantum v) = "quantum=" ++ show v- show (Rank v) = "rank=" ++ show v- show (RankDir v) = "rankdir=" ++ show v- show (Ranksep v) = "ranksep=" ++ show v- show (Ratio v) = "ratio=" ++ show v- show (Rects v) = "rects=" ++ show v- show (Regular v) = "regular=" ++ show v- show (ReMinCross v) = "remincross=" ++ show v- show (RepulsiveForce v) = "repulsiveforce=" ++ show v- show (Resolution v) = "resolution=" ++ show v- show (Root v) = "root=" ++ show v- show (Rotate v) = "rotate=" ++ show v- show (SameHead v) = "samehead=" ++ v- show (SameTail v) = "sametail=" ++ v- show (SamplePoints v) = "samplepoints=" ++ show v- show (SearchSize v) = "searchsize=" ++ show v- show (Sep v) = "sep=" ++ show v- show (Shape v) = "shape=" ++ show v- show (ShapeFile v) = "shapefile=" ++ v- show (ShowBoxes v) = "showboxes=" ++ show v- show (Sides v) = "sides=" ++ show v- show (Size v) = "size=" ++ show v- show (Skew v) = "skew=" ++ show v- show (Smoothing v) = "smoothing=" ++ show v- show (SortV v) = "sortv=" ++ show v- show (Splines v) = "splines=" ++ show v- show (Start v) = "start=" ++ show v- show (Style v) = "style=" ++ show v- show (StyleSheet v) = "stylesheet=" ++ v- show (TailURL v) = "tailURL=" ++ show v- show (TailClip v) = "tailclip=" ++ show v- show (TailLabel v) = "taillabel=" ++ show v- show (TailPort v) = "tailport=" ++ show v- show (TailTarget v) = "tailtarget=" ++ v- show (TailTooltip v) = "tailtooltip=" ++ v- show (Target v) = "target=" ++ v- show (Tooltip v) = "tooltip=" ++ v- show (TrueColor v) = "truecolor=" ++ show v- show (Vertices v) = "vertices=" ++ show v- show (ViewPort v) = "viewport=" ++ show v- show (VoroMargin v) = "voro_margin=" ++ show v- show (Weight v) = "weight=" ++ show v- show (Width v) = "width=" ++ show v- show (Z v) = "z=" ++ show v--instance Parseable Attribute where- parse = oneOf [ liftM Damping $ parseField "Damping"- , liftM K $ parseField "K"- , liftM URL $ oneOf (map parseField ["URL", "href"])- , liftM ArrowHead $ parseField "arrowhead"- , liftM ArrowSize $ parseField "arrowsize"- , liftM ArrowTail $ parseField "arrowtail"- , liftM Aspect $ parseField "aspect"- , liftM Bb $ parseField "bb"- , liftM BgColor $ parseField "bgcolor"- , liftM Center $ parseBoolField "center"- , liftM Charset $ parseField "charset"- , liftM ClusterRank $ parseField "clusterrank"- , liftM Color $ parseField "color"- , liftM ColorScheme $ parseField "colorscheme"- , liftM Comment $ parseField "comment"- , liftM Compound $ parseBoolField "compound"- , liftM Concentrate $ parseBoolField "concentrate"- , liftM Constraint $ parseBoolField "constraint"- , liftM Decorate $ parseBoolField "decorate"- , liftM DefaultDist $ parseField "defaultdist"- , liftM Dim $ parseField "dim"- , liftM Dimen $ parseField "dimen"- , liftM Dir $ parseField "dir"- , liftM DirEdgeConstraints $ parseFieldDef (DEBool True) "diredgeconstraints"- , liftM Distortion $ parseField "distortion"- , liftM DPI $ parseField "dpi"- , liftM EdgeURL $ oneOf (map parseField ["edgeURL", "edgehref"])- , liftM EdgeTarget $ parseField "edgetarget"- , liftM EdgeTooltip $ parseField "edgetooltip"- , liftM Epsilon $ parseField "epsilon"- , liftM ESep $ parseField "esep"- , liftM FillColor $ parseField "fillcolor"- , liftM FixedSize $ parseBoolField "fixedsize"- , liftM FontColor $ parseField "fontcolor"- , liftM FontName $ parseField "fontname"- , liftM FontNames $ parseField "fontnames"- , liftM FontPath $ parseField "fontpath"- , liftM FontSize $ parseField "fontsize"- , liftM Group $ parseField "group"- , liftM HeadURL $ oneOf (map parseField ["headURL", "headhref"])- , liftM HeadClip $ parseBoolField "headclip"- , liftM HeadLabel $ parseField "headlabel"- , liftM HeadPort $ parseField "headport"- , liftM HeadTarget $ parseField "headtarget"- , liftM HeadTooltip $ parseField "headtooltip"- , liftM Height $ parseField "height"- , liftM ID $ parseField "id"- , liftM Image $ parseField "image"- , liftM ImageScale $ parseFieldDef UniformScale "imagescale"- , liftM Label $ parseField "label"- , liftM LabelURL $ oneOf (map parseField ["labelURL", "labelhref"])- , liftM LabelAngle $ parseField "labelangle"- , liftM LabelDistance $ parseField "labeldistance"- , liftM LabelFloat $ parseBoolField "labelfloat"- , liftM LabelFontColor $ parseField "labelfontcolor"- , liftM LabelFontName $ parseField "labelfontname"- , liftM LabelFontSize $ parseField "labelfontsize"- , liftM LabelJust $ parseField "labeljust"- , liftM LabelLoc $ parseField "labelloc"- , liftM LabelTarget $ parseField "labeltarget"- , liftM LabelTooltip $ parseField "labeltooltip"- , liftM Landscape $ parseBoolField "landscape"- , liftM Layer $ parseField "layer"- , liftM Layers $ parseField "layers"- , liftM LayerSep $ parseField "layersep"- , liftM Layout $ parseField "layout"- , liftM Len $ parseField "len"- , liftM Levels $ parseField "levels"- , liftM LevelsGap $ parseField "levelsgap"- , liftM LHead $ parseField "lhead"- , liftM LP $ parseField "lp"- , liftM LTail $ parseField "ltail"- , liftM Margin $ parseField "margin"- , liftM MaxIter $ parseField "maxiter"- , liftM MCLimit $ parseField "mclimit"- , liftM MinDist $ parseField "mindist"- , liftM MinLen $ parseField "minlen"- , liftM Mode $ parseField "mode"- , liftM Model $ parseField "model"- , liftM Mosek $ parseBoolField "mosek"- , liftM NodeSep $ parseField "nodesep"- , liftM NoJustify $ parseBoolField "nojustify"- , liftM Normalize $ parseBoolField "normalize"- , liftM Nslimit $ parseField "nslimit"- , liftM Nslimit1 $ parseField "nslimit1"- , liftM Ordering $ parseField "ordering"- , liftM Orientation $ parseField "orientation"- , liftM OrientationGraph $ parseField "orientation"- , liftM OutputOrder $ parseField "outputorder"- , liftM Overlap $ parseFieldDef KeepOverlaps "overlap"- , liftM OverlapScaling $ parseField "overlap_scaling"- , liftM Pack $ parseFieldDef DoPack "pack"- , liftM PackMode $ parseField "packmode"- , liftM Pad $ parseField "pad"- , liftM Page $ parseField "page"- , liftM PageDir $ parseField "pagedir"- , liftM PenColor $ parseField "pencolor"- , liftM PenWidth $ parseField "penwidth"- , liftM Peripheries $ parseField "peripheries"- , liftM Pin $ parseBoolField "pin"- , liftM Pos $ parseField "pos"- , liftM QuadTree $ parseFieldDef NormalQT "quadtree"- , liftM Quantum $ parseField "quantum"- , liftM Rank $ parseField "rank"- , liftM RankDir $ parseField "rankdir"- , liftM Ranksep $ parseField "ranksep"- , liftM Ratio $ parseField "ratio"- , liftM Rects $ parseField "rects"- , liftM Regular $ parseBoolField "regular"- , liftM ReMinCross $ parseBoolField "remincross"- , liftM RepulsiveForce $ parseField "repulsiveforce"- , liftM Resolution $ parseField "resolution"- , liftM Root $ parseFieldDef IsCentral "root"- , liftM Rotate $ parseField "rotate"- , liftM SameHead $ parseField "samehead"- , liftM SameTail $ parseField "sametail"- , liftM SamplePoints $ parseField "samplepoints"- , liftM SearchSize $ parseField "searchsize"- , liftM Sep $ parseField "sep"- , liftM Shape $ parseField "shape"- , liftM ShapeFile $ parseField "shapefile"- , liftM ShowBoxes $ parseField "showboxes"- , liftM Sides $ parseField "sides"- , liftM Size $ parseField "size"- , liftM Skew $ parseField "skew"- , liftM Smoothing $ parseField "smoothing"- , liftM SortV $ parseField "sortv"- , liftM Splines $ parseFieldDef SplineEdges "splines"- , liftM Start $ parseField "start"- , liftM Style $ parseField "style"- , liftM StyleSheet $ parseField "stylesheet"- , liftM TailURL $ oneOf (map parseField ["tailURL", "tailhref"])- , liftM TailClip $ parseBoolField "tailclip"- , liftM TailLabel $ parseField "taillabel"- , liftM TailPort $ parseField "tailport"- , liftM TailTarget $ parseField "tailtarget"- , liftM TailTooltip $ parseField "tailtooltip"- , liftM Target $ parseField "target"- , liftM Tooltip $ parseField "tooltip"- , liftM TrueColor $ parseBoolField "truecolor"- , liftM Vertices $ parseField "vertices"- , liftM ViewPort $ parseField "viewport"- , liftM VoroMargin $ parseField "voro_margin"- , liftM Weight $ parseField "weight"- , liftM Width $ parseField "width"- , liftM Z $ parseField "z"- ]---- | Determine if this Attribute is valid for use with Graphs.-usedByGraphs :: Attribute -> Bool-usedByGraphs Damping{} = True-usedByGraphs K{} = True-usedByGraphs URL{} = True-usedByGraphs Aspect{} = True-usedByGraphs Bb{} = True-usedByGraphs BgColor{} = True-usedByGraphs Center{} = True-usedByGraphs Charset{} = True-usedByGraphs ClusterRank{} = True-usedByGraphs ColorScheme{} = True-usedByGraphs Comment{} = True-usedByGraphs Compound{} = True-usedByGraphs Concentrate{} = True-usedByGraphs DefaultDist{} = True-usedByGraphs Dim{} = True-usedByGraphs Dimen{} = True-usedByGraphs DirEdgeConstraints{} = True-usedByGraphs DPI{} = True-usedByGraphs Epsilon{} = True-usedByGraphs ESep{} = True-usedByGraphs FontColor{} = True-usedByGraphs FontName{} = True-usedByGraphs FontNames{} = True-usedByGraphs FontPath{} = True-usedByGraphs FontSize{} = True-usedByGraphs ID{} = True-usedByGraphs Label{} = True-usedByGraphs LabelJust{} = True-usedByGraphs LabelLoc{} = True-usedByGraphs Landscape{} = True-usedByGraphs Layers{} = True-usedByGraphs LayerSep{} = True-usedByGraphs Layout{} = True-usedByGraphs Levels{} = True-usedByGraphs LevelsGap{} = True-usedByGraphs LP{} = True-usedByGraphs Margin{} = True-usedByGraphs MaxIter{} = True-usedByGraphs MCLimit{} = True-usedByGraphs MinDist{} = True-usedByGraphs Mode{} = True-usedByGraphs Model{} = True-usedByGraphs Mosek{} = True-usedByGraphs NodeSep{} = True-usedByGraphs NoJustify{} = True-usedByGraphs Normalize{} = True-usedByGraphs Nslimit{} = True-usedByGraphs Nslimit1{} = True-usedByGraphs Ordering{} = True-usedByGraphs OrientationGraph{} = True-usedByGraphs OutputOrder{} = True-usedByGraphs Overlap{} = True-usedByGraphs OverlapScaling{} = True-usedByGraphs Pack{} = True-usedByGraphs PackMode{} = True-usedByGraphs Pad{} = True-usedByGraphs Page{} = True-usedByGraphs PageDir{} = True-usedByGraphs QuadTree{} = True-usedByGraphs Quantum{} = True-usedByGraphs RankDir{} = True-usedByGraphs Ranksep{} = True-usedByGraphs Ratio{} = True-usedByGraphs ReMinCross{} = True-usedByGraphs RepulsiveForce{} = True-usedByGraphs Resolution{} = True-usedByGraphs Root{} = True-usedByGraphs Rotate{} = True-usedByGraphs SearchSize{} = True-usedByGraphs Sep{} = True-usedByGraphs ShowBoxes{} = True-usedByGraphs Size{} = True-usedByGraphs Smoothing{} = True-usedByGraphs SortV{} = True-usedByGraphs Splines{} = True-usedByGraphs Start{} = True-usedByGraphs StyleSheet{} = True-usedByGraphs Target{} = True-usedByGraphs TrueColor{} = True-usedByGraphs ViewPort{} = True-usedByGraphs VoroMargin{} = True-usedByGraphs _ = False---- | Determine if this Attribute is valid for use with Clusters.-usedByClusters :: Attribute -> Bool-usedByClusters K{} = True-usedByClusters URL{} = True-usedByClusters BgColor{} = True-usedByClusters Color{} = True-usedByClusters ColorScheme{} = True-usedByClusters FillColor{} = True-usedByClusters FontColor{} = True-usedByClusters FontName{} = True-usedByClusters FontSize{} = True-usedByClusters Label{} = True-usedByClusters LabelJust{} = True-usedByClusters LabelLoc{} = True-usedByClusters LP{} = True-usedByClusters NoJustify{} = True-usedByClusters PenColor{} = True-usedByClusters PenWidth{} = True-usedByClusters Peripheries{} = True-usedByClusters Rank{} = True-usedByClusters SortV{} = True-usedByClusters Style{} = True-usedByClusters Target{} = True-usedByClusters Tooltip{} = True-usedByClusters _ = False---- | Determine if this Attribute is valid for use with SubGraphs.-usedBySubGraphs :: Attribute -> Bool-usedBySubGraphs Rank{} = True-usedBySubGraphs _ = False---- | Determine if this Attribute is valid for use with Nodes.-usedByNodes :: Attribute -> Bool-usedByNodes URL{} = True-usedByNodes Color{} = True-usedByNodes ColorScheme{} = True-usedByNodes Comment{} = True-usedByNodes Distortion{} = True-usedByNodes FillColor{} = True-usedByNodes FixedSize{} = True-usedByNodes FontColor{} = True-usedByNodes FontName{} = True-usedByNodes FontSize{} = True-usedByNodes Group{} = True-usedByNodes Height{} = True-usedByNodes ID{} = True-usedByNodes Image{} = True-usedByNodes ImageScale{} = True-usedByNodes Label{} = True-usedByNodes LabelLoc{} = True-usedByNodes Layer{} = True-usedByNodes Margin{} = True-usedByNodes NoJustify{} = True-usedByNodes Orientation{} = True-usedByNodes PenWidth{} = True-usedByNodes Peripheries{} = True-usedByNodes Pin{} = True-usedByNodes Pos{} = True-usedByNodes Rects{} = True-usedByNodes Regular{} = True-usedByNodes Root{} = True-usedByNodes SamplePoints{} = True-usedByNodes Shape{} = True-usedByNodes ShapeFile{} = True-usedByNodes ShowBoxes{} = True-usedByNodes Sides{} = True-usedByNodes Skew{} = True-usedByNodes SortV{} = True-usedByNodes Style{} = True-usedByNodes Target{} = True-usedByNodes Tooltip{} = True-usedByNodes Vertices{} = True-usedByNodes Width{} = True-usedByNodes Z{} = True-usedByNodes _ = False---- | Determine if this Attribute is valid for use with Edges.-usedByEdges :: Attribute -> Bool-usedByEdges URL{} = True-usedByEdges ArrowHead{} = True-usedByEdges ArrowSize{} = True-usedByEdges ArrowTail{} = True-usedByEdges Color{} = True-usedByEdges ColorScheme{} = True-usedByEdges Comment{} = True-usedByEdges Constraint{} = True-usedByEdges Decorate{} = True-usedByEdges Dir{} = True-usedByEdges EdgeURL{} = True-usedByEdges EdgeTarget{} = True-usedByEdges EdgeTooltip{} = True-usedByEdges FontColor{} = True-usedByEdges FontName{} = True-usedByEdges FontSize{} = True-usedByEdges HeadURL{} = True-usedByEdges HeadClip{} = True-usedByEdges HeadLabel{} = True-usedByEdges HeadPort{} = True-usedByEdges HeadTarget{} = True-usedByEdges HeadTooltip{} = True-usedByEdges ID{} = True-usedByEdges Label{} = True-usedByEdges LabelURL{} = True-usedByEdges LabelAngle{} = True-usedByEdges LabelDistance{} = True-usedByEdges LabelFloat{} = True-usedByEdges LabelFontColor{} = True-usedByEdges LabelFontName{} = True-usedByEdges LabelFontSize{} = True-usedByEdges LabelTarget{} = True-usedByEdges LabelTooltip{} = True-usedByEdges Layer{} = True-usedByEdges Len{} = True-usedByEdges LHead{} = True-usedByEdges LP{} = True-usedByEdges LTail{} = True-usedByEdges MinLen{} = True-usedByEdges NoJustify{} = True-usedByEdges PenWidth{} = True-usedByEdges Pos{} = True-usedByEdges SameHead{} = True-usedByEdges SameTail{} = True-usedByEdges ShowBoxes{} = True-usedByEdges Style{} = True-usedByEdges TailURL{} = True-usedByEdges TailClip{} = True-usedByEdges TailLabel{} = True-usedByEdges TailPort{} = True-usedByEdges TailTarget{} = True-usedByEdges TailTooltip{} = True-usedByEdges Target{} = True-usedByEdges Tooltip{} = True-usedByEdges Weight{} = True-usedByEdges _ = False---- -------------------------------------------------------------------------------newtype URL = UStr { urlString :: String }- deriving (Eq, Read)--instance Show URL where- show u = '<' : urlString u ++ ">"--instance Parseable URL where- parse = do char open- cnt <- many1 $ satisfy ((/=) close)- char close- return $ UStr cnt- where- open = '<'- close = '>'---- -------------------------------------------------------------------------------data ArrowType = Normal | Inv- | DotArrow | InvDot- | ODot | InvODot- | NoArrow | Tee- | Empty | InvEmpty- | Diamond | ODiamond- | EDiamond | Crow- | Box | OBox- | Open | HalfOpen- | Vee- deriving (Eq, Read)--instance Show ArrowType where- show Normal = "normal"- show Inv = "inv"- show DotArrow = "dot"- show InvDot = "invdot"- show ODot = "odot"- show InvODot = "invodot"- show NoArrow = "none"- show Tee = "tee"- show Empty = "empty"- show InvEmpty = "invempty"- show Diamond = "diamond"- show ODiamond = "odiamond"- show EDiamond = "ediamond"- show Crow = "crow"- show Box = "box"- show OBox = "obox"- show Open = "open"- show HalfOpen = "halfopen"- show Vee = "vee"--instance Parseable ArrowType where- parse = optionalQuoted- $ oneOf [ string "normal" >> return Normal- , string "inv" >> return Inv- , string "dot" >> return DotArrow- , string "invdot" >> return InvDot- , string "odot" >> return ODot- , string "invodot" >> return InvODot- , string "none" >> return NoArrow- , string "tee" >> return Tee- , string "empty" >> return Empty- , string "invempty" >> return InvEmpty- , string "diamond" >> return Diamond- , string "odiamond" >> return ODiamond- , string "ediamond" >> return EDiamond- , string "crow" >> return Crow- , string "box" >> return Box- , string "obox" >> return OBox- , string "open" >> return Open- , string "halfopen" >> return HalfOpen- , string "vee" >> return Vee- ]---- -------------------------------------------------------------------------------data AspectType = RatioOnly Double- | RatioPassCount Double Int- deriving (Eq, Read)--instance Show AspectType where- show (RatioOnly r) = show r- show (RatioPassCount r p) = show $ show r ++ ',' : show p--instance Parseable AspectType where- parse = oneOf [ liftM RatioOnly parse- , quotedParse $ do r <- parse- char ','- whitespace'- p <- parse- return $ RatioPassCount r p- ]---- -------------------------------------------------------------------------------data Rect = Rect Point Point- deriving (Eq, Read)--instance Show Rect where- show (Rect p1 p2) = show $ show p1 ++ ',' : show p2--instance Parseable Rect where- parse = liftM (uncurry Rect) . quotedParse- $ commaSep' parsePoint parsePoint---- -------------------------------------------------------------------------------data Color = RGB { red :: Word8- , green :: Word8- , blue :: Word8- }- | RGBA { red :: Word8- , green :: Word8- , blue :: Word8- , alpha :: Word8- }- | HSV { hue :: Int- , saturation :: Int- , value :: Int- }- | ColorName String- deriving (Eq, Read)--instance Show Color where- show = show . showColor-- showList cs s = show $ go cs- where- go [] = s- go [c] = showColor c ++ s- go (c:cs') = showColor c ++ ':' : go cs'--showColor :: Color -> String-showColor (RGB r g b) = '#' : foldr showWord8Pad "" [r,g,b]-showColor (RGBA r g b a) = '#' : foldr showWord8Pad "" [r,g,b,a]-showColor (HSV h s v) = show h ++ " " ++ show s ++ " " ++ show v-showColor (ColorName name) = name--showWord8Pad :: Word8 -> String -> String-showWord8Pad w s = padding ++ simple ++ s- where- simple = showHex w ""- padding = replicate count '0'- count = 2 - findCols 1 w- findCols :: Int -> Word8 -> Int- findCols c n- | n < 16 = c- | otherwise = findCols (c+1) (n `div` 16)--instance Parseable Color where- parse = quotedParse parseColor-- parseList = quotedParse $ sepBy1 parseColor (char ':')--parseColor :: Parse Color-parseColor = oneOf [ parseHexBased- , parseHSV- , liftM ColorName parse -- Should we check it is a colour?- ]- where- parseHexBased- = do char '#'- cs <- many1 parse2Hex- return $ case cs of- [r,g,b] -> RGB r g b- [r,g,b,a] -> RGBA r g b a- _ -> error $ "Not a valid hex Color specification: "- ++ show cs- parseHSV = do h <- parse- parseSep- s <- parse- parseSep- v <- parse- return $ HSV h s v- parseSep = oneOf [ string ","- , whitespace- ]- parse2Hex = do c1 <- satisfy isHexDigit- c2 <- satisfy isHexDigit- let [(n, [])] = readHex [c1, c2]- return n---- -------------------------------------------------------------------------------data ClusterMode = Local- | Global- | NoCluster- deriving (Eq, Read)--instance Show ClusterMode where- show Local = "local"- show Global = "global"- show NoCluster = "none"--instance Parseable ClusterMode where- parse = optionalQuoted- . oneOf- $ [ string "local" >> return Local- , string "global" >> return Global- , string "none" >> return NoCluster- ]---- -------------------------------------------------------------------------------data DirType = Forward | Back | Both | NoDir- deriving (Eq, Read)--instance Show DirType where- show Forward = "forward"- show Back = "back"- show Both = "both"- show NoDir = "none"--instance Parseable DirType where- parse = optionalQuoted- $ oneOf [ string "forward" >> return Forward- , string "back" >> return Back- , string "both" >> return Both- , string "none" >> return NoDir- ]---- --------------------------------------------------------------------------------- | Only when @mode=ipsep@.-data DEConstraints = DEBool Bool- | Hier- deriving (Eq, Read)--instance Show DEConstraints where- show (DEBool b) = show b- show Hier = "hier"--instance Parseable DEConstraints where- parse = optionalQuoted- $ oneOf [ liftM DEBool parse- , string "hier" >> return Hier- ]---- --------------------------------------------------------------------------------- | Either a 'Double' or a 'Point'.-data DPoint = DVal Double- | PVal Point- deriving (Eq, Read)--instance Show DPoint where- show (DVal d) = show d- show (PVal p) = show p--instance Parseable DPoint where- parse = oneOf [ liftM DVal parse- , liftM PVal parsePoint- ]---- -------------------------------------------------------------------------------data Label = StrLabel String- | URLLabel URL- deriving (Eq, Read)--instance Show Label where- show (StrLabel s) = s- show (URLLabel u) = show u--instance Parseable Label where- parse = oneOf [ liftM StrLabel parse- , liftM URLLabel parse- ]---- -------------------------------------------------------------------------------data Point = Point Int Int- | PointD Double Double- deriving (Eq, Read)--instance Show Point where- show = show . showPoint-- showList ps s = unwords (map showPoint ps) ++ s--showPoint :: Point -> String-showPoint (Point x y) = show x ++ ',' : show y-showPoint (PointD x y) = show x ++ ',' : show y--instance Parseable Point where- parse = quotedParse parsePoint-- parseList = quotedParse $ sepBy1 parsePoint whitespace--parsePoint :: Parse Point-parsePoint = oneOf [ liftM (uncurry Point) commaSep- , liftM (uncurry PointD) commaSep- ]---- -------------------------------------------------------------------------------data Overlap = KeepOverlaps- | RemoveOverlaps- | ScaleOverlaps- | ScaleXYOverlaps- | PrismOverlap (Maybe Int) -- ^ Only when sfdp is available, @Int@ is non-negative- | CompressOverlap- | VpscOverlap- | IpsepOverlap -- ^ Only when @mode="ipsep"@- deriving (Eq, Read)--instance Show Overlap where- show KeepOverlaps = "true"- show RemoveOverlaps = "false"- show ScaleOverlaps = "scale"- show ScaleXYOverlaps = "scalexy"- show (PrismOverlap i) = maybe id (flip (++) . show) i $ "prism"- show CompressOverlap = "compress"- show VpscOverlap = "vpsc"- show IpsepOverlap = "ipsep"--instance Parseable Overlap where- parse = optionalQuoted- $ oneOf [ string "true" >> return KeepOverlaps- , string "false" >> return RemoveOverlaps- , string "scale" >> return ScaleOverlaps- , string "scalexy" >> return ScaleXYOverlaps- , string "prism" >> liftM PrismOverlap (optional parse)- , string "compress" >> return CompressOverlap- , string "vpsc" >> return VpscOverlap- , string "ipsep" >> return IpsepOverlap- ]---- -------------------------------------------------------------------------------data LayerRange = LRID LayerID- | LRS LayerID String LayerID- deriving (Eq, Read)--instance Show LayerRange where- show (LRID lid) = show lid- show (LRS id1 sep id2) = show $ show id1 ++ sep ++ show id2--instance Parseable LayerRange where- parse = oneOf [ liftM LRID parse- , do id1 <- parse- sep <- parseLayerSep- id2 <- parse- return $ LRS id1 sep id2- ]--parseLayerSep :: Parse String-parseLayerSep = many1 . oneOf- $ map char defLayerSep--defLayerSep :: [Char]-defLayerSep = [' ', ':', '\t']--parseLayerName :: Parse String-parseLayerName = many1 $ satisfy (flip notElem defLayerSep)--data LayerID = AllLayers- | LRInt Int- | LRName String- deriving (Eq, Read)--instance Show LayerID where- show AllLayers = "all"- show (LRInt n) = show n- show (LRName nm) = nm--instance Parseable LayerID where- parse = oneOf [ optionalQuotedString "all" >> return AllLayers- , liftM LRInt $ optionalQuoted parse- , liftM LRName parseLayerName- ]---- | The list represent (Separator, Name)-data LayerList = LL String [(String, String)]- deriving (Eq, Read)--instance Show LayerList where- show (LL l1 ols) = l1 ++ concatMap (uncurry (++)) ols--instance Parseable LayerList where- parse = do l1 <- parseLayerName- ols <- many $ do sep <- parseLayerSep- lnm <- parseLayerName- return (sep, lnm)- return $ LL l1 ols---- -------------------------------------------------------------------------------data OutputMode = BreadthFirst | NodesFirst | EdgesFirst- deriving (Eq, Read)--instance Show OutputMode where- show BreadthFirst = "breadthfirst"- show NodesFirst = "nodesfirst"- show EdgesFirst = "edgesfirst"--instance Parseable OutputMode where- parse = optionalQuoted- $ oneOf [ string "breadthfirst" >> return BreadthFirst- , string "nodesfirst" >> return NodesFirst- , string "edgesfirst" >> return EdgesFirst- ]---- -------------------------------------------------------------------------------data Pack = DoPack- | DontPack- | PackMargin Int -- ^ If non-negative, then packs; otherwise doesn't.- deriving (Eq, Read)--instance Show Pack where- show DoPack = "true"- show DontPack = "false"- show (PackMargin m) = show m--instance Parseable Pack where- parse = optionalQuoted- $ oneOf [ liftM (bool DoPack DontPack) parse- , liftM PackMargin parse- ]---- -------------------------------------------------------------------------------data PackMode = PackNode- | PackClust- | PackGraph- | PackArray Bool Bool (Maybe Int) -- ^ Sort by cols, sort- -- by user, number of- -- rows/cols- deriving (Eq, Read)--instance Show PackMode where- show PackNode = "node"- show PackClust = "clust"- show PackGraph = "graph"- show (PackArray c u mi) = addNum . isU . isC . isUnder- $ "array"- where- addNum = maybe id (flip (++) . show) mi- isUnder = if c || u- then flip (++) "_"- else id- isC = if c- then flip (++) "c"- else id- isU = if u- then flip (++) "u"- else id--instance Parseable PackMode where- parse = optionalQuoted- $ oneOf [ string "node" >> return PackNode- , string "clust" >> return PackClust- , string "graph" >> return PackGraph- , do string "array"- mcu <- optional $ do char '_'- many1 $ satisfy (not . isDigit)- let c = hasChar mcu 'c'- u = hasChar mcu 'u'- mi <- optional parse- return $ PackArray c u mi- ]- where- hasChar ms c = maybe False (elem c) ms---- -------------------------------------------------------------------------------data Pos = PointPos Point- | SplinePos [Spline]- deriving (Eq, Read)--instance Show Pos where- show (PointPos p) = show p- show (SplinePos ss) = show ss--instance Parseable Pos where- -- [Spline] must be quoted, so use the quoted parser for Point as- -- well.- parse = oneOf [ liftM PointPos parse- , liftM SplinePos parse- ]---- --------------------------------------------------------------------------------- | Controls how (and if) edges are represented.-data EdgeType = SplineEdges- | LineEdges- | NoEdges- | PolyLine- | CompoundEdge -- ^ fdp only- deriving (Eq, Read)--instance Show EdgeType where- show SplineEdges = "true"- show LineEdges = "false"- show NoEdges = "\"\""- show PolyLine = "polyline"- show CompoundEdge = "compound"--instance Parseable EdgeType where- parse = optionalQuoted- $ oneOf [ liftM (bool SplineEdges LineEdges) parse- , string "spline" >> return SplineEdges- , string "line" >> return LineEdges- , string "\"\"" >> return NoEdges- , string "polyline" >> return PolyLine- , string "compound" >> return CompoundEdge- ]---- --------------------------------------------------------------------------------- | Upper-case first character is major order;--- lower-case second character is minor order.-data PageDir = Bl | Br | Tl | Tr | Rb | Rt | Lb | Lt- deriving (Eq, Read)--instance Show PageDir where- show Bl = "BL"- show Br = "BR"- show Tl = "TL"- show Tr = "TR"- show Rb = "RB"- show Rt = "RT"- show Lb = "LB"- show Lt = "LT"--instance Parseable PageDir where- parse = optionalQuoted- $ oneOf [ string "BL" >> return Bl- , string "BR" >> return Br- , string "TL" >> return Tl- , string "TR" >> return Tr- , string "RB" >> return Rb- , string "RT" >> return Rt- , string "LB" >> return Lb- , string "LT" >> return Lt- ]---- --------------------------------------------------------------------------------- | The number of points in the list must be equivalent to 1 mod 3;--- note that this is not checked.-data Spline = Spline (Maybe Point) (Maybe Point) [Point]- deriving (Eq, Read)--instance Show Spline where- show = show . showSpline-- showList ss o = show $ go ss- where- go [] = o- go [s] = showSpline s ++ o- go (s:ss') = showSpline s ++ ';' : go ss'--showSpline :: Spline -> String-showSpline (Spline ms me ps) = addS . addE- . unwords- $ map showPoint ps- where- addP t = maybe id (\p -> (++) $ t : ',' : show p)- addS = addP 's' ms- addE = addP 'e' me--instance Parseable Spline where- parse = quotedParse parseSpline-- parseList = quotedParse $ sepBy1 parseSpline (char ';')--parseSpline :: Parse Spline-parseSpline = do ms <- parseP 's'- whitespace- me <- parseP 'e'- whitespace- ps <- sepBy1 parsePoint whitespace- return $ Spline ms me ps- where- parseP t = optional $ do char t- char ';'- parse---- -------------------------------------------------------------------------------data QuadType = NormalQT- | FastQT- | NoQT- deriving (Eq, Read)--instance Show QuadType where- show NormalQT = "normal"- show FastQT = "fast"- show NoQT = "none"--instance Parseable QuadType where- -- Have to take into account the slightly different interpretation- -- of Bool used as an option for parsing QuadType- parse = optionalQuoted- $ oneOf [ string "normal" >> return NormalQT- , string "fast" >> return FastQT- , string "none" >> return NoQT- , char '2' >> return FastQT -- weird bool- , liftM (bool NormalQT NoQT) parse- ]---- --------------------------------------------------------------------------------- | Specify the root node either as a Node attribute or a Graph attribute.-data Root = IsCentral -- ^ For Nodes only- | NotCentral -- ^ For Nodes only- | NodeName String -- ^ For Graphs only- deriving (Eq, Read)--instance Show Root where- show IsCentral = "true"- show NotCentral = "false"- show (NodeName n) = n--instance Parseable Root where- parse = optionalQuoted- $ oneOf [ liftM (bool IsCentral NotCentral) parse- , liftM NodeName parse- ]---- -------------------------------------------------------------------------------data RankType = SameRank- | MinRank- | SourceRank- | MaxRank- | SinkRank- deriving (Eq, Read)--instance Show RankType where- show SameRank = "same"- show MinRank = "min"- show SourceRank = "source"- show MaxRank = "max"- show SinkRank = "sink"--instance Parseable RankType where- parse = optionalQuoted- $ oneOf [ string "same" >> return SameRank- , string "min" >> return MinRank- , string "source" >> return SourceRank- , string "max" >> return MaxRank- , string "sink" >> return SinkRank- ]---- -------------------------------------------------------------------------------data RankDir = FromTop- | FromLeft- | FromBottom- | FromRight- deriving (Eq, Read)--instance Show RankDir where- show FromTop = "TB"- show FromLeft = "LR"- show FromBottom = "BT"- show FromRight = "RL"--instance Parseable RankDir where- parse = optionalQuoted- $ oneOf [ string "TB" >> return FromTop- , string "LR" >> return FromLeft- , string "BT" >> return FromBottom- , string "RL" >> return FromRight- ]---- -------------------------------------------------------------------------------data Shape- = BoxShape- | Polygon- | Ellipse- | Circle- | PointShape- | Egg- | Triangle- | Plaintext- | DiamondShape- | Trapezium- | Parallelogram- | House- | Pentagon- | Hexagon- | Septagon- | Octagon- | Doublecircle- | Doubleoctagon- | Tripleoctagon- | Invtriangle- | Invtrapezium- | Invhouse- | Mdiamond- | Msquare- | Mcircle- | Rectangle- | NoShape- | Note- | Tab- | Folder- | Box3d- | Component- deriving (Eq, Read)--instance Show Shape where- show BoxShape = "box"- show Polygon = "polygon"- show Ellipse = "ellipse"- show Circle = "circle"- show PointShape = "point"- show Egg = "egg"- show Triangle = "triangle"- show Plaintext = "plaintext"- show DiamondShape = "diamond"- show Trapezium = "trapezium"- show Parallelogram = "parallelogram"- show House = "house"- show Pentagon = "pentagon"- show Hexagon = "hexagon"- show Septagon = "septagon"- show Octagon = "octagon"- show Doublecircle = "doublecircle"- show Doubleoctagon = "doubleoctagon"- show Tripleoctagon = "tripleoctagon"- show Invtriangle = "invtriangle"- show Invtrapezium = "invtrapezium"- show Invhouse = "invhouse"- show Mdiamond = "mdiamond"- show Msquare = "msquare"- show Mcircle = "mcircle"- show Rectangle = "rectangle"- show NoShape = "none"- show Note = "note"- show Tab = "tab"- show Folder = "folder"- show Box3d = "box3d"- show Component = "component"--instance Parseable Shape where- parse = optionalQuoted- $ oneOf [ string "box" >> return BoxShape- , string "polygon" >> return Polygon- , string "ellipse" >> return Ellipse- , string "circle" >> return Circle- , string "point" >> return PointShape- , string "egg" >> return Egg- , string "triangle" >> return Triangle- , string "plaintext" >> return Plaintext- , string "diamond" >> return DiamondShape- , string "trapezium" >> return Trapezium- , string "parallelogram" >> return Parallelogram- , string "house" >> return House- , string "pentagon" >> return Pentagon- , string "hexagon" >> return Hexagon- , string "septagon" >> return Septagon- , string "octagon" >> return Octagon- , string "doublecircle" >> return Doublecircle- , string "doubleoctagon" >> return Doubleoctagon- , string "tripleoctagon" >> return Tripleoctagon- , string "invtriangle" >> return Invtriangle- , string "invtrapezium" >> return Invtrapezium- , string "invhouse" >> return Invhouse- , string "mdiamond" >> return Mdiamond- , string "msquare" >> return Msquare- , string "mcircle" >> return Mcircle- , string "rectangle" >> return Rectangle- , string "none" >> return NoShape- , string "note" >> return Note- , string "tab" >> return Tab- , string "folder" >> return Folder- , string "box3d" >> return Box3d- , string "component" >> return Component- ]---- -------------------------------------------------------------------------------data SmoothType = NoSmooth- | AvgDist- | GraphDist- | PowerDist- | RNG- | Spring- | TriangleSmooth- deriving (Eq, Read)--instance Show SmoothType where- show NoSmooth = "none"- show AvgDist = "avg_dist"- show GraphDist = "graph_dist"- show PowerDist = "power_dist"- show RNG = "rng"- show Spring = "spring"- show TriangleSmooth = "triangle"--instance Parseable SmoothType where- parse = optionalQuoted- $ oneOf [ string "none" >> return NoSmooth- , string "avg_dist" >> return AvgDist- , string "graph_dist" >> return GraphDist- , string "power_dist" >> return PowerDist- , string "rng" >> return RNG- , string "spring" >> return Spring- , string "triangle" >> return TriangleSmooth- ]---- --------------------------------------------------------------------------------- | It it assumed that at least one of these is @Just{}@.-data StartType = ST (Maybe STStyle) (Maybe Int) -- Use a Word?- deriving (Eq, Read)--instance Show StartType where- show (ST ms mi) = maybe id ((++) . show) ms- $ maybe "" show mi--instance Parseable StartType where- parse = optionalQuoted- $ do ms <- optional parse- mi <- optional parse- return $ ST ms mi--data STStyle = RegularStyle- | Self- | Random- deriving (Eq, Read)--instance Show STStyle where- show RegularStyle = "regular"- show Self = "self"- show Random = "random"--instance Parseable STStyle where- parse = oneOf [ string "regular" >> return RegularStyle- , string "self" >> return Self- , string "random" >> return Random- ]---- -------------------------------------------------------------------------------data Style = Stl StyleName (Maybe String)- deriving (Eq, Read)--instance Show Style where- show (Stl nm marg) = maybe snm- (\arg -> show $ snm ++ '(' : arg ++ ")")- marg- where- snm = show nm--instance Parseable Style where- parse = oneOf [ optionalQuoted $ liftM (\nm -> Stl nm Nothing) parse- , quotedParse $ do nm <- parse- char '('- arg <- many1- $ satisfy (flip notElem "()")-- char ')'- return $ Stl nm (Just arg)- ]--data StyleName = Dashed -- ^ Nodes and Edges- | Dotted -- ^ Nodes and Edges- | Solid -- ^ Nodes and Edges- | Bold -- ^ Nodes and Edges- | Invisible -- ^ Nodes and Edges- | Filled -- ^ Nodes and Clusters- | Diagonals -- ^ Nodes only- | Rounded -- ^ Nodes and Clusters- deriving (Eq, Read)--instance Show StyleName where- show Filled = "filled"- show Invisible = "invis"- show Diagonals = "diagonals"- show Rounded = "rounded"- show Dashed = "dashed"- show Dotted = "dotted"- show Solid = "solid"- show Bold = "bold"--instance Parseable StyleName where- parse = optionalQuoted- $ oneOf [ string "filled" >> return Filled- , string "invis" >> return Invisible- , string "diagonals" >> return Diagonals- , string "rounded" >> return Rounded- , string "dashed" >> return Dashed- , string "dotted" >> return Dotted- , string "solid" >> return Solid- , string "bold" >> return Bold- ]---- -------------------------------------------------------------------------------newtype PortPos = PP CompassPoint- deriving (Eq, Read)--instance Show PortPos where- show (PP cp) = show cp--instance Parseable PortPos where- parse = liftM PP parse--data CompassPoint = North- | NorthEast- | East- | SouthEast- | South- | SouthWest- | West- | NorthWest- | CenterPoint- | NoCP- deriving (Eq, Read)--instance Show CompassPoint where- show North = "n"- show NorthEast = "ne"- show East = "e"- show SouthEast = "se"- show South = "s"- show SouthWest = "sw"- show West = "w"- show NorthWest = "nw"- show CenterPoint = "c"- show NoCP = "_"--instance Parseable CompassPoint where- parse = optionalQuoted- $ oneOf [ string "n" >> return North- , string "ne" >> return NorthEast- , string "e" >> return East- , string "se" >> return SouthEast- , string "s" >> return South- , string "sw" >> return SouthWest- , string "w" >> return West- , string "nw" >> return NorthWest- , string "c" >> return CenterPoint- , string "_" >> return NoCP- ]---- -------------------------------------------------------------------------------data ViewPort = VP { wVal :: Double- , hVal :: Double- , zVal :: Double- , focus :: Maybe FocusType- }- deriving (Eq, Read)--instance Show ViewPort where- show vp = show- . maybe id (flip (++) . show) (focus vp)- $ show (wVal vp)- ++ ',' : show (hVal vp)- ++ ',' : show (zVal vp)--instance Parseable ViewPort where- parse = quotedParse- $ do wv <- parse- char ','- hv <- parse- char ','- zv <- parse- mf <- optional $ char ',' >> parse- return $ VP wv hv zv mf--data FocusType = XY Point- | NodeFocus String- deriving (Eq, Read)--instance Show FocusType where- show (XY p) = showPoint p- show (NodeFocus nm) = nm--instance Parseable FocusType where- parse = oneOf [ liftM XY parsePoint- , liftM NodeFocus stringBlock- ]---- --------------------------------------------------------------------------------- | Note that 'VCenter' is only valid for Nodes.-data VerticalPlacement = VTop- | VCenter- | VBottom- deriving (Eq, Read)--instance Show VerticalPlacement where- show VTop = "t"- show VCenter = "c"- show VBottom = "b"--instance Parseable VerticalPlacement where- parse = optionalQuoted- $ oneOf [ string "t" >> return VTop- , string "c" >> return VCenter- , string "b" >> return VBottom- ]----- -------------------------------------------------------------------------------data ScaleType = UniformScale- | NoScale- | FillWidth- | FillHeight- | FillBoth- deriving (Eq, Read)--instance Show ScaleType where- show UniformScale = "true"- show NoScale = "false"- show FillWidth = "width"- show FillHeight = "height"- show FillBoth = "both"--instance Parseable ScaleType where- parse = optionalQuoted- $ oneOf [ string "true" >> return UniformScale- , string "false" >> return NoScale- , string "width" >> return FillWidth- , string "height" >> return FillHeight- , string "both" >> return FillBoth- ]---- -------------------------------------------------------------------------------data Justification = JLeft- | JRight- | JCenter- deriving (Eq, Read)--instance Show Justification where- show JLeft = "l"- show JRight = "r"- show JCenter = "c"--instance Parseable Justification where- parse = optionalQuoted- $ oneOf [ string "l" >> return JLeft- , string "r" >> return JRight- , string "c" >> return JCenter- ]---- -------------------------------------------------------------------------------data Ratios = AspectRatio Double- | FillRatio- | CompressRatio- | ExpandRatio- | AutoRatio- deriving (Eq, Read)--instance Show Ratios where- show (AspectRatio r) = show r- show FillRatio = "fill"- show CompressRatio = "compress"- show ExpandRatio = "expand"- show AutoRatio = "auto"--instance Parseable Ratios where- parse = optionalQuoted- $ oneOf [ liftM AspectRatio parse- , string "fill" >> return FillRatio- , string "compress" >> return CompressRatio- , string "expand" >> return ExpandRatio- , string "auto" >> return AutoRatio- ]---- --------------------------------------------------------------------------------- | Represents 'String's that definitely have quotes around them.-newtype QuotedString = QS { qStr :: String }- deriving (Eq, Read)--instance Show QuotedString where- show = show . qStr--instance Parseable QuotedString where- parse = liftM (QS . tail . init) quotedString---- --------------------------------------------------------------------------------- Utility Functions---- | Fold over 'Bool's.-bool :: a -> a -> Bool -> a-bool t f b = if b- then t- else f+ <http://graphviz.org/doc/info/attrs.html>++ For more information on usage, etc. please see that document.++ A summary of known current constraints\/limitations\/differences:++ * There might still be a few cases where quotes are still not+ escaped/parsed correctly; if you find such a situation, please+ let me know; however, you should be able to use 'String' values+ directly without having to worry about when quotes are required+ or extra escaping of quote characters as 'PrintDot' and+ 'ParseDot' instances for 'String' should take care of that+ for you.++ * Note that for an edge, in /Dot/ parlance if the edge goes from+ /A/ to /B/, then /A/ is the tail node and /B/ is the head node+ (since /A/ is at the tail end of the arrow).++ * GraphViz says that a number like @/.02/@ is valid; this library+ disagrees.++ * When parsing named 'Color' values, the entire value entered is+ kept as-is; this library as yet has no understanding of different+ color schemes, etc.++ * ColorList and PointfList are defined as actual lists (but+ 'LayerList' is not). Note that for the Color 'Attribute' for+ node values, only a single Color is valid; edges are allowed+ multiple colors with one spline/arrow per color in the list (but+ you must have at least one 'Color' in the list). This might be+ changed in future.++ * Style is implemented as a list of 'StyleItem' values; note that+ empty lists are not allowed.++ * A lot of values have a possible value of @none@. These now+ have custom constructors. In fact, most constructors have been+ expanded upon to give an idea of what they represent rather than+ using generic terms.++ * @PointF@ and 'Point' have been combined, and feature support for pure+ 'Int'-based co-ordinates as well as 'Double' ones (i.e. no floating+ point-only points for Point). The optional '!' and third value+ for Point are not available.++ * 'Rect' uses two 'Point' values to denote the lower-left and+ top-right corners.++ * The two 'LabelLoc' attributes have been combined.++ * The defined 'LayerSep' is not used to parse 'LayerRange' or+ 'LayerList'; the default (@[' ', ':', '\t']@) is instead used.++ * @SplineType@ has been replaced with @['Spline']@.++ * Only polygon-based 'Shape's are available.++ * 'PortPos' only has the 'CompassPoint' option, not+ @PortName[:CompassPoint]@ (since record shapes aren't allowed,+ and parsing HTML-like labels could be problematic).++ * Not every 'Attribute' is fully documented/described. However,+ all those which have specific allowed values should be covered.++ * Deprecated 'Overlap' algorithms are not defined.++ -}+module Data.GraphViz.Attributes+ ( -- * The actual /Dot/ attributes.+ Attribute(..)+ , Attributes+ -- ** Validity functions on @Attribute@ values.+ , usedByGraphs+ , usedBySubGraphs+ , usedByClusters+ , usedByNodes+ , usedByEdges+ -- * Value types for @Attribute@s.+ , EscString+ , URL(..)+ , ArrowType(..)+ , AspectType(..)+ , Rect(..)+ , Color(..)+ , ClusterMode(..)+ , DirType(..)+ , DEConstraints(..)+ , DPoint(..)+ , ModeType(..)+ , Model(..)+ , Label(..)+ , Point(..)+ , Overlap(..)+ , LayerRange(..)+ , LayerID(..)+ , LayerList(..)+ , OutputMode(..)+ , Pack(..)+ , PackMode(..)+ , Pos(..)+ , EdgeType(..)+ , PageDir(..)+ , Spline(..)+ , QuadType(..)+ , Root(..)+ , RankType(..)+ , RankDir(..)+ , Shape(..)+ , SmoothType(..)+ , StartType(..)+ , STStyle(..)+ , StyleItem(..)+ , StyleName(..)+ , PortPos(..)+ , CompassPoint(..)+ , ViewPort(..)+ , FocusType(..)+ , VerticalPlacement(..)+ , ScaleType(..)+ , Justification(..)+ , Ratios(..)+ -- * Types representing the Dot grammar for @ArrowType@.+ , ArrowShape(..)+ , ArrowModifier(..)+ , ArrowFill(..)+ , ArrowSide(..)+ -- ** Default @ArrowType@ aliases.+ -- *** The 9 primitive @ArrowShape@s.+ , box, crow, diamond, dotArrow, inv, noArrow, normal, tee, vee+ -- *** 5 derived Arrows.+ , oDot, invDot, invODot, oBox, oDiamond+ -- *** 5 supported cases for backwards compatibility+ , eDiamond, openArr, halfOpen, emptyArr, invEmpty+ -- ** @ArrowModifier@ instances+ , noMods, openMod+ ) where++import Data.GraphViz.Types.Internal+import Data.GraphViz.Types.Parsing+import Data.GraphViz.Types.Printing++import Data.Char(isDigit, isHexDigit)+import Data.Maybe(isJust, maybe)+import Data.Word(Word8)+import Numeric(showHex, readHex)+import Control.Monad(liftM)++-- -----------------------------------------------------------------------------++{- |++ These attributes have been implemented in a /permissive/ manner:+ that is, rather than split them up based on which type of value+ they are allowed, they have all been included in the one data type,+ with functions to determine if they are indeed valid for what+ they're being applied to.++ To interpret the /Valid for/ listings:++ [@G@] Valid for Graphs.++ [@C@] Valid for Clusters.++ [@S@] Valid for Sub-Graphs (and also Clusters).++ [@N@] Valid for Nodes.++ [@E@] Valid for Edges.++ The /Default/ listings are those that the various GraphViz commands+ use if that 'Attribute' isn't specified (in cases where this is+ /none/, this is equivalent to a 'Nothing' value; that is, no value+ is used). The /Parsing Default/ listings represent what value is+ used (i.e. corresponds to 'True') when the 'Attribute' name is+ listed on its own in /Dot/ source code.+-}+data Attribute+ = Damping Double -- ^ /Valid for/: G; /Default/: @0.99@; /Minimum/: @0.0@; /Notes/: neato only+ | K Double -- ^ /Valid for/: GC; /Default/: @0.3@; /Minimum/: @0@; /Notes/: sfdp, fdp only+ | URL URL -- ^ /Valid for/: ENGC; /Default/: none; /Notes/: svg, postscript, map only+ | ArrowHead ArrowType -- ^ /Valid for/: E; /Default/: @'normal'@+ | ArrowSize Double -- ^ /Valid for/: E; /Default/: @1.0@; /Minimum/: @0.0@+ | ArrowTail ArrowType -- ^ /Valid for/: E; /Default/: @'normal'@+ | Aspect AspectType -- ^ /Valid for/: G; /Notes/: dot only+ | Bb Rect -- ^ /Valid for/: G; /Notes/: write only+ | BgColor Color -- ^ /Valid for/: GC; /Default/: none+ | Center Bool -- ^ /Valid for/: G; /Default/: @'False'@; /Parsing Default/: 'True'+ | Charset String -- ^ /Valid for/: G; /Default/: @\"UTF-8\"@+ | ClusterRank ClusterMode -- ^ /Valid for/: G; /Default/: @'Local'@; /Notes/: dot only+ | Color [Color] -- ^ /Valid for/: ENC; /Default/: @black@+ | ColorScheme String -- ^ /Valid for/: ENCG; /Default/: @\"\"@+ | Comment String -- ^ /Valid for/: ENG; /Default/: @\"\"@+ | Compound Bool -- ^ /Valid for/: G; /Default/: @'False'@; /Parsing Default/: 'True'; /Notes/: dot only+ | Concentrate Bool -- ^ /Valid for/: G; /Default/: @'False'@; /Parsing Default/: 'True'+ | Constraint Bool -- ^ /Valid for/: E; /Default/: @'True'@; /Parsing Default/: 'True'; /Notes/: dot only+ | Decorate Bool -- ^ /Valid for/: E; /Default/: @'False'@; /Parsing Default/: 'True'+ | DefaultDist Double -- ^ /Valid for/: G; /Default/: @1+(avg. len)*sqrt(|V|)@; /Minimum/: @epsilon@; /Notes/: neato only+ | Dim Int -- ^ /Valid for/: G; /Default/: @2@; /Minimum/: @2@; /Notes/: sfdp, fdp, neato only+ | Dimen Int -- ^ /Valid for/: G; /Default/: @2@; /Minimum/: @2@; /Notes/: sfdp, fdp, neato only+ | Dir DirType -- ^ /Valid for/: E; /Default/: @'Forward'@ (directed), @'NoDir'@ (undirected)+ | DirEdgeConstraints DEConstraints -- ^ /Valid for/: G; /Default/: @'NoConstraints'@; /Parsing Default/: 'EdgeConstraints'; /Notes/: neato only+ | Distortion Double -- ^ /Valid for/: N; /Default/: @0.0@; /Minimum/: @-100.0@+ | DPI Double -- ^ /Valid for/: G; /Default/: @96.0@, @0.0@; /Notes/: svg, bitmap output only; \"resolution\" is a synonym+ | EdgeURL URL -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: svg, map only+ | EdgeTarget EscString -- ^ /Valid for/: E; /Default/: none; /Notes/: svg, map only+ | EdgeTooltip EscString -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: svg, cmap only+ | Epsilon Double -- ^ /Valid for/: G; /Default/: @.0001 * # nodes@ (@mode == 'KK'@), @.0001@ (@mode == 'Major'@); /Notes/: neato only+ | ESep DPoint -- ^ /Valid for/: G; /Default/: @+3@; /Notes/: not dot+ | FillColor Color -- ^ /Valid for/: NC; /Default/: @lightgrey@ (nodes), @black@ (clusters)+ | FixedSize Bool -- ^ /Valid for/: N; /Default/: @'False'@; /Parsing Default/: 'True'+ | FontColor Color -- ^ /Valid for/: ENGC; /Default/: @black@+ | FontName String -- ^ /Valid for/: ENGC; /Default/: @\"Times-Roman\"@+ | FontNames String -- ^ /Valid for/: G; /Default/: @\"\"@; /Notes/: svg only+ | FontPath String -- ^ /Valid for/: G; /Default/: system-dependent+ | FontSize Double -- ^ /Valid for/: ENGC; /Default/: @14.0@; /Minimum/: @1.0@+ | Group String -- ^ /Valid for/: N; /Default/: @\"\"@; /Notes/: dot only+ | HeadURL URL -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: svg, map only+ | HeadClip Bool -- ^ /Valid for/: E; /Default/: @'True'@; /Parsing Default/: 'True'+ | HeadLabel Label -- ^ /Valid for/: E; /Default/: @\"\"@+ | HeadPort PortPos -- ^ /Valid for/: E; /Default/: @'PP' 'CenterPoint'@+ | HeadTarget EscString -- ^ /Valid for/: E; /Default/: none; /Notes/: svg, map only+ | HeadTooltip EscString -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: svg, cmap only+ | Height Double -- ^ /Valid for/: N; /Default/: @0.5@; /Minimum/: @0.02@+ | ID Label -- ^ /Valid for/: GNE; /Default/: @\"\"@; /Notes/: svg, postscript, map only+ | Image String -- ^ /Valid for/: N; /Default/: @\"\"@+ | ImageScale ScaleType -- ^ /Valid for/: N; /Default/: @'NoScale'@; /Parsing Default/: 'UniformScale'+ | Label Label -- ^ /Valid for/: ENGC; /Default/: @'StrLabel' \"\N\"@ (nodes), @'StrLabel' \"\"@ (otherwise)+ | LabelURL URL -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: svg, map only+ | LabelAngle Double -- ^ /Valid for/: E; /Default/: @-25.0@; /Minimum/: @-180.0@+ | LabelDistance Double -- ^ /Valid for/: E; /Default/: @1.0@; /Minimum/: @0.0@+ | LabelFloat Bool -- ^ /Valid for/: E; /Default/: @'False'@; /Parsing Default/: 'True'+ | LabelFontColor Color -- ^ /Valid for/: E; /Default/: @black@+ | LabelFontName String -- ^ /Valid for/: E; /Default/: @\"Times-Roman\"@+ | LabelFontSize Double -- ^ /Valid for/: E; /Default/: @14.0@; /Minimum/: @1.0@+ | LabelJust Justification -- ^ /Valid for/: GC; /Default/: @'JCenter'@+ | LabelLoc VerticalPlacement -- ^ /Valid for/: GCN; /Default/: @'VTop'@ (clusters), @'VBottom'@ (root graphs), @'VCenter'@ (nodes)+ | LabelTarget EscString -- ^ /Valid for/: E; /Default/: none; /Notes/: svg, map only+ | LabelTooltip EscString -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: svg, cmap only+ | Landscape Bool -- ^ /Valid for/: G; /Default/: @'False'@; /Parsing Default/: 'True'+ | Layer LayerRange -- ^ /Valid for/: EN; /Default/: @\"\"@+ | Layers LayerList -- ^ /Valid for/: G; /Default/: @\"\"@+ | LayerSep String -- ^ /Valid for/: G; /Default/: @\" :\t\"@+ | Layout String -- ^ /Valid for/: G; /Default/: @\"\"@+ | Len Double -- ^ /Valid for/: E; /Default/: @1.0@ (neato), @0.3@ (fdp); /Notes/: fdp, neato only+ | Levels Int -- ^ /Valid for/: G; /Default/: @MAXINT@; /Minimum/: @0@; /Notes/: sfdp only+ | LevelsGap Double -- ^ /Valid for/: G; /Default/: @0.0@; /Notes/: neato only+ | LHead String -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: dot only+ | LPos Point -- ^ /Valid for/: EGC; /Notes/: write only+ | LTail String -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: dot only+ | Margin DPoint -- ^ /Valid for/: NG; /Default/: device-dependent+ | MaxIter Int -- ^ /Valid for/: G; /Default/: @100 * # nodes@ (@mode == 'KK'@), @200@ (@mode == 'Major'@), @600@ (fdp); /Notes/: fdp, neato only+ | MCLimit Double -- ^ /Valid for/: G; /Default/: @1.0@; /Notes/: dot only+ | MinDist Double -- ^ /Valid for/: G; /Default/: @1.0@; /Minimum/: @0.0@; /Notes/: circo only+ | MinLen Int -- ^ /Valid for/: E; /Default/: @1@; /Minimum/: @0@; /Notes/: dot only+ | Mode ModeType -- ^ /Valid for/: G; /Default/: @'Major'@; /Notes/: neato only+ | Model Model -- ^ /Valid for/: G; /Default/: @'ShortPath'@; /Notes/: neato only+ | Mosek Bool -- ^ /Valid for/: G; /Default/: @'False'@; /Parsing Default/: 'True'; /Notes/: neato only; requires the Mosek software+ | NodeSep Double -- ^ /Valid for/: G; /Default/: @0.25@; /Minimum/: @0.02@; /Notes/: dot only+ | NoJustify Bool -- ^ /Valid for/: GCNE; /Default/: @'False'@; /Parsing Default/: 'True'+ | Normalize Bool -- ^ /Valid for/: G; /Default/: @'False'@; /Parsing Default/: 'True'; /Notes/: not dot+ | Nslimit Double -- ^ /Valid for/: G; /Notes/: dot only+ | Nslimit1 Double -- ^ /Valid for/: G; /Notes/: dot only+ | Ordering String -- ^ /Valid for/: G; /Default/: @\"\"@; /Notes/: dot only+ | Orientation Double -- ^ /Valid for/: N; /Default/: @0.0@; /Minimum/: @360.0@+ | OrientationGraph String -- ^ /Valid for/: G; /Default/: @\"\"@; /Notes/: Landscape if \"[lL]*\" and rotate not defined+ | OutputOrder OutputMode -- ^ /Valid for/: G; /Default/: @'BreadthFirst'@+ | Overlap Overlap -- ^ /Valid for/: G; /Default/: @'KeepOverlaps'@; /Parsing Default/: 'KeepOverlaps'; /Notes/: not dot+ | OverlapScaling Double -- ^ /Valid for/: G; /Default/: @-4@; /Minimum/: @-1.0e10@; /Notes/: prism only+ | Pack Pack -- ^ /Valid for/: G; /Default/: @'False'@; /Parsing Default/: 'DoPack'; /Notes/: not dot+ | PackMode PackMode -- ^ /Valid for/: G; /Default/: @'PackNode'@; /Notes/: not dot+ | Pad DPoint -- ^ /Valid for/: G; /Default/: @'DVal' 0.0555@ (4 points)+ | Page Point -- ^ /Valid for/: G+ | PageDir PageDir -- ^ /Valid for/: G; /Default/: @'BL'@+ | PenColor Color -- ^ /Valid for/: C; /Default/: @black@+ | PenWidth Double -- ^ /Valid for/: CNE; /Default/: @1.0@; /Minimum/: @0.0@+ | Peripheries Int -- ^ /Valid for/: NC; /Default/: shape default (nodes), @1@ (clusters); /Minimum/: 0+ | Pin Bool -- ^ /Valid for/: N; /Default/: @'False'@; /Parsing Default/: 'True'; /Notes/: fdp, neato only+ | Pos Pos -- ^ /Valid for/: EN+ | QuadTree QuadType -- ^ /Valid for/: G; /Default/: @'NormalQT'@; /Parsing Default/: 'NormalQT'; /Notes/: sfdp only+ | Quantum Double -- ^ /Valid for/: G; /Default/: @0.0@; /Minimum/: @0.0@+ | Rank RankType -- ^ /Valid for/: S; /Notes/: dot only+ | RankDir RankDir -- ^ /Valid for/: G; /Default/: @'TB'@; /Notes/: dot only+ | Ranksep Double -- ^ /Valid for/: G; /Default/: @0.5@ (dot), @1.0@ (twopi); /Minimum/: 0.02; /Notes/: twopi, dot only+ | Ratio Ratios -- ^ /Valid for/: G+ | Rects Rect -- ^ /Valid for/: N; /Notes/: write only+ | Regular Bool -- ^ /Valid for/: N; /Default/: @'False'@; /Parsing Default/: 'True'+ | ReMinCross Bool -- ^ /Valid for/: G; /Default/: @'False'@; /Parsing Default/: 'True'; /Notes/: dot only+ | RepulsiveForce Double -- ^ /Valid for/: G; /Default/: @1.0@; /Minimum/: @0.0@; /Notes/: sfdp only+ | Root Root -- ^ /Valid for/: GN; /Default/: @'NodeName' \"\"@ (graphs), @'NotCentral'@ (nodes); /Parsing Default/: 'IsCentral'; /Notes/: circo, twopi only+ | Rotate Int -- ^ /Valid for/: G; /Default/: @0@+ | SameHead String -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: dot only+ | SameTail String -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: dot only+ | SamplePoints Int -- ^ /Valid for/: N; /Default/: @8@ (output), @20@ (overlap and image maps)+ | SearchSize Int -- ^ /Valid for/: G; /Default/: @30@; /Notes/: dot only+ | Sep DPoint -- ^ /Valid for/: G; /Default/: @+4@; /Notes/: not dot+ | Shape Shape -- ^ /Valid for/: N; /Default/: @'Ellipse'@+ | ShapeFile String -- ^ /Valid for/: N; /Default/: @\"\"@+ | ShowBoxes Int -- ^ /Valid for/: ENG; /Default/: @0@; /Minimum/: @0@; /Notes/: dot only+ | Sides Int -- ^ /Valid for/: N; /Default/: @4@; /Minimum/: @0@+ | Size Point -- ^ /Valid for/: G+ | Skew Double -- ^ /Valid for/: N; /Default/: @0.0@; /Minimum/: @-100.0@+ | Smoothing SmoothType -- ^ /Valid for/: G; /Default/: @'NoSmooth'@; /Notes/: sfdp only+ | SortV Int -- ^ /Valid for/: GCN; /Default/: @0@; /Minimum/: @0@+ | Splines EdgeType -- ^ /Valid for/: G; /Parsing Default/: 'SplineEdges'+ | Start StartType -- ^ /Valid for/: G; /Default/: @\"\"@; /Notes/: fdp, neato only+ | Style [StyleItem] -- ^ /Valid for/: ENC+ | StyleSheet String -- ^ /Valid for/: G; /Default/: @\"\"@; /Notes/: svg only+ | TailURL URL -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: svg, map only+ | TailClip Bool -- ^ /Valid for/: E; /Default/: @'True'@; /Parsing Default/: 'True'+ | TailLabel Label -- ^ /Valid for/: E; /Default/: @\"\"@+ | TailPort PortPos -- ^ /Valid for/: E; /Default/: center+ | TailTarget EscString -- ^ /Valid for/: E; /Default/: none; /Notes/: svg, map only+ | TailTooltip EscString -- ^ /Valid for/: E; /Default/: @\"\"@; /Notes/: svg, cmap only+ | Target EscString -- ^ /Valid for/: ENGC; /Default/: none; /Notes/: svg, map only+ | Tooltip EscString -- ^ /Valid for/: NEC; /Default/: @\"\"@; /Notes/: svg, cmap only+ | TrueColor Bool -- ^ /Valid for/: G; /Parsing Default/: 'True'; /Notes/: bitmap output only+ | Vertices [Point] -- ^ /Valid for/: N; /Notes/: write only+ | ViewPort ViewPort -- ^ /Valid for/: G; /Default/: none+ | VoroMargin Double -- ^ /Valid for/: G; /Default/: @0.05@; /Minimum/: @0.0@; /Notes/: not dot+ | Weight Double -- ^ /Valid for/: E; /Default/: @1.0@; /Minimum/: @0@ (dot), @1@ (neato,fdp,sfdp)+ | Width Double -- ^ /Valid for/: N; /Default/: @0.75@; /Minimum/: @0.01@+ | Z Double -- ^ /Valid for/: N; /Default/: @0.0@; /Minimum/: @-MAXFLOAT@, @-1000@+ deriving (Eq, Show, Read)++type Attributes = [Attribute]++instance PrintDot Attribute where+ unqtDot (Damping v) = printField "Damping" v+ unqtDot (K v) = printField "K" v+ unqtDot (URL v) = printField "URL" v+ unqtDot (ArrowHead v) = printField "arrowhead" v+ unqtDot (ArrowSize v) = printField "arrowsize" v+ unqtDot (ArrowTail v) = printField "arrowtail" v+ unqtDot (Aspect v) = printField "aspect" v+ unqtDot (Bb v) = printField "bb" v+ unqtDot (BgColor v) = printField "bgcolor" v+ unqtDot (Center v) = printField "center" v+ unqtDot (Charset v) = printField "charset" v+ unqtDot (ClusterRank v) = printField "clusterrank" v+ unqtDot (Color v) = printField "color" v+ unqtDot (ColorScheme v) = printField "colorscheme" v+ unqtDot (Comment v) = printField "comment" v+ unqtDot (Compound v) = printField "compound" v+ unqtDot (Concentrate v) = printField "concentrate" v+ unqtDot (Constraint v) = printField "constraint" v+ unqtDot (Decorate v) = printField "decorate" v+ unqtDot (DefaultDist v) = printField "defaultdist" v+ unqtDot (Dim v) = printField "dim" v+ unqtDot (Dimen v) = printField "dimen" v+ unqtDot (Dir v) = printField "dir" v+ unqtDot (DirEdgeConstraints v) = printField "diredgeconstraints" v+ unqtDot (Distortion v) = printField "distortion" v+ unqtDot (DPI v) = printField "dpi" v+ unqtDot (EdgeURL v) = printField "edgeURL" v+ unqtDot (EdgeTarget v) = printField "edgetarget" v+ unqtDot (EdgeTooltip v) = printField "edgetooltip" v+ unqtDot (Epsilon v) = printField "epsilon" v+ unqtDot (ESep v) = printField "esep" v+ unqtDot (FillColor v) = printField "fillcolor" v+ unqtDot (FixedSize v) = printField "fixedsize" v+ unqtDot (FontColor v) = printField "fontcolor" v+ unqtDot (FontName v) = printField "fontname" v+ unqtDot (FontNames v) = printField "fontnames" v+ unqtDot (FontPath v) = printField "fontpath" v+ unqtDot (FontSize v) = printField "fontsize" v+ unqtDot (Group v) = printField "group" v+ unqtDot (HeadURL v) = printField "headURL" v+ unqtDot (HeadClip v) = printField "headclip" v+ unqtDot (HeadLabel v) = printField "headlabel" v+ unqtDot (HeadPort v) = printField "headport" v+ unqtDot (HeadTarget v) = printField "headtarget" v+ unqtDot (HeadTooltip v) = printField "headtooltip" v+ unqtDot (Height v) = printField "height" v+ unqtDot (ID v) = printField "id" v+ unqtDot (Image v) = printField "image" v+ unqtDot (ImageScale v) = printField "imagescale" v+ unqtDot (Label v) = printField "label" v+ unqtDot (LabelURL v) = printField "labelURL" v+ unqtDot (LabelAngle v) = printField "labelangle" v+ unqtDot (LabelDistance v) = printField "labeldistance" v+ unqtDot (LabelFloat v) = printField "labelfloat" v+ unqtDot (LabelFontColor v) = printField "labelfontcolor" v+ unqtDot (LabelFontName v) = printField "labelfontname" v+ unqtDot (LabelFontSize v) = printField "labelfontsize" v+ unqtDot (LabelJust v) = printField "labeljust" v+ unqtDot (LabelLoc v) = printField "labelloc" v+ unqtDot (LabelTarget v) = printField "labeltarget" v+ unqtDot (LabelTooltip v) = printField "labeltooltip" v+ unqtDot (Landscape v) = printField "landscape" v+ unqtDot (Layer v) = printField "layer" v+ unqtDot (Layers v) = printField "layers" v+ unqtDot (LayerSep v) = printField "layersep" v+ unqtDot (Layout v) = printField "layout" v+ unqtDot (Len v) = printField "len" v+ unqtDot (Levels v) = printField "levels" v+ unqtDot (LevelsGap v) = printField "levelsgap" v+ unqtDot (LHead v) = printField "lhead" v+ unqtDot (LPos v) = printField "lp" v+ unqtDot (LTail v) = printField "ltail" v+ unqtDot (Margin v) = printField "margin" v+ unqtDot (MaxIter v) = printField "maxiter" v+ unqtDot (MCLimit v) = printField "mclimit" v+ unqtDot (MinDist v) = printField "mindist" v+ unqtDot (MinLen v) = printField "minlen" v+ unqtDot (Mode v) = printField "mode" v+ unqtDot (Model v) = printField "model" v+ unqtDot (Mosek v) = printField "mosek" v+ unqtDot (NodeSep v) = printField "nodesep" v+ unqtDot (NoJustify v) = printField "nojustify" v+ unqtDot (Normalize v) = printField "normalize" v+ unqtDot (Nslimit v) = printField "nslimit" v+ unqtDot (Nslimit1 v) = printField "nslimit1" v+ unqtDot (Ordering v) = printField "ordering" v+ unqtDot (Orientation v) = printField "orientation" v+ unqtDot (OrientationGraph v) = printField "orientation" v+ unqtDot (OutputOrder v) = printField "outputorder" v+ unqtDot (Overlap v) = printField "overlap" v+ unqtDot (OverlapScaling v) = printField "overlap_scaling" v+ unqtDot (Pack v) = printField "pack" v+ unqtDot (PackMode v) = printField "packmode" v+ unqtDot (Pad v) = printField "pad" v+ unqtDot (Page v) = printField "page" v+ unqtDot (PageDir v) = printField "pagedir" v+ unqtDot (PenColor v) = printField "pencolor" v+ unqtDot (PenWidth v) = printField "penwidth" v+ unqtDot (Peripheries v) = printField "peripheries" v+ unqtDot (Pin v) = printField "pin" v+ unqtDot (Pos v) = printField "pos" v+ unqtDot (QuadTree v) = printField "quadtree" v+ unqtDot (Quantum v) = printField "quantum" v+ unqtDot (Rank v) = printField "rank" v+ unqtDot (RankDir v) = printField "rankdir" v+ unqtDot (Ranksep v) = printField "ranksep" v+ unqtDot (Ratio v) = printField "ratio" v+ unqtDot (Rects v) = printField "rects" v+ unqtDot (Regular v) = printField "regular" v+ unqtDot (ReMinCross v) = printField "remincross" v+ unqtDot (RepulsiveForce v) = printField "repulsiveforce" v+ unqtDot (Root v) = printField "root" v+ unqtDot (Rotate v) = printField "rotate" v+ unqtDot (SameHead v) = printField "samehead" v+ unqtDot (SameTail v) = printField "sametail" v+ unqtDot (SamplePoints v) = printField "samplepoints" v+ unqtDot (SearchSize v) = printField "searchsize" v+ unqtDot (Sep v) = printField "sep" v+ unqtDot (Shape v) = printField "shape" v+ unqtDot (ShapeFile v) = printField "shapefile" v+ unqtDot (ShowBoxes v) = printField "showboxes" v+ unqtDot (Sides v) = printField "sides" v+ unqtDot (Size v) = printField "size" v+ unqtDot (Skew v) = printField "skew" v+ unqtDot (Smoothing v) = printField "smoothing" v+ unqtDot (SortV v) = printField "sortv" v+ unqtDot (Splines v) = printField "splines" v+ unqtDot (Start v) = printField "start" v+ unqtDot (Style v) = printField "style" v+ unqtDot (StyleSheet v) = printField "stylesheet" v+ unqtDot (TailURL v) = printField "tailURL" v+ unqtDot (TailClip v) = printField "tailclip" v+ unqtDot (TailLabel v) = printField "taillabel" v+ unqtDot (TailPort v) = printField "tailport" v+ unqtDot (TailTarget v) = printField "tailtarget" v+ unqtDot (TailTooltip v) = printField "tailtooltip" v+ unqtDot (Target v) = printField "target" v+ unqtDot (Tooltip v) = printField "tooltip" v+ unqtDot (TrueColor v) = printField "truecolor" v+ unqtDot (Vertices v) = printField "vertices" v+ unqtDot (ViewPort v) = printField "viewport" v+ unqtDot (VoroMargin v) = printField "voro_margin" v+ unqtDot (Weight v) = printField "weight" v+ unqtDot (Width v) = printField "width" v+ unqtDot (Z v) = printField "z" v++ listToDot = unqtListToDot++instance ParseDot Attribute where+ parseUnqt = oneOf [ liftM Damping $ parseField "Damping"+ , liftM K $ parseField "K"+ , liftM URL $ parseFields ["URL", "href"]+ , liftM ArrowHead $ parseField "arrowhead"+ , liftM ArrowSize $ parseField "arrowsize"+ , liftM ArrowTail $ parseField "arrowtail"+ , liftM Aspect $ parseField "aspect"+ , liftM Bb $ parseField "bb"+ , liftM BgColor $ parseField "bgcolor"+ , liftM Center $ parseFieldBool "center"+ , liftM Charset $ parseField "charset"+ , liftM ClusterRank $ parseField "clusterrank"+ , liftM Color $ parseField "color"+ , liftM ColorScheme $ parseField "colorscheme"+ , liftM Comment $ parseField "comment"+ , liftM Compound $ parseFieldBool "compound"+ , liftM Concentrate $ parseFieldBool "concentrate"+ , liftM Constraint $ parseFieldBool "constraint"+ , liftM Decorate $ parseFieldBool "decorate"+ , liftM DefaultDist $ parseField "defaultdist"+ , liftM Dim $ parseField "dim"+ , liftM Dimen $ parseField "dimen"+ , liftM Dir $ parseField "dir"+ , liftM DirEdgeConstraints $ parseFieldDef EdgeConstraints "diredgeconstraints"+ , liftM Distortion $ parseField "distortion"+ , liftM DPI $ parseFields ["dpi", "resolution"]+ , liftM EdgeURL $ parseFields ["edgeURL", "edgehref"]+ , liftM EdgeTarget $ parseField "edgetarget"+ , liftM EdgeTooltip $ parseField "edgetooltip"+ , liftM Epsilon $ parseField "epsilon"+ , liftM ESep $ parseField "esep"+ , liftM FillColor $ parseField "fillcolor"+ , liftM FixedSize $ parseFieldBool "fixedsize"+ , liftM FontColor $ parseField "fontcolor"+ , liftM FontName $ parseField "fontname"+ , liftM FontNames $ parseField "fontnames"+ , liftM FontPath $ parseField "fontpath"+ , liftM FontSize $ parseField "fontsize"+ , liftM Group $ parseField "group"+ , liftM HeadURL $ parseFields ["headURL", "headhref"]+ , liftM HeadClip $ parseFieldBool "headclip"+ , liftM HeadLabel $ parseField "headlabel"+ , liftM HeadPort $ parseField "headport"+ , liftM HeadTarget $ parseField "headtarget"+ , liftM HeadTooltip $ parseField "headtooltip"+ , liftM Height $ parseField "height"+ , liftM ID $ parseField "id"+ , liftM Image $ parseField "image"+ , liftM ImageScale $ parseFieldDef UniformScale "imagescale"+ , liftM Label $ parseField "label"+ , liftM LabelURL $ parseFields ["labelURL", "labelhref"]+ , liftM LabelAngle $ parseField "labelangle"+ , liftM LabelDistance $ parseField "labeldistance"+ , liftM LabelFloat $ parseFieldBool "labelfloat"+ , liftM LabelFontColor $ parseField "labelfontcolor"+ , liftM LabelFontName $ parseField "labelfontname"+ , liftM LabelFontSize $ parseField "labelfontsize"+ , liftM LabelJust $ parseField "labeljust"+ , liftM LabelLoc $ parseField "labelloc"+ , liftM LabelTarget $ parseField "labeltarget"+ , liftM LabelTooltip $ parseField "labeltooltip"+ , liftM Landscape $ parseFieldBool "landscape"+ , liftM Layer $ parseField "layer"+ , liftM Layers $ parseField "layers"+ , liftM LayerSep $ parseField "layersep"+ , liftM Layout $ parseField "layout"+ , liftM Len $ parseField "len"+ , liftM Levels $ parseField "levels"+ , liftM LevelsGap $ parseField "levelsgap"+ , liftM LHead $ parseField "lhead"+ , liftM LPos $ parseField "lp"+ , liftM LTail $ parseField "ltail"+ , liftM Margin $ parseField "margin"+ , liftM MaxIter $ parseField "maxiter"+ , liftM MCLimit $ parseField "mclimit"+ , liftM MinDist $ parseField "mindist"+ , liftM MinLen $ parseField "minlen"+ , liftM Mode $ parseField "mode"+ , liftM Model $ parseField "model"+ , liftM Mosek $ parseFieldBool "mosek"+ , liftM NodeSep $ parseField "nodesep"+ , liftM NoJustify $ parseFieldBool "nojustify"+ , liftM Normalize $ parseFieldBool "normalize"+ , liftM Nslimit $ parseField "nslimit"+ , liftM Nslimit1 $ parseField "nslimit1"+ , liftM Ordering $ parseField "ordering"+ , liftM Orientation $ parseField "orientation"+ , liftM OrientationGraph $ parseField "orientation"+ , liftM OutputOrder $ parseField "outputorder"+ , liftM Overlap $ parseFieldDef KeepOverlaps "overlap"+ , liftM OverlapScaling $ parseField "overlap_scaling"+ , liftM Pack $ parseFieldDef DoPack "pack"+ , liftM PackMode $ parseField "packmode"+ , liftM Pad $ parseField "pad"+ , liftM Page $ parseField "page"+ , liftM PageDir $ parseField "pagedir"+ , liftM PenColor $ parseField "pencolor"+ , liftM PenWidth $ parseField "penwidth"+ , liftM Peripheries $ parseField "peripheries"+ , liftM Pin $ parseFieldBool "pin"+ , liftM Pos $ parseField "pos"+ , liftM QuadTree $ parseFieldDef NormalQT "quadtree"+ , liftM Quantum $ parseField "quantum"+ , liftM Rank $ parseField "rank"+ , liftM RankDir $ parseField "rankdir"+ , liftM Ranksep $ parseField "ranksep"+ , liftM Ratio $ parseField "ratio"+ , liftM Rects $ parseField "rects"+ , liftM Regular $ parseFieldBool "regular"+ , liftM ReMinCross $ parseFieldBool "remincross"+ , liftM RepulsiveForce $ parseField "repulsiveforce"+ , liftM Root $ parseFieldDef IsCentral "root"+ , liftM Rotate $ parseField "rotate"+ , liftM SameHead $ parseField "samehead"+ , liftM SameTail $ parseField "sametail"+ , liftM SamplePoints $ parseField "samplepoints"+ , liftM SearchSize $ parseField "searchsize"+ , liftM Sep $ parseField "sep"+ , liftM Shape $ parseField "shape"+ , liftM ShapeFile $ parseField "shapefile"+ , liftM ShowBoxes $ parseField "showboxes"+ , liftM Sides $ parseField "sides"+ , liftM Size $ parseField "size"+ , liftM Skew $ parseField "skew"+ , liftM Smoothing $ parseField "smoothing"+ , liftM SortV $ parseField "sortv"+ , liftM Splines $ parseFieldDef SplineEdges "splines"+ , liftM Start $ parseField "start"+ , liftM Style $ parseField "style"+ , liftM StyleSheet $ parseField "stylesheet"+ , liftM TailURL $ parseFields ["tailURL", "tailhref"]+ , liftM TailClip $ parseFieldBool "tailclip"+ , liftM TailLabel $ parseField "taillabel"+ , liftM TailPort $ parseField "tailport"+ , liftM TailTarget $ parseField "tailtarget"+ , liftM TailTooltip $ parseField "tailtooltip"+ , liftM Target $ parseField "target"+ , liftM Tooltip $ parseField "tooltip"+ , liftM TrueColor $ parseFieldBool "truecolor"+ , liftM Vertices $ parseField "vertices"+ , liftM ViewPort $ parseField "viewport"+ , liftM VoroMargin $ parseField "voro_margin"+ , liftM Weight $ parseField "weight"+ , liftM Width $ parseField "width"+ , liftM Z $ parseField "z"+ ]++ parse = parseUnqt++ parseList = parseUnqtList++-- | Determine if this Attribute is valid for use with Graphs.+usedByGraphs :: Attribute -> Bool+usedByGraphs Damping{} = True+usedByGraphs K{} = True+usedByGraphs URL{} = True+usedByGraphs Aspect{} = True+usedByGraphs Bb{} = True+usedByGraphs BgColor{} = True+usedByGraphs Center{} = True+usedByGraphs Charset{} = True+usedByGraphs ClusterRank{} = True+usedByGraphs ColorScheme{} = True+usedByGraphs Comment{} = True+usedByGraphs Compound{} = True+usedByGraphs Concentrate{} = True+usedByGraphs DefaultDist{} = True+usedByGraphs Dim{} = True+usedByGraphs Dimen{} = True+usedByGraphs DirEdgeConstraints{} = True+usedByGraphs DPI{} = True+usedByGraphs Epsilon{} = True+usedByGraphs ESep{} = True+usedByGraphs FontColor{} = True+usedByGraphs FontName{} = True+usedByGraphs FontNames{} = True+usedByGraphs FontPath{} = True+usedByGraphs FontSize{} = True+usedByGraphs ID{} = True+usedByGraphs Label{} = True+usedByGraphs LabelJust{} = True+usedByGraphs LabelLoc{} = True+usedByGraphs Landscape{} = True+usedByGraphs Layers{} = True+usedByGraphs LayerSep{} = True+usedByGraphs Layout{} = True+usedByGraphs Levels{} = True+usedByGraphs LevelsGap{} = True+usedByGraphs LPos{} = True+usedByGraphs Margin{} = True+usedByGraphs MaxIter{} = True+usedByGraphs MCLimit{} = True+usedByGraphs MinDist{} = True+usedByGraphs Mode{} = True+usedByGraphs Model{} = True+usedByGraphs Mosek{} = True+usedByGraphs NodeSep{} = True+usedByGraphs NoJustify{} = True+usedByGraphs Normalize{} = True+usedByGraphs Nslimit{} = True+usedByGraphs Nslimit1{} = True+usedByGraphs Ordering{} = True+usedByGraphs OrientationGraph{} = True+usedByGraphs OutputOrder{} = True+usedByGraphs Overlap{} = True+usedByGraphs OverlapScaling{} = True+usedByGraphs Pack{} = True+usedByGraphs PackMode{} = True+usedByGraphs Pad{} = True+usedByGraphs Page{} = True+usedByGraphs PageDir{} = True+usedByGraphs QuadTree{} = True+usedByGraphs Quantum{} = True+usedByGraphs RankDir{} = True+usedByGraphs Ranksep{} = True+usedByGraphs Ratio{} = True+usedByGraphs ReMinCross{} = True+usedByGraphs RepulsiveForce{} = True+usedByGraphs Root{} = True+usedByGraphs Rotate{} = True+usedByGraphs SearchSize{} = True+usedByGraphs Sep{} = True+usedByGraphs ShowBoxes{} = True+usedByGraphs Size{} = True+usedByGraphs Smoothing{} = True+usedByGraphs SortV{} = True+usedByGraphs Splines{} = True+usedByGraphs Start{} = True+usedByGraphs StyleSheet{} = True+usedByGraphs Target{} = True+usedByGraphs TrueColor{} = True+usedByGraphs ViewPort{} = True+usedByGraphs VoroMargin{} = True+usedByGraphs _ = False++-- | Determine if this Attribute is valid for use with Clusters.+usedByClusters :: Attribute -> Bool+usedByClusters K{} = True+usedByClusters URL{} = True+usedByClusters BgColor{} = True+usedByClusters Color{} = True+usedByClusters ColorScheme{} = True+usedByClusters FillColor{} = True+usedByClusters FontColor{} = True+usedByClusters FontName{} = True+usedByClusters FontSize{} = True+usedByClusters Label{} = True+usedByClusters LabelJust{} = True+usedByClusters LabelLoc{} = True+usedByClusters LPos{} = True+usedByClusters NoJustify{} = True+usedByClusters PenColor{} = True+usedByClusters PenWidth{} = True+usedByClusters Peripheries{} = True+usedByClusters Rank{} = True+usedByClusters SortV{} = True+usedByClusters Style{} = True+usedByClusters Target{} = True+usedByClusters Tooltip{} = True+usedByClusters _ = False++-- | Determine if this Attribute is valid for use with SubGraphs.+usedBySubGraphs :: Attribute -> Bool+usedBySubGraphs Rank{} = True+usedBySubGraphs _ = False++-- | Determine if this Attribute is valid for use with Nodes.+usedByNodes :: Attribute -> Bool+usedByNodes URL{} = True+usedByNodes Color{} = True+usedByNodes ColorScheme{} = True+usedByNodes Comment{} = True+usedByNodes Distortion{} = True+usedByNodes FillColor{} = True+usedByNodes FixedSize{} = True+usedByNodes FontColor{} = True+usedByNodes FontName{} = True+usedByNodes FontSize{} = True+usedByNodes Group{} = True+usedByNodes Height{} = True+usedByNodes ID{} = True+usedByNodes Image{} = True+usedByNodes ImageScale{} = True+usedByNodes Label{} = True+usedByNodes LabelLoc{} = True+usedByNodes Layer{} = True+usedByNodes Margin{} = True+usedByNodes NoJustify{} = True+usedByNodes Orientation{} = True+usedByNodes PenWidth{} = True+usedByNodes Peripheries{} = True+usedByNodes Pin{} = True+usedByNodes Pos{} = True+usedByNodes Rects{} = True+usedByNodes Regular{} = True+usedByNodes Root{} = True+usedByNodes SamplePoints{} = True+usedByNodes Shape{} = True+usedByNodes ShapeFile{} = True+usedByNodes ShowBoxes{} = True+usedByNodes Sides{} = True+usedByNodes Skew{} = True+usedByNodes SortV{} = True+usedByNodes Style{} = True+usedByNodes Target{} = True+usedByNodes Tooltip{} = True+usedByNodes Vertices{} = True+usedByNodes Width{} = True+usedByNodes Z{} = True+usedByNodes _ = False++-- | Determine if this Attribute is valid for use with Edges.+usedByEdges :: Attribute -> Bool+usedByEdges URL{} = True+usedByEdges ArrowHead{} = True+usedByEdges ArrowSize{} = True+usedByEdges ArrowTail{} = True+usedByEdges Color{} = True+usedByEdges ColorScheme{} = True+usedByEdges Comment{} = True+usedByEdges Constraint{} = True+usedByEdges Decorate{} = True+usedByEdges Dir{} = True+usedByEdges EdgeURL{} = True+usedByEdges EdgeTarget{} = True+usedByEdges EdgeTooltip{} = True+usedByEdges FontColor{} = True+usedByEdges FontName{} = True+usedByEdges FontSize{} = True+usedByEdges HeadURL{} = True+usedByEdges HeadClip{} = True+usedByEdges HeadLabel{} = True+usedByEdges HeadPort{} = True+usedByEdges HeadTarget{} = True+usedByEdges HeadTooltip{} = True+usedByEdges ID{} = True+usedByEdges Label{} = True+usedByEdges LabelURL{} = True+usedByEdges LabelAngle{} = True+usedByEdges LabelDistance{} = True+usedByEdges LabelFloat{} = True+usedByEdges LabelFontColor{} = True+usedByEdges LabelFontName{} = True+usedByEdges LabelFontSize{} = True+usedByEdges LabelTarget{} = True+usedByEdges LabelTooltip{} = True+usedByEdges Layer{} = True+usedByEdges Len{} = True+usedByEdges LHead{} = True+usedByEdges LPos{} = True+usedByEdges LTail{} = True+usedByEdges MinLen{} = True+usedByEdges NoJustify{} = True+usedByEdges PenWidth{} = True+usedByEdges Pos{} = True+usedByEdges SameHead{} = True+usedByEdges SameTail{} = True+usedByEdges ShowBoxes{} = True+usedByEdges Style{} = True+usedByEdges TailURL{} = True+usedByEdges TailClip{} = True+usedByEdges TailLabel{} = True+usedByEdges TailPort{} = True+usedByEdges TailTarget{} = True+usedByEdges TailTooltip{} = True+usedByEdges Target{} = True+usedByEdges Tooltip{} = True+usedByEdges Weight{} = True+usedByEdges _ = False++{- Delete to here -}+-- -----------------------------------------------------------------------------++{- |++ Some 'Attribute's (mainly label-like ones) take a 'String' argument+ that allows for extra escape codes. This library doesn't do any+ extra checks or special parsing for these escape codes, but usage+ of 'EscString' rather than 'String' indicates that the GraphViz+ tools will recognise these extra escape codes for these+ 'Attribute's.++ The extra escape codes include (note that these are all 'String's):++ [@\\N@] Replace with the name of the node (for Node 'Attribute's).++ [@\\G@] Replace with the name of the graph (for Node 'Attribute's)+ or the name of the graph or cluster, whichever is+ applicable (for Graph, Cluster and Edge 'Attribute's).++ [@\\E@] Replace with the name of the edge, formed by the two+ adjoining nodes and the edge type (for Edge 'Attribute's).++ [@\\T@] Replace with the name of the tail node (for Edge+ 'Attribute's).++ [@\\H@] Replace with the name of the head node (for Edge+ 'Attribute's).++ [@\\L@] Replace with the object's label (for all 'Attribute's).++ Also, if the 'Attribute' in question is 'Label', 'HeadLabel' or+ 'TailLabel', then @\\n@, @\\l@ and @\\r@ split the label into lines+ centered, left-justified and right-justified respectively.++ -}+type EscString = String++-- -----------------------------------------------------------------------------++newtype URL = UStr { urlString :: EscString }+ deriving (Eq, Show, Read)++instance PrintDot URL where+ unqtDot = wrap (char '<') (char '>')+ -- Explicitly use text here... no quotes!+ . text . urlString++instance ParseDot URL where+ parseUnqt = liftM UStr+ $ bracket (character open)+ (character close)+ (liftM return $ satisfy ((/=) close))+ where+ open = '<'+ close = '>'++ -- No quotes+ parse = parseUnqt++-- -----------------------------------------------------------------------------++-- | /Dot/ has a basic grammar of arrow shapes which allows usage of+-- up to 1,544,761 different shapes from 9 different basic+-- 'ArrowShape's. Note that whilst an explicit list is used in the+-- definition of 'ArrowType', there must be at least one tuple and a+-- maximum of 4 (since that is what is required by Dot). For more+-- information, see: <http://graphviz.org/doc/info/arrows.html>+--+-- The 19 basic arrows shown on the overall attributes page have+-- been defined below as a convenience. Parsing of the 5+-- backward-compatible special cases is also supported.+newtype ArrowType = AType [(ArrowModifier, ArrowShape)]+ deriving (Eq, Show, Read)++box, crow, diamond, dotArrow, inv, noArrow, normal, tee, vee :: ArrowType+oDot, invDot, invODot, oBox, oDiamond :: ArrowType+eDiamond, openArr, halfOpen, emptyArr, invEmpty :: ArrowType++normal = AType [(noMods, Normal)]+inv = AType [(noMods, Inv)]+dotArrow = AType [(noMods, DotArrow)]+invDot = AType [ (noMods, Inv)+ , (noMods, DotArrow)]+oDot = AType [(ArrMod OpenArrow BothSides, DotArrow)]+invODot = AType [ (noMods, Inv)+ , (openMod, DotArrow)]+noArrow = AType [(noMods, NoArrow)]+tee = AType [(noMods, Tee)]+emptyArr = AType [(openMod, Normal)]+invEmpty = AType [ (noMods, Inv)+ , (openMod, Normal)]+diamond = AType [(noMods, Diamond)]+oDiamond = AType [(openMod, Diamond)]+eDiamond = oDiamond+crow = AType [(noMods, Crow)]+box = AType [(noMods, Box)]+oBox = AType [(openMod, Box)]+openArr = vee+halfOpen = AType [(ArrMod FilledArrow LeftSide, Vee)]+vee = AType [(noMods, Vee)]++instance PrintDot ArrowType where+ unqtDot (AType mas) = hcat $ map appMod mas+ where+ appMod (m, a) = unqtDot m <> unqtDot a++instance ParseDot ArrowType where+ parseUnqt = do mas <- many1 $ do m <- parseUnqt+ a <- parseUnqt+ return (m,a)+ return $ AType mas+ `onFail`+ specialArrowParse++specialArrowParse :: Parse ArrowType+specialArrowParse = oneOf [ stringRep eDiamond "ediamond"+ , stringRep openArr "open"+ , stringRep halfOpen "halfopen"+ , stringRep emptyArr "empty"+ , stringRep invEmpty "invempty"+ ]++data ArrowShape = Box+ | Crow+ | Diamond+ | DotArrow+ | Inv+ | NoArrow+ | Normal+ | Tee+ | Vee+ deriving (Eq, Show, Read)++instance PrintDot ArrowShape where+ unqtDot Box = unqtDot "box"+ unqtDot Crow = unqtDot "crow"+ unqtDot Diamond = unqtDot "diamond"+ unqtDot DotArrow = unqtDot "dot"+ unqtDot Inv = unqtDot "inv"+ unqtDot NoArrow = unqtDot "none"+ unqtDot Normal = unqtDot "normal"+ unqtDot Tee = unqtDot "tee"+ unqtDot Vee = unqtDot "vee"++instance ParseDot ArrowShape where+ parseUnqt = oneOf [ stringRep Box "box"+ , stringRep Crow "crow"+ , stringRep Diamond "diamond"+ , stringRep DotArrow "dot"+ , stringRep Inv "inv"+ , stringRep NoArrow "none"+ , stringRep Normal "normal"+ , stringRep Tee "tee"+ , stringRep Vee "vee"+ ]++-- | What modifications to apply to an 'ArrowShape'.+data ArrowModifier = ArrMod { arrowFill :: ArrowFill+ , arrowSide :: ArrowSide+ }+ deriving (Eq, Show, Read)++-- | Apply no modifications to an 'ArrowShape'.+noMods :: ArrowModifier+noMods = ArrMod FilledArrow BothSides++-- | 'OpenArrow' and 'BothSides'+openMod :: ArrowModifier+openMod = ArrMod OpenArrow BothSides++instance PrintDot ArrowModifier where+ unqtDot (ArrMod f s) = unqtDot f <> unqtDot s++instance ParseDot ArrowModifier where+ parseUnqt = do f <- parseUnqt+ s <- parseUnqt+ return $ ArrMod f s++data ArrowFill = OpenArrow+ | FilledArrow+ deriving (Eq, Show, Read)++instance PrintDot ArrowFill where+ unqtDot OpenArrow = char 'o'+ unqtDot FilledArrow = empty++instance ParseDot ArrowFill where+ parseUnqt = liftM (bool OpenArrow FilledArrow . isJust)+ $ optional (character 'o')++ -- Not used individually+ parse = parseUnqt++-- | Represents which side (when looking towards the node the arrow is+-- pointing to) is drawn.+data ArrowSide = LeftSide+ | RightSide+ | BothSides+ deriving (Eq, Show, Read)++instance PrintDot ArrowSide where+ unqtDot LeftSide = char 'l'+ unqtDot RightSide = char 'r'+ unqtDot BothSides = empty++instance ParseDot ArrowSide where+ parseUnqt = liftM getSideType+ $ optional (oneOf $ map character ['l', 'r'])+ where+ getSideType = maybe BothSides+ (bool LeftSide RightSide . (==) 'l')++ -- Not used individually+ parse = parseUnqt++-- -----------------------------------------------------------------------------++data AspectType = RatioOnly Double+ | RatioPassCount Double Int+ deriving (Eq, Show, Read)++instance PrintDot AspectType where+ unqtDot (RatioOnly r) = unqtDot r+ unqtDot (RatioPassCount r p) = commaDel r p++ toDot at@RatioOnly{} = unqtDot at+ toDot at@RatioPassCount{} = doubleQuotes $ unqtDot at++instance ParseDot AspectType where+ parseUnqt = liftM (uncurry RatioPassCount) commaSep+ `onFail`+ liftM RatioOnly parse+++ parse = quotedParse (liftM (uncurry RatioPassCount) commaSep)+ `onFail`+ optionalQuoted (liftM RatioOnly parse)++-- -----------------------------------------------------------------------------++data Rect = Rect Point Point+ deriving (Eq, Show, Read)++instance PrintDot Rect where+ unqtDot (Rect p1 p2) = commaDel p1 p2++ toDot = doubleQuotes . unqtDot++instance ParseDot Rect where+ parseUnqt = liftM (uncurry Rect)+ $ commaSep' parseUnqt parseUnqt++ parse = liftM (uncurry Rect) . quotedParse+ $ commaSep' parseUnqt parseUnqt++-- -----------------------------------------------------------------------------++data Color = RGB { red :: Word8+ , green :: Word8+ , blue :: Word8+ }+ | RGBA { red :: Word8+ , green :: Word8+ , blue :: Word8+ , alpha :: Word8+ }+ | HSV { hue :: Double+ , saturation :: Double+ , value :: Double+ }+ | ColorName String+ deriving (Eq, Show, Read)++instance PrintDot Color where+ unqtDot (RGB r g b) = hexColor [r,g,b]+ unqtDot (RGBA r g b a) = hexColor [r,g,b,a]+ unqtDot (HSV h s v) = hcat . punctuate comma $ map unqtDot [h,s,v]+ unqtDot (ColorName name) = unqtDot name++ toDot (ColorName name) = toDot name+ toDot c = doubleQuotes $ unqtDot c++ unqtListToDot = hcat . punctuate colon . map unqtDot++ listToDot [ColorName nm] = toDot nm+ listToDot cs = doubleQuotes $ unqtListToDot cs++hexColor :: [Word8] -> DotCode+hexColor = (<>) (char '#') . hcat . map word8Doc++word8Doc :: Word8 -> DotCode+word8Doc w = text $ padding ++ simple+ where+ simple = showHex w ""+ padding = replicate count '0'+ count = 2 - findCols 1 w+ findCols :: Int -> Word8 -> Int+ findCols c n+ | n < 16 = c+ | otherwise = findCols (c+1) (n `div` 16)++instance ParseDot Color where+ parseUnqt = oneOf [ parseHexBased+ , parseHSV+ , liftM ColorName parse -- Should we check it+ -- is a colour?+ ]+ where+ parseHexBased+ = do character '#'+ cs <- many1 parse2Hex+ return $ case cs of+ [r,g,b] -> RGB r g b+ [r,g,b,a] -> RGBA r g b a+ _ -> error $ "Not a valid hex Color specification: "+ ++ show cs+ parseHSV = do h <- parse+ parseSep+ s <- parse+ parseSep+ v <- parse+ return $ HSV h s v+ parseSep = oneOf [ string ","+ , whitespace+ ]+ parse2Hex = do c1 <- satisfy isHexDigit+ c2 <- satisfy isHexDigit+ let [(n, [])] = readHex [c1, c2]+ return n+++ parse = liftM ColorName stringBlock -- unquoted Color Name+ `onFail`+ quotedParse parseUnqt++ parseUnqtList = sepBy1 parseUnqt (character ':')++ parseList = liftM (return . ColorName) stringBlock -- unquoted single+ -- ColorName+ `onFail`+ quotedParse parseUnqtList++-- -----------------------------------------------------------------------------++data ClusterMode = Local+ | Global+ | NoCluster+ deriving (Eq, Show, Read)++instance PrintDot ClusterMode where+ unqtDot Local = unqtDot "local"+ unqtDot Global = unqtDot "global"+ unqtDot NoCluster = unqtDot "none"++++instance ParseDot ClusterMode where+ parseUnqt = oneOf [ stringRep Local "local"+ , stringRep Global "global"+ , stringRep NoCluster "none"+ ]++-- -----------------------------------------------------------------------------++data DirType = Forward | Back | Both | NoDir+ deriving (Eq, Show, Read)++instance PrintDot DirType where+ unqtDot Forward = unqtDot "forward"+ unqtDot Back = unqtDot "back"+ unqtDot Both = unqtDot "both"+ unqtDot NoDir = unqtDot "none"++instance ParseDot DirType where+ parseUnqt = oneOf [ stringRep Forward "forward"+ , stringRep Back "back"+ , stringRep Both "both"+ , stringRep NoDir "none"+ ]++-- -----------------------------------------------------------------------------++-- | Only when @mode == 'IpSep'@.+data DEConstraints = EdgeConstraints+ | NoConstraints+ | HierConstraints+ deriving (Eq, Show, Read)++instance PrintDot DEConstraints where+ unqtDot EdgeConstraints = unqtDot True+ unqtDot NoConstraints = unqtDot False+ unqtDot HierConstraints = text "hier"++instance ParseDot DEConstraints where+ parseUnqt = liftM (bool EdgeConstraints NoConstraints) parse+ `onFail`+ stringRep HierConstraints "hier"++-- -----------------------------------------------------------------------------++-- | Either a 'Double' or a 'Point'.+data DPoint = DVal Double+ | PVal Point+ deriving (Eq, Show, Read)++instance PrintDot DPoint where+ unqtDot (DVal d) = unqtDot d+ unqtDot (PVal p) = unqtDot p++ toDot (DVal d) = toDot d+ toDot (PVal p) = toDot p++instance ParseDot DPoint where+ parseUnqt = liftM DVal parseUnqt+ `onFail`+ liftM PVal parseUnqt++ parse = liftM DVal parse+ `onFail`+ liftM PVal parse++-- -----------------------------------------------------------------------------++data ModeType = Major+ | KK+ | Hier+ | IpSep+ deriving (Eq, Show, Read)++instance PrintDot ModeType where+ unqtDot Major = text "major"+ unqtDot KK = text "KK"+ unqtDot Hier = text "hier"+ unqtDot IpSep = text "ipsep"++instance ParseDot ModeType where+ parseUnqt = oneOf [ stringRep Major "major"+ , stringRep KK "KK"+ , stringRep Hier "hier"+ , stringRep IpSep "ipsep"+ ]++-- -----------------------------------------------------------------------------++data Model = ShortPath+ | SubSet+ | Circuit+ deriving (Eq, Show, Read)++instance PrintDot Model where+ unqtDot ShortPath = text "shortpath"+ unqtDot SubSet = text "subset"+ unqtDot Circuit = text "circuit"++instance ParseDot Model where+ parseUnqt = oneOf [ stringRep ShortPath "shortpath"+ , stringRep SubSet "subset"+ , stringRep Circuit "circuit"+ ]++-- -----------------------------------------------------------------------------++data Label = StrLabel EscString+ | URLLabel URL+ deriving (Eq, Show, Read)++instance PrintDot Label where+ unqtDot (StrLabel s) = unqtDot s+ unqtDot (URLLabel u) = unqtDot u++ toDot (StrLabel s) = toDot s+ toDot (URLLabel u) = toDot u++instance ParseDot Label where+ parseUnqt = liftM StrLabel parseUnqt+ `onFail`+ liftM URLLabel parseUnqt++ parse = liftM StrLabel parse+ `onFail`+ liftM URLLabel parse++-- -----------------------------------------------------------------------------++data Point = Point Int Int+ | PointD Double Double+ deriving (Eq, Show, Read)++instance PrintDot Point where+ unqtDot (Point x y) = commaDel x y+ unqtDot (PointD x y) = commaDel x y++ toDot = doubleQuotes . unqtDot++ unqtListToDot = hsep . map unqtDot++ listToDot = doubleQuotes . unqtListToDot++instance ParseDot Point where+ parseUnqt = liftM (uncurry Point) commaSep+ `onFail`+ liftM (uncurry PointD) commaSep++ parse = quotedParse parseUnqt++ parseUnqtList = sepBy1 parseUnqt whitespace++-- -----------------------------------------------------------------------------++data Overlap = KeepOverlaps+ | RemoveOverlaps+ | ScaleOverlaps+ | ScaleXYOverlaps+ | PrismOverlap (Maybe Int) -- ^ Only when sfdp is available, 'Int' is non-negative+ | CompressOverlap+ | VpscOverlap+ | IpsepOverlap -- ^ Only when @mode == 'IpSep'@+ deriving (Eq, Show, Read)++instance PrintDot Overlap where+ unqtDot KeepOverlaps = unqtDot True+ unqtDot RemoveOverlaps = unqtDot False+ unqtDot ScaleOverlaps = text "scale"+ unqtDot ScaleXYOverlaps = text "scalexy"+ unqtDot (PrismOverlap i) = maybe id (flip (<>) . unqtDot) i $ text "prism"+ unqtDot CompressOverlap = text "compress"+ unqtDot VpscOverlap = text "vpsc"+ unqtDot IpsepOverlap = text "ipsep"++instance ParseDot Overlap where+ parseUnqt = oneOf [ stringRep KeepOverlaps "true"+ , stringRep RemoveOverlaps "false"+ , stringRep ScaleOverlaps "scale"+ , stringRep ScaleXYOverlaps "scalexy"+ , string "prism" >> liftM PrismOverlap (optional parse)+ , stringRep CompressOverlap "compress"+ , stringRep VpscOverlap "vpsc"+ , stringRep IpsepOverlap "ipsep"+ ]++-- -----------------------------------------------------------------------------++data LayerRange = LRID LayerID+ | LRS LayerID String LayerID+ deriving (Eq, Show, Read)++instance PrintDot LayerRange where+ unqtDot (LRID lid) = unqtDot lid+ unqtDot (LRS id1 s id2) = unqtDot id1 <> unqtDot s <> unqtDot id2++ toDot (LRID lid) = toDot lid+ toDot lrs = doubleQuotes $ unqtDot lrs++instance ParseDot LayerRange where+ parseUnqt = liftM LRID parseUnqt+ `onFail`+ do id1 <- parseUnqt+ s <- parseLayerSep+ id2 <- parseUnqt+ return $ LRS id1 s id2++ parse = liftM LRID parse+ `onFail`+ quotedParse ( do id1 <- parseUnqt+ s <- parseLayerSep+ id2 <- parseUnqt+ return $ LRS id1 s id2+ )+++parseLayerSep :: Parse String+parseLayerSep = many1 . oneOf+ $ map character defLayerSep++defLayerSep :: [Char]+defLayerSep = [' ', ':', '\t']++parseLayerName :: Parse String+parseLayerName = many1 $ satisfy (flip notElem defLayerSep)++data LayerID = AllLayers+ | LRInt Int+ | LRName String+ deriving (Eq, Show, Read)++instance PrintDot LayerID where+ unqtDot AllLayers = text "all"+ unqtDot (LRInt n) = unqtDot n+ unqtDot (LRName nm) = unqtDot nm++ toDot (LRName nm) = toDot nm+ -- Other two don't need quotes+ toDot li = unqtDot li++instance ParseDot LayerID where+ parseUnqt = oneOf [ stringRep AllLayers "all"+ , liftM LRInt parseUnqt+ , liftM LRName parseLayerName+ ]++ parse = oneOf [ optionalQuoted $ stringRep AllLayers "all"+ , liftM LRInt parse -- Has optionalQuoted in it+ , quotedParse $ liftM LRName parseLayerName+ ]++-- | The list represent (Separator, Name)+data LayerList = LL String [(String, String)]+ deriving (Eq, Show, Read)++instance PrintDot LayerList where+ unqtDot (LL l1 ols) = unqtDot l1 <> hsep (map subLL ols)+ where+ subLL (s, l) = unqtDot s <> unqtDot l++ -- Might not need quotes, but probably will.+ toDot = doubleQuotes . unqtDot++instance ParseDot LayerList where+ parseUnqt = do l1 <- parseLayerName+ ols <- many $ do s <- parseLayerSep+ lnm <- parseLayerName+ return (s, lnm)+ return $ LL l1 ols++ parse = quotedParse parseUnqt++-- -----------------------------------------------------------------------------++data OutputMode = BreadthFirst | NodesFirst | EdgesFirst+ deriving (Eq, Show, Read)++instance PrintDot OutputMode where+ unqtDot BreadthFirst = text "breadthfirst"+ unqtDot NodesFirst = text "nodesfirst"+ unqtDot EdgesFirst = text "edgesfirst"++instance ParseDot OutputMode where+ parseUnqt = oneOf [ stringRep BreadthFirst "breadthfirst"+ , stringRep NodesFirst "nodesfirst"+ , stringRep EdgesFirst "edgesfirst"+ ]++-- -----------------------------------------------------------------------------++data Pack = DoPack+ | DontPack+ | PackMargin Int -- ^ If non-negative, then packs; otherwise doesn't.+ deriving (Eq, Show, Read)++instance PrintDot Pack where+ unqtDot DoPack = unqtDot True+ unqtDot DontPack = unqtDot False+ unqtDot (PackMargin m) = unqtDot m++instance ParseDot Pack where+ -- What happens if it parses 0? It's non-negative, but parses as False+ parseUnqt = oneOf [ liftM (bool DoPack DontPack) parseUnqt+ , liftM PackMargin parseUnqt+ ]++-- -----------------------------------------------------------------------------++data PackMode = PackNode+ | PackClust+ | PackGraph+ | PackArray Bool Bool (Maybe Int) -- ^ Sort by cols, sort+ -- by user, number of+ -- rows/cols+ deriving (Eq, Show, Read)++instance PrintDot PackMode where+ unqtDot PackNode = text "node"+ unqtDot PackClust = text "clust"+ unqtDot PackGraph = text "graph"+ unqtDot (PackArray c u mi) = addNum . isU . isC . isUnder+ $ text "array"+ where+ addNum = maybe id (flip (<>) . unqtDot) mi+ isUnder = if c || u+ then flip (<>) $ char '_'+ else id+ isC = if c+ then flip (<>) $ char 'c'+ else id+ isU = if u+ then flip (<>) $ char 'u'+ else id++instance ParseDot PackMode where+ parseUnqt = oneOf [ stringRep PackNode "node"+ , stringRep PackClust "clust"+ , stringRep PackGraph "graph"+ , do string "array"+ mcu <- optional $ do character '_'+ many1 $ satisfy (not . isDigit)+ let c = hasCharacter mcu 'c'+ u = hasCharacter mcu 'u'+ mi <- optional parseUnqt+ return $ PackArray c u mi+ ]+ where+ hasCharacter ms c = maybe False (elem c) ms++-- -----------------------------------------------------------------------------++data Pos = PointPos Point+ | SplinePos [Spline]+ deriving (Eq, Show, Read)++instance PrintDot Pos where+ unqtDot (PointPos p) = unqtDot p+ unqtDot (SplinePos ss) = unqtDot ss++ toDot (PointPos p) = toDot p+ toDot (SplinePos ss) = toDot ss++instance ParseDot Pos where+ parseUnqt = oneOf [ liftM PointPos parseUnqt+ , liftM SplinePos parseUnqt+ ]++ parse = quotedParse parseUnqt++-- -----------------------------------------------------------------------------++-- | Controls how (and if) edges are represented.+data EdgeType = SplineEdges+ | LineEdges+ | NoEdges+ | PolyLine+ | CompoundEdge -- ^ fdp only+ deriving (Eq, Show, Read)++instance PrintDot EdgeType where+ unqtDot SplineEdges = toDot True+ unqtDot LineEdges = toDot False+ unqtDot NoEdges = empty+ unqtDot PolyLine = text "polyline"+ unqtDot CompoundEdge = text "compound"++ toDot NoEdges = doubleQuotes empty+ toDot et = unqtDot et++instance ParseDot EdgeType where+ -- Can't parse NoEdges without quotes.+ parseUnqt = oneOf [ liftM (bool SplineEdges LineEdges) parse+ , stringRep SplineEdges "spline"+ , stringRep LineEdges "line"+ , stringRep PolyLine "polyline"+ , stringRep CompoundEdge "compound"+ ]++ parse = stringRep NoEdges "\"\""+ `onFail`+ optionalQuoted parseUnqt++-- -----------------------------------------------------------------------------++-- | Upper-case first character is major order;+-- lower-case second character is minor order.+data PageDir = Bl | Br | Tl | Tr | Rb | Rt | Lb | Lt+ deriving (Eq, Show, Read)++instance PrintDot PageDir where+ unqtDot Bl = text "BL"+ unqtDot Br = text "BR"+ unqtDot Tl = text "TL"+ unqtDot Tr = text "TR"+ unqtDot Rb = text "RB"+ unqtDot Rt = text "RT"+ unqtDot Lb = text "LB"+ unqtDot Lt = text "LT"++instance ParseDot PageDir where+ parseUnqt = oneOf [ stringRep Bl "BL"+ , stringRep Br "BR"+ , stringRep Tl "TL"+ , stringRep Tr "TR"+ , stringRep Rb "RB"+ , stringRep Rt "RT"+ , stringRep Lb "LB"+ , stringRep Lt "LT"+ ]++-- -----------------------------------------------------------------------------++-- | The number of points in the list must be equivalent to 1 mod 3;+-- note that this is not checked.+data Spline = Spline (Maybe Point) (Maybe Point) [Point]+ deriving (Eq, Show, Read)++instance PrintDot Spline where+ unqtDot (Spline ms me ps) = addS . addE+ . hsep+ $ map unqtDot ps+ where+ addP t = maybe id ((<>) . commaDel t)+ addS = addP 's' ms+ addE = addP 'e' me++ toDot = doubleQuotes . unqtDot++ unqtListToDot = hsep . punctuate semi . map unqtDot++ listToDot = doubleQuotes . unqtListToDot++instance ParseDot Spline where+ parseUnqt = do ms <- parseP 's'+ whitespace+ me <- parseP 'e'+ whitespace+ ps <- sepBy1 parseUnqt whitespace+ return $ Spline ms me ps+ where+ parseP t = optional $ do character t+ character ';'+ parse++ parse = quotedParse parseUnqt++ parseUnqtList = sepBy1 parseUnqt (character ';')++-- -----------------------------------------------------------------------------++data QuadType = NormalQT+ | FastQT+ | NoQT+ deriving (Eq, Show, Read)++instance PrintDot QuadType where+ unqtDot NormalQT = text "normal"+ unqtDot FastQT = text "fast"+ unqtDot NoQT = text "none"++instance ParseDot QuadType where+ -- Have to take into account the slightly different interpretation+ -- of Bool used as an option for parsing QuadType+ parseUnqt = oneOf [ stringRep NormalQT "normal"+ , stringRep FastQT "fast"+ , stringRep NoQT "none"+ , character '2' >> return FastQT -- weird bool+ , liftM (bool NormalQT NoQT) parse+ ]++-- -----------------------------------------------------------------------------++-- | Specify the root node either as a Node attribute or a Graph attribute.+data Root = IsCentral -- ^ For Nodes only+ | NotCentral -- ^ For Nodes only+ | NodeName String -- ^ For Graphs only+ deriving (Eq, Show, Read)++instance PrintDot Root where+ unqtDot IsCentral = unqtDot True+ unqtDot NotCentral = unqtDot False+ unqtDot (NodeName n) = unqtDot n++ toDot (NodeName n) = toDot n+ toDot r = unqtDot r++instance ParseDot Root where+ parseUnqt = liftM (bool IsCentral NotCentral) parse+ `onFail`+ liftM NodeName parse++-- -----------------------------------------------------------------------------++data RankType = SameRank+ | MinRank+ | SourceRank+ | MaxRank+ | SinkRank+ deriving (Eq, Show, Read)++instance PrintDot RankType where+ unqtDot SameRank = text "same"+ unqtDot MinRank = text "min"+ unqtDot SourceRank = text "source"+ unqtDot MaxRank = text "max"+ unqtDot SinkRank = text "sink"++instance ParseDot RankType where+ parseUnqt = oneOf [ stringRep SameRank "same"+ , stringRep MinRank "min"+ , stringRep SourceRank "source"+ , stringRep MaxRank "max"+ , stringRep SinkRank "sink"+ ]++-- -----------------------------------------------------------------------------++data RankDir = FromTop+ | FromLeft+ | FromBottom+ | FromRight+ deriving (Eq, Show, Read)++instance PrintDot RankDir where+ unqtDot FromTop = text "TB"+ unqtDot FromLeft = text "LR"+ unqtDot FromBottom = text "BT"+ unqtDot FromRight = text "RL"++instance ParseDot RankDir where+ parseUnqt = oneOf [ stringRep FromTop "TB"+ , stringRep FromLeft "LR"+ , stringRep FromBottom "BT"+ , stringRep FromRight "RL"+ ]++-- -----------------------------------------------------------------------------++data Shape+ = BoxShape+ | Polygon+ | Ellipse+ | Circle+ | PointShape+ | Egg+ | Triangle+ | Plaintext+ | DiamondShape+ | Trapezium+ | Parallelogram+ | House+ | Pentagon+ | Hexagon+ | Septagon+ | Octagon+ | Doublecircle+ | Doubleoctagon+ | Tripleoctagon+ | Invtriangle+ | Invtrapezium+ | Invhouse+ | Mdiamond+ | Msquare+ | Mcircle+ | Rectangle+ | NoShape+ | Note+ | Tab+ | Folder+ | Box3d+ | Component+ deriving (Eq, Show, Read)++instance PrintDot Shape where+ unqtDot BoxShape = text "box"+ unqtDot Polygon = text "polygon"+ unqtDot Ellipse = text "ellipse"+ unqtDot Circle = text "circle"+ unqtDot PointShape = text "point"+ unqtDot Egg = text "egg"+ unqtDot Triangle = text "triangle"+ unqtDot Plaintext = text "plaintext"+ unqtDot DiamondShape = text "diamond"+ unqtDot Trapezium = text "trapezium"+ unqtDot Parallelogram = text "parallelogram"+ unqtDot House = text "house"+ unqtDot Pentagon = text "pentagon"+ unqtDot Hexagon = text "hexagon"+ unqtDot Septagon = text "septagon"+ unqtDot Octagon = text "octagon"+ unqtDot Doublecircle = text "doublecircle"+ unqtDot Doubleoctagon = text "doubleoctagon"+ unqtDot Tripleoctagon = text "tripleoctagon"+ unqtDot Invtriangle = text "invtriangle"+ unqtDot Invtrapezium = text "invtrapezium"+ unqtDot Invhouse = text "invhouse"+ unqtDot Mdiamond = text "Mdiamond"+ unqtDot Msquare = text "Msquare"+ unqtDot Mcircle = text "Mcircle"+ unqtDot Rectangle = text "rectangle"+ unqtDot NoShape = text "none"+ unqtDot Note = text "note"+ unqtDot Tab = text "tab"+ unqtDot Folder = text "folder"+ unqtDot Box3d = text "box3d"+ unqtDot Component = text "component"++instance ParseDot Shape where+ parseUnqt = oneOf [ stringRep BoxShape "box"+ , stringRep Polygon "polygon"+ , stringRep Ellipse "ellipse"+ , stringRep Circle "circle"+ , stringRep PointShape "point"+ , stringRep Egg "egg"+ , stringRep Triangle "triangle"+ , stringRep Plaintext "plaintext"+ , stringRep DiamondShape "diamond"+ , stringRep Trapezium "trapezium"+ , stringRep Parallelogram "parallelogram"+ , stringRep House "house"+ , stringRep Pentagon "pentagon"+ , stringRep Hexagon "hexagon"+ , stringRep Septagon "septagon"+ , stringRep Octagon "octagon"+ , stringRep Doublecircle "doublecircle"+ , stringRep Doubleoctagon "doubleoctagon"+ , stringRep Tripleoctagon "tripleoctagon"+ , stringRep Invtriangle "invtriangle"+ , stringRep Invtrapezium "invtrapezium"+ , stringRep Invhouse "invhouse"+ , stringRep Mdiamond "Mdiamond"+ , stringRep Msquare "Msquare"+ , stringRep Mcircle "Mcircle"+ , stringRep Rectangle "rectangle"+ , stringRep NoShape "none"+ , stringRep Note "note"+ , stringRep Tab "tab"+ , stringRep Folder "folder"+ , stringRep Box3d "box3d"+ , stringRep Component "component"+ ]++-- -----------------------------------------------------------------------------++data SmoothType = NoSmooth+ | AvgDist+ | GraphDist+ | PowerDist+ | RNG+ | Spring+ | TriangleSmooth+ deriving (Eq, Show, Read)++instance PrintDot SmoothType where+ unqtDot NoSmooth = text "none"+ unqtDot AvgDist = text "avg_dist"+ unqtDot GraphDist = text "graph_dist"+ unqtDot PowerDist = text "power_dist"+ unqtDot RNG = text "rng"+ unqtDot Spring = text "spring"+ unqtDot TriangleSmooth = text "triangle"++instance ParseDot SmoothType where+ parseUnqt = oneOf [ stringRep NoSmooth "none"+ , stringRep AvgDist "avg_dist"+ , stringRep GraphDist "graph_dist"+ , stringRep PowerDist "power_dist"+ , stringRep RNG "rng"+ , stringRep Spring "spring"+ , stringRep TriangleSmooth "triangle"+ ]++-- -----------------------------------------------------------------------------++-- | It is assumed that at least one of these is @Just{}@.+data StartType = StartStyle STStyle+ | StartSeed Int+ | StartStyleSeed STStyle Int+ deriving (Eq, Show, Read)++instance PrintDot StartType where+ unqtDot (StartStyle ss) = unqtDot ss+ unqtDot (StartSeed s) = unqtDot s+ unqtDot (StartStyleSeed ss s) = unqtDot ss <> unqtDot s++instance ParseDot StartType where+ parseUnqt = oneOf [ do ss <- parseUnqt+ s <- parseUnqt+ return $ StartStyleSeed ss s+ , liftM StartStyle parseUnqt+ , liftM StartSeed parseUnqt+ ]++data STStyle = RegularStyle+ | SelfStyle+ | RandomStyle+ deriving (Eq, Show, Read)++instance PrintDot STStyle where+ unqtDot RegularStyle = text "regular"+ unqtDot SelfStyle = text "self"+ unqtDot RandomStyle = text "random"++instance ParseDot STStyle where+ parseUnqt = oneOf [ stringRep RegularStyle "regular"+ , stringRep SelfStyle "self"+ , stringRep RandomStyle "random"+ ]++-- -----------------------------------------------------------------------------++data StyleItem = SItem StyleName [String]+ deriving (Eq, Show, Read)++instance PrintDot StyleItem where+ unqtDot (SItem nm args)+ | null args = dnm+ | otherwise = dnm <> parens args'+ where+ dnm = unqtDot nm+ args' = hcat . punctuate comma $ map unqtDot args++ toDot si@(SItem nm args)+ | null args = toDot nm+ | otherwise = doubleQuotes $ unqtDot si++ unqtListToDot = hcat . punctuate comma . map unqtDot++ listToDot [SItem nm []] = toDot nm+ listToDot sis = doubleQuotes $ unqtListToDot sis++instance ParseDot StyleItem where+ parseUnqt = do nm <- parseUnqt+ args <- tryParseList' parseArgs+ return $ SItem nm args+ where+ parseArgs = bracketSep (character '(')+ parseComma+ (character ')')+ parseStyleName++ -- Ignore quotations for the DD case atm, since I'm not sure how+ -- to deal with it.++ parseUnqtList = sepBy1 parseUnqt parseComma++ -- Might not necessarily need to be quoted if a singleton...+ parseList = liftM return parse+ `onFail`+ parseUnqtList++data StyleName = Dashed -- ^ Nodes and Edges+ | Dotted -- ^ Nodes and Edges+ | Solid -- ^ Nodes and Edges+ | Bold -- ^ Nodes and Edges+ | Invisible -- ^ Nodes and Edges+ | Filled -- ^ Nodes and Clusters+ | Diagonals -- ^ Nodes only+ | Rounded -- ^ Nodes and Clusters+ | DD String -- ^ Device Dependent+ deriving (Eq, Show, Read)++instance PrintDot StyleName where+ unqtDot Filled = text "filled"+ unqtDot Invisible = text "invis"+ unqtDot Diagonals = text "diagonals"+ unqtDot Rounded = text "rounded"+ unqtDot Dashed = text "dashed"+ unqtDot Dotted = text "dotted"+ unqtDot Solid = text "solid"+ unqtDot Bold = text "bold"+ unqtDot (DD nm) = unqtDot nm++ toDot (DD nm) = toDot nm+ toDot sn = unqtDot sn++instance ParseDot StyleName where+ parseUnqt = oneOf [ stringRep Filled "filled"+ , stringRep Invisible "invis"+ , stringRep Diagonals "diagonals"+ , stringRep Rounded "rounded"+ , stringRep Dashed "dashed"+ , stringRep Dotted "dotted"+ , stringRep Solid "solid"+ , stringRep Bold "bold"+ , liftM DD parseStyleName+ ]++ -- Never used on its own, so not bothering with a separate parse+ -- implementation (which is iffy due to the DD case).++parseStyleName :: Parse String+parseStyleName = do f <- noneOf ['(', ')', ',', ' ']+ r <- many (noneOf ['(', ')', ','])+ return $ f:r++-- -----------------------------------------------------------------------------++newtype PortPos = PP CompassPoint+ deriving (Eq, Show, Read)++instance PrintDot PortPos where+ unqtDot (PP cp) = unqtDot cp++ toDot (PP cp) = toDot cp++instance ParseDot PortPos where+ parseUnqt = liftM PP parseUnqt++data CompassPoint = North+ | NorthEast+ | East+ | SouthEast+ | South+ | SouthWest+ | West+ | NorthWest+ | CenterPoint+ | NoCP+ deriving (Eq, Show, Read)++instance PrintDot CompassPoint where+ unqtDot NorthEast = text "ne"+ unqtDot NorthWest = text "nw"+ unqtDot North = text "n"+ unqtDot East = text "e"+ unqtDot SouthEast = text "se"+ unqtDot SouthWest = text "sw"+ unqtDot South = text "s"+ unqtDot West = text "w"+ unqtDot CenterPoint = text "c"+ unqtDot NoCP = text "_"++instance ParseDot CompassPoint where+ parseUnqt = oneOf [ stringRep North "n"+ , stringRep NorthEast "ne"+ , stringRep East "e"+ , stringRep SouthEast "se"+ , stringRep South "s"+ , stringRep SouthWest "sw"+ , stringRep West "w"+ , stringRep NorthWest "nw"+ , stringRep CenterPoint "c"+ , stringRep NoCP "_"+ ]++-- -----------------------------------------------------------------------------++data ViewPort = VP { wVal :: Double+ , hVal :: Double+ , zVal :: Double+ , focus :: Maybe FocusType+ }+ deriving (Eq, Show, Read)++instance PrintDot ViewPort where+ unqtDot vp = maybe vs ((<>) (vs <> comma) . unqtDot)+ $ focus vp+ where+ vs = hcat . punctuate comma+ $ map (unqtDot . flip ($) vp) [wVal, hVal, zVal]++ toDot = doubleQuotes . unqtDot++instance ParseDot ViewPort where+ parseUnqt = do wv <- parseUnqt+ parseComma+ hv <- parseUnqt+ parseComma+ zv <- parseUnqt+ mf <- optional $ parseComma >> parseUnqt+ return $ VP wv hv zv mf++ parse = quotedParse parseUnqt++data FocusType = XY Point+ | NodeFocus String+ deriving (Eq, Show, Read)++instance PrintDot FocusType where+ unqtDot (XY p) = unqtDot p+ unqtDot (NodeFocus nm) = unqtDot nm++ toDot (XY p) = toDot p+ toDot (NodeFocus nm) = toDot nm++instance ParseDot FocusType where+ parseUnqt = liftM XY parseUnqt+ `onFail`+ liftM NodeFocus stringBlock++-- -----------------------------------------------------------------------------++data VerticalPlacement = VTop+ | VCenter -- ^ Only valid for Nodes.+ | VBottom+ deriving (Eq, Show, Read)++instance PrintDot VerticalPlacement where+ unqtDot VTop = char 't'+ unqtDot VCenter = char 'c'+ unqtDot VBottom = char 'b'++instance ParseDot VerticalPlacement where+ parseUnqt = oneOf [ stringRep VTop "t"+ , stringRep VCenter "c"+ , stringRep VBottom "b"+ ]++-- -----------------------------------------------------------------------------++data ScaleType = UniformScale+ | NoScale+ | FillWidth+ | FillHeight+ | FillBoth+ deriving (Eq, Show, Read)++instance PrintDot ScaleType where+ unqtDot UniformScale = unqtDot True+ unqtDot NoScale = unqtDot False+ unqtDot FillWidth = text "width"+ unqtDot FillHeight = text "height"+ unqtDot FillBoth = text "both"++instance ParseDot ScaleType where+ parseUnqt = oneOf [ stringRep UniformScale "true"+ , stringRep NoScale "false"+ , stringRep FillWidth "width"+ , stringRep FillHeight "height"+ , stringRep FillBoth "both"+ ]++-- -----------------------------------------------------------------------------++data Justification = JLeft+ | JRight+ | JCenter+ deriving (Eq, Show, Read)++instance PrintDot Justification where+ unqtDot JLeft = char 'l'+ unqtDot JRight = char 'r'+ unqtDot JCenter = char 'c'++instance ParseDot Justification where+ parseUnqt = oneOf [ stringRep JLeft "l"+ , stringRep JRight "r"+ , stringRep JCenter "c"+ ]++-- -----------------------------------------------------------------------------++data Ratios = AspectRatio Double+ | FillRatio+ | CompressRatio+ | ExpandRatio+ | AutoRatio+ deriving (Eq, Show, Read)++instance PrintDot Ratios where+ unqtDot (AspectRatio r) = unqtDot r+ unqtDot FillRatio = text "fill"+ unqtDot CompressRatio = text "compress"+ unqtDot ExpandRatio = text "expand"+ unqtDot AutoRatio = text "auto"++instance ParseDot Ratios where+ parseUnqt = oneOf [ liftM AspectRatio parseUnqt+ , stringRep FillRatio "fill"+ , stringRep CompressRatio "compress"+ , stringRep ExpandRatio "expand"+ , stringRep AutoRatio "auto"+ ]
Data/GraphViz/Commands.hs view
@@ -33,6 +33,7 @@ import Control.Exception.Extensible import Data.GraphViz.Types+import Data.GraphViz.Types.Printing -- | The available Graphviz commands. data GraphvizCommand = Dot | Neato | TwoPi | Circo | Fdp@@ -53,7 +54,7 @@ undirCommand = Neato -- | The appropriate (default) GraphViz command for the given graph.-commandFor :: DotGraph -> GraphvizCommand+commandFor :: DotGraph a -> GraphvizCommand commandFor dg = if (directedGraph dg) then dirCommand else undirCommand@@ -149,14 +150,15 @@ -- | Run the recommended Graphviz command on this graph, saving the result -- to the file provided (note: file extensions are /not/ checked). -- Returns @True@ if successful, @False@ otherwise.-runGraphviz :: DotGraph -> GraphvizOutput -> FilePath -> IO Bool+runGraphviz :: (PrintDot n) => DotGraph n -> GraphvizOutput -> FilePath+ -> IO Bool runGraphviz gr t fp = runGraphvizCommand (commandFor gr) gr t fp -- | Run the chosen Graphviz command on this graph, saving the result -- to the file provided (note: file extensions are /not/ checked). -- Returns @True@ if successful, @False@ otherwise.-runGraphvizCommand :: GraphvizCommand -> DotGraph -> GraphvizOutput- -> FilePath -> IO Bool+runGraphvizCommand :: (PrintDot n) => GraphvizCommand -> DotGraph n+ -> GraphvizOutput -> FilePath -> IO Bool runGraphvizCommand cmd gr t fp = do pipe <- tryJust (\(SomeException _) -> return ()) $ openFile fp WriteMode@@ -174,11 +176,11 @@ -- | Run the chosen Graphviz command on this graph, but send the result to the -- given handle rather than to a file. -- The result is wrapped in 'Maybe' rather than throwing an error.-graphvizWithHandle :: (Show a) => GraphvizCommand -> DotGraph -> GraphvizOutput- -> (Handle -> IO a) -> IO (Maybe a)+graphvizWithHandle :: (PrintDot n, Show a) => GraphvizCommand -> DotGraph n+ -> GraphvizOutput -> (Handle -> IO a) -> IO (Maybe a) graphvizWithHandle cmd gr t f = do (inp, outp, errp, prc) <- runInteractiveCommand command- forkIO $ hPrint inp gr >> hClose inp+ forkIO $ hPutStrLn inp (printDotGraph gr) >> hClose inp forkIO $ (hGetContents errp >>= hPutStr stderr >> hClose errp) a <- f outp -- Don't close outp until f finishes.
− Data/GraphViz/ParserCombinators.hs
@@ -1,239 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}--{- |- Module : Data.GraphViz.ParserCombinators- Description : Helper functions for Parsing.- Copyright : (c) Matthew Sackman, Ivan Lazar Miljenovic- License : 3-Clause BSD-style- Maintainer : Ivan.Miljenovic@gmail.com-- This module defines simple helper functions for use with- "Text.ParserCombinators.Poly.Lazy".-- Note that the 'Parseable' instances for 'Bool', etc. match those- specified for use with GraphViz (e.g. non-zero integers are- equivalent to 'True').-- You should not be using this module; rather, it is here for- informative/documentative reasons. If you want to parse a- @'Data.GraphViz.Types.DotGraph'@, you should use- @'Data.GraphViz.Types.parseDotGraph'@ rather than its 'Parseable'- instance.---}--module Data.GraphViz.ParserCombinators- ( module Text.ParserCombinators.Poly.Lazy- , Parse- , Parseable(..)- , stringBlock- , quotedString- , parseAndSpace- , string- , strings- , hasString- , char- , whitespace- , whitespace'- , optionalQuotedString- , optionalQuoted- , quotedParse- , newline- , skipToNewline- , parseField- , parseBoolField- , parseFieldDef- , commaSep- , commaSep'- ) where--import Text.ParserCombinators.Poly.Lazy-import Data.Char( digitToInt- , isAsciiLower- , isAsciiUpper- , isDigit- , isSpace- , toLower- )-import Data.Function(on)-import Data.Maybe(isJust)-import Data.Ratio((%))-import Control.Monad---- -------------------------------------------------------------------------------- Based off code from Text.Parse in the polyparse library---- | A @ReadS@-like type alias.-type Parse a = Parser Char a--class Parseable a where- parse :: Parse a-- parseList :: Parse [a]- parseList = oneOf [ char '[' >> whitespace' >> char ']' >> return []- , bracketSep (parseAndSpace $ char '[')- (parseAndSpace $ char ',')- (parseAndSpace $ char ']')- (parseAndSpace parse)- ]--instance Parseable Int where- parse = parseInt--instance Parseable Double where- parse = parseSigned parseFloat--instance Parseable Bool where- parse = oneOf [ string "true" >> return True- , string "false" >> return False- , liftM (zero /=) parseInt- ]- where- zero :: Int- zero = 0--instance Parseable Char where- parse = next-- parseList = oneOf [ stringBlock- , quotedString- ]---- | Used when quotes are explicitly required;--- note that the quotes are not stripped off.--instance (Parseable a) => Parseable [a] where- parse = parseList--stringBlock :: Parse String-stringBlock = do frst <- satisfy frstCond- rest <- many (satisfy restCond)- return $ frst : rest- where- frstCond c = any ($c) [ isAsciiUpper- , isAsciiLower- , (==) '_'- , \ x -> x >= '\200' && x <= '\377'- ]- restCond c = frstCond c || isDigit c--quotedString :: Parse String-quotedString = do w <- word- if head w == '"'- then return w- else fail $ "Not a quoted string: " ++ w--word :: Parse String-word = P (\s-> case lex s of- [] -> Failure s "no input? (impossible)"- [("","")] -> Failure "" "no input?"- [("",s')] -> Failure s' "lexing failed?"- ((x,s'):_) -> Success s' x- )--parseSigned :: Real a => Parse a -> Parse a-parseSigned p = do '-' <- next; commit (fmap negate p)- `onFail`- p--parseInt :: (Integral a) => Parse a-parseInt = do cs <- many1 (satisfy isDigit)- return (foldl1 (\n d-> n*radix+d)- (map (fromIntegral . digitToInt) cs))- `adjustErr` (++ "\nexpected one or more digits")- where- radix = 10--parseFloat :: (RealFrac a) => Parse a-parseFloat = do ds <- many1 (satisfy isDigit)- frac <- (do '.' <- next- many (satisfy isDigit)- `adjustErrBad` (++"expected digit after .")- `onFail` return [] )- expn <- parseExp `onFail` return 0- ( return . fromRational . (* (10^^(expn - length frac)))- . (%1) . fst- . runParser parseInt) (ds++frac)- `onFail`- do w <- many (satisfy (not.isSpace))- case map toLower w of- "nan" -> return (0/0)- "infinity" -> return (1/0)- _ -> fail "expected a floating point number"- where parseExp = do 'e' <- fmap toLower next- commit (do '+' <- next; parseInt- `onFail`- parseSigned parseInt)---- -------------------------------------------------------------------------------parseAndSpace :: Parse a -> Parse a-parseAndSpace p = p `discard` whitespace'--string :: String -> Parse String-string = mapM char--strings :: [String] -> Parse String-strings = oneOf . map string--hasString :: String -> Parse Bool-hasString = liftM isJust . optional . string--char :: Char -> Parse Char-char c = satisfy (((==) `on` toLower) c)- `adjustErr`- (++ "\nnot the expected char: " ++ [c])--noneOf :: (Eq a) => [a] -> Parser a a-noneOf t = satisfy (\x -> all (/= x) t)--whitespace :: Parse String-whitespace = many1 (satisfy isSpace)--whitespace' :: Parse String-whitespace' = many (satisfy isSpace)--optionalQuotedString :: String -> Parse String-optionalQuotedString = optionalQuoted . string--optionalQuoted :: Parse a -> Parse a-optionalQuoted p = oneOf [ p- , quotedParse p- ]--quotedParse :: Parse a -> Parse a-quotedParse p = char '"' >> p `discard` char '"'--newline :: Parse String-newline = oneOf . map string $ ["\r\n", "\n", "\r"]--skipToNewline :: Parse ()-skipToNewline = many (noneOf ['\n','\r']) >> newline >> return ()--parseField :: (Parseable a) => String -> Parse a-parseField fld = do string fld- whitespace'- char '='- whitespace'- parse--parseBoolField :: String -> Parse Bool-parseBoolField = parseFieldDef True---- | For 'Bool'-like data structures where the presence of the field--- name without a value implies a default value.-parseFieldDef :: (Parseable a) => a -> String -> Parse a-parseFieldDef d fld = oneOf [ parseField fld- , string fld >> return d- ]--commaSep :: (Parseable a, Parseable b) => Parse (a, b)-commaSep = commaSep' parse parse--commaSep' :: Parse a -> Parse b -> Parse (a,b)-commaSep' pa pb = do a <- pa- whitespace'- char ','- whitespace'- b <- pb- return (a,b)
Data/GraphViz/Types.hs view
@@ -9,246 +9,599 @@ with them for the GraphViz library. The specifications are based loosely upon the information available at: <http://graphviz.org/doc/info/lang.html>--} + Printing of /Dot/ code is done as strictly as possible, whilst+ parsing is as permissive as possible. For example, if the types+ allow it then @\"2\"@ will be parsed as an 'Int' value. Note that+ quoting and escaping of 'String' values is done automagically.++ A summary of known limitations\/differences:++ * When creating 'GraphID' values for 'graphID' and 'subGraphID',+ you should ensure that none of them have the same printed value+ as one of the 'nodeID' values to avoid any possible problems.++ * Whilst 'DotGraph', etc. are polymorphic in their node type, you+ should ensure that you use a relatively simple node type (that+ is, it only covers a single line, etc.).++ * Also, whilst GraphViz allows you to mix the types used for nodes,+ this library requires\/assumes that they are all the same type.++ * 'DotEdge' defines an edge @(a, b)@ (with an edge going from @a@+ to @b@); in /Dot/ parlance the edge has a head at @a@ and a tail+ at @b@. Care must be taken when using the related @Head*@ and+ @Tail*@ 'Attribute's. See the differences section in+ "Data.GraphViz.Attributes" for more information.++ * It is common to see multiple edges defined on the one line in Dot+ (e.g. @n1 -> n2 -> n3@ means to create a directed edge from @n1@+ to @n2@ and from @n2@ to @n3@). These types of edge definitions+ are parseable; however, they are converted to singleton edges.++ * Cannot create edges with subgraphs\/clusters as one of the+ end points.++ * When either creating a 'DotGraph' by hand or parsing one, it is+ possible to specify that @'directedGraph' = d@, but at lease one+ 'DotEdge' has @'directedEdge' = 'not' d@.++ * Nodes cannot have Port values.++ * Cannot place items in an arbitrary order: in particular, this+ means that it is not possible to use the normal GraphViz hack of+ having graph attributes that do not apply to subgraphs\/clusters+ by listing them /after/ the subgraphs\/clusters.++ * The parser cannot as yet parse comments (in that it doesn't even+ know what comments are, and will throw an error). Multiline+ statements and C pre-processor lines (that is, lines beginning+ with a @#@) are also not parseable.++ See "Data.GraphViz.Attributes" for more limitations.++-} module Data.GraphViz.Types- ( DotGraph(..)- , GraphID(..)- , DotNode(..)- , DotEdge(..)+ ( -- * The overall representation of a graph in /Dot/ format.+ DotGraph(..)+ -- ** Printing and parsing a @DotGraph@.+ , printDotGraph , parseDotGraph+ -- ** Functions acting on a @DotGraph@. , setID , makeStrict+ , graphNodes+ , graphEdges+ -- ** Reporting of errors in a @DotGraph@.+ , DotError(..) , isValidGraph- , invalidAttributes+ , graphErrors+ -- * Sub-components of a @DotGraph@.+ , GraphID(..)+ , DotStatements(..)+ , GlobalAttributes(..)+ , DotSubGraph(..)+ , DotNode(..)+ , DotEdge(..) ) where import Data.GraphViz.Attributes-import Data.GraphViz.ParserCombinators+import Data.GraphViz.Types.Internal+import Data.GraphViz.Types.Parsing+import Data.GraphViz.Types.Printing -import Data.Maybe-import Control.Monad+import Data.Maybe(isJust)+import Control.Monad(liftM) -- ----------------------------------------------------------------------------- -- | The internal representation of a graph in Dot form.-data DotGraph = DotGraph { strictGraph :: Bool- , directedGraph :: Bool- , graphID :: Maybe GraphID- , graphAttributes :: [Attribute]- , graphNodes :: [DotNode]- , graphEdges :: [DotEdge]- }- deriving (Eq, Read)+data DotGraph a = DotGraph { strictGraph :: Bool -- ^ If 'True', no multiple edges are drawn.+ , directedGraph :: Bool+ , graphID :: Maybe GraphID+ , graphStatements :: DotStatements a+ }+ deriving (Eq, Show, Read) -- | A strict graph disallows multiple edges.-makeStrict :: DotGraph -> DotGraph+makeStrict :: DotGraph a -> DotGraph a makeStrict g = g { strictGraph = True } -setID :: GraphID -> DotGraph -> DotGraph+-- | Set the ID of the graph.+setID :: GraphID -> DotGraph a -> DotGraph a setID i g = g { graphID = Just i } --- | Check if all the @Attribute@s are being used correctly.-isValidGraph :: DotGraph -> Bool-isValidGraph g = null gas && null nas && null eas- where- (gas, nas, eas) = invalidAttributes g+-- | Return all the 'DotNode's contained within this 'DotGraph'. Note+-- that it does not yet return all implicitly defined nodes+-- contained only within 'DotEdge's.+graphNodes :: DotGraph a -> [DotNode a]+graphNodes = statementNodes . graphStatements --- | Return all those @Attribute@s which aren't being used properly.-invalidAttributes :: DotGraph -> ( [Attribute]- , [(DotNode, Attribute)]- , [(DotEdge, Attribute)]- )-invalidAttributes g = ( invalidGraphAttributes g- , concatMap invalidNodeAttributes $ graphNodes g- , concatMap invalidEdgeAttributes $ graphEdges g- )+-- | Return all the 'DotEdge's contained within this 'DotGraph'.+graphEdges :: DotGraph a -> [DotEdge a]+graphEdges = statementEdges . graphStatements -invalidGraphAttributes :: DotGraph -> [Attribute]-invalidGraphAttributes = filter (not . usedByGraphs) . graphAttributes+-- | The actual /Dot/ code for a 'DotGraph'. Note that it is expected+-- that @'parseDotGraph' . 'printDotGraph' == 'id'@ (this might not+-- be true the other way around due to un-parseable components).+printDotGraph :: (PrintDot a) => DotGraph a -> String+printDotGraph = renderDot . toDot -instance Show DotGraph where- show g- = unlines $ (hdr ++ " {") : (rest ++ ["}"])- where- hdr = strct . addId $ gType- strct = if strictGraph g- then ("strict " ++)- else id- addId = maybe id (\ i -> flip (++) $ ' ' : show i) $ graphID g- gType = if directedGraph g then dirGraph else undirGraph- rest = case graphAttributes g of- [] -> nodesEdges- a -> ("\tgraph " ++ show a ++ ";") : nodesEdges- nodesEdges = map show (graphNodes g) ++ map show (graphEdges g)+-- | Parse a limited subset of the Dot language to form a 'DotGraph'+-- (that is, the caveats listed in "Data.GraphViz.Attributes" aside,+-- Dot graphs are parsed if they match the layout of 'DotGraph').+parseDotGraph :: (ParseDot a) => String -> DotGraph a+parseDotGraph = fst . runParser parse +-- | Check if all the 'Attribute's are being used correctly.+isValidGraph :: DotGraph a -> Bool+isValidGraph = null . graphErrors++-- | Return detectable errors in the 'DotGraph'.+graphErrors :: DotGraph a -> [DotError a]+graphErrors = invalidStmts usedByGraphs . graphStatements++instance (PrintDot a) => PrintDot (DotGraph a) where+ unqtDot = printStmtBased printGraphID graphStatements++printGraphID :: (PrintDot a) => DotGraph a -> DotCode+printGraphID g = bool strGraph' empty (strictGraph g)+ <+> bool dirGraph' undirGraph' (directedGraph g)+ <+> maybe empty toDot (graphID g)++instance (ParseDot a) => ParseDot (DotGraph a) where+ parseUnqt = parseStmtBased parseGraphID++ parse = parseUnqt -- Don't want the option of quoting+ `adjustErr`+ (++ "\n\nNot a valid DotGraph")++parseGraphID :: (ParseDot a) => Parse (DotStatements a -> DotGraph a)+parseGraphID = do str <- liftM isJust+ $ optional (parseAndSpace $ string strGraph)+ dir <- parseAndSpace ( stringRep True dirGraph+ `onFail`+ stringRep False undirGraph+ )+ gID <- optional parse+ return $ DotGraph str dir gID+ dirGraph :: String dirGraph = "digraph" +dirGraph' :: DotCode+dirGraph' = text dirGraph+ undirGraph :: String undirGraph = "graph" --- | Parse a limited subset of the Dot language to form a 'DotGraph'--- (that is, the caveats listed in "Data.GraphViz.Attributes" aside,--- Dot graphs are parsed if they match the layout of DotGraph).-parseDotGraph :: Parse DotGraph-parseDotGraph = parse+undirGraph' :: DotCode+undirGraph' = text undirGraph -instance Parseable DotGraph where- parse = do isStrict <- parseAndSpace $ hasString "strict"- gType <- strings [dirGraph,undirGraph]- gId <- optional (parse `discard` whitespace)- whitespace- char '{'- skipToNewline- as <- liftM concat $- many (whitespace' >>- oneOf [ string "edge" >> skipToNewline >> return []- , string "node" >> skipToNewline >> return []- , string "graph" >> whitespace- >> parse `discard` skipToNewline- ]- )- ns <- many1 (whitespace' >> parse `discard` skipToNewline)- es <- many1 (whitespace' >> parse `discard` skipToNewline)- char '}'- return DotGraph { strictGraph = isStrict- , directedGraph = gType == dirGraph- , graphID = gId- , graphAttributes = as- , graphNodes = ns- , graphEdges = es- }+strGraph :: String+strGraph = "strict" - `adjustErr`- (++ "\nNot a valid DotGraph")+strGraph' :: DotCode+strGraph' = text strGraph +instance Functor DotGraph where+ fmap f g = g { graphStatements = fmap f $ graphStatements g }+ -- ----------------------------------------------------------------------------- +-- | Used to record invalid 'Attribute' usage. A 'Just' value denotes+-- that it was used in an explicit 'DotNode' or 'DotEdge' usage;+-- 'Nothing' means that it was used in a 'GlobalAttributes' value.+data DotError a = GraphError Attribute+ | NodeError (Maybe a) Attribute+ | EdgeError (Maybe (a,a)) Attribute+ deriving (Eq, Show, Read)++-- -----------------------------------------------------------------------------++-- | A polymorphic type that covers all possible ID values allowed by+-- Dot syntax. Note that whilst the 'ParseDot' and 'PrintDot'+-- instances for 'String' will properly take care of the special+-- cases for numbers, they are treated differently here. data GraphID = Str String- | Num Double- | QStr QuotedString+ | Int Int+ | Dbl Double | HTML URL- deriving (Eq, Read)+ deriving (Eq, Show, Read) -instance Show GraphID where- show (Str str) = str- show (Num n) = show n- show (QStr str) = show str- show (HTML url) = show url+instance PrintDot GraphID where+ unqtDot (Str str) = unqtDot str+ unqtDot (Int i) = unqtDot i+ unqtDot (Dbl d) = unqtDot d+ unqtDot (HTML u) = unqtDot u -instance Parseable GraphID where- parse = oneOf [ liftM Str stringBlock- , liftM Num parse- , liftM QStr parse+ toDot (Str str) = toDot str+ toDot gID = unqtDot gID++instance ParseDot GraphID where+ parseUnqt = oneOf [ liftM Str parseUnqt+ , liftM Int parseUnqt+ , liftM Dbl parseUnqt+ , liftM HTML parseUnqt+ ]++ parse = oneOf [ liftM Int parse+ , liftM Dbl parse , liftM HTML parse+ -- Parse last so that quoted numbers are parsed as numbers.+ , liftM Str parse ] `adjustErr`- (++ "\nNot a valid GraphID")+ (++ "Not a valid GraphID") -- ----------------------------------------------------------------------------- --- | A node in 'DotGraph' is either a singular node, or a cluster--- containing nodes (or more clusters) within it.--- At the moment, clusters are not parsed.-data DotNode- = DotNode { nodeID :: Int- , nodeAttributes :: [Attribute]- }- | DotCluster { clusterID :: String- , clusterAttributes :: [Attribute]- , clusterElems :: [DotNode]- }- deriving (Eq, Read)+data DotStatements a = DotStmts { attrStmts :: [GlobalAttributes]+ , subGraphs :: [DotSubGraph a]+ , nodeStmts :: [DotNode a]+ , edgeStmts :: [DotEdge a]+ }+ deriving (Eq, Show, Read) -invalidNodeAttributes :: DotNode -> [(DotNode, Attribute)]-invalidNodeAttributes n@DotNode{} = map ((,) n)- . filter (not . usedByNodes)- $ nodeAttributes n-invalidNodeAttributes c@DotCluster{} = cErr ++ nErr+instance (PrintDot a) => PrintDot (DotStatements a) where+ unqtDot stmts = vcat [ toDot $ attrStmts stmts+ , toDot $ subGraphs stmts+ , toDot $ nodeStmts stmts+ , toDot $ edgeStmts stmts+ ]++instance (ParseDot a) => ParseDot (DotStatements a) where+ parseUnqt = do atts <- tryParseList+ newline'+ sGraphs <- tryParseList+ newline'+ nodes <- tryParseList+ newline'+ edges <- tryParseList+ return $ DotStmts atts sGraphs nodes edges++ parse = parseUnqt -- Don't want the option of quoting+ `adjustErr`+ (++ "Not a valid set of statements")++instance Functor DotStatements where+ fmap f stmts = stmts { subGraphs = map (fmap f) $ subGraphs stmts+ , nodeStmts = map (fmap f) $ nodeStmts stmts+ , edgeStmts = map (fmap f) $ edgeStmts stmts+ }++printStmtBased :: (PrintDot n) => (a -> DotCode)+ -> (a -> DotStatements n) -> a -> DotCode+printStmtBased ff fss a = vcat [ ff a <+> lbrace+ , ind stmts+ , rbrace+ ] where- cErr = map ((,) c) . filter (not . usedByClusters)- $ clusterAttributes c- nErr = concatMap invalidNodeAttributes $ clusterElems c+ ind = nest 4+ stmts = toDot $ fss a -instance Show DotNode where- show = init . unlines . addTabs . nodesToString+printStmtBasedList :: (PrintDot n) => (a -> DotCode)+ -> (a -> DotStatements n) -> [a] -> DotCode+printStmtBasedList ff fss = vcat . map (printStmtBased ff fss) -nodesToString :: DotNode -> [String]-nodesToString n@(DotNode {})- | null nAs = [nID ++ ";"]- | otherwise = [nID ++ (' ':(show nAs ++ ";"))]+parseStmtBased :: (ParseDot n) => Parse (DotStatements n -> a) -> Parse a+parseStmtBased p = do f <- p+ whitespace'+ character '{'+ newline'+ stmts <- parse+ newline'+ whitespace'+ character '}'+ return $ f stmts+ `adjustErr`+ (++ "\n\nNot a valid statement-based structure")++parseStmtBasedList :: (ParseDot n) => Parse (DotStatements n -> a)+ -> Parse [a]+parseStmtBasedList p = sepBy (whitespace' >> parseStmtBased p) newline'++-- | The function represents which function to use to check the+-- 'GraphAttrs' values.+invalidStmts :: (Attribute -> Bool) -> DotStatements a -> [DotError a]+invalidStmts f stmts = concatMap (invalidGlobal f) (attrStmts stmts)+ ++ concatMap invalidSubGraph (subGraphs stmts)+ ++ concatMap invalidNode (nodeStmts stmts)+ ++ concatMap invalidEdge (edgeStmts stmts)++statementNodes :: DotStatements a -> [DotNode a]+statementNodes stmts = concatMap subGraphNodes (subGraphs stmts)+ ++ nodeStmts stmts++statementEdges :: DotStatements a -> [DotEdge a]+statementEdges stmts = concatMap subGraphEdges (subGraphs stmts)+ ++ edgeStmts stmts++-- -----------------------------------------------------------------------------++-- | Represents a list of top-level list of 'Attribute's for the+-- entire graph/sub-graph. Note that 'GraphAttrs' also applies to+-- 'DotSubGraph's.+--+-- Note that Dot allows a single 'Attribute' to be listen on a line;+-- if this is the case then when parsing, the type of 'Attribute' it+-- is determined and that type of 'GlobalAttribute' is created.+data GlobalAttributes = GraphAttrs { attrs :: Attributes }+ | NodeAttrs { attrs :: Attributes }+ | EdgeAttrs { attrs :: Attributes }+ deriving (Eq, Show, Read)++instance PrintDot GlobalAttributes where+ unqtDot = printAttrBased printGlobAttrType attrs++ unqtListToDot = printAttrBasedList printGlobAttrType attrs++ listToDot = unqtListToDot++printGlobAttrType :: GlobalAttributes -> DotCode+printGlobAttrType GraphAttrs{} = text "graph"+printGlobAttrType NodeAttrs{} = text "node"+printGlobAttrType EdgeAttrs{} = text "edge"++instance ParseDot GlobalAttributes where+ parseUnqt = parseAttrBased parseGlobAttrType+ `onFail`+ liftM determineType parse `discard` optional lineEnd++ parse = parseUnqt -- Don't want the option of quoting+ `adjustErr`+ (++ "\n\nNot a valid listing of global attributes")++ -- Have to do this manually because of the special case+ parseUnqtList = sepBy (whitespace' >> parse) newline'++ parseList = parseUnqtList++parseGlobAttrType :: Parse (Attributes -> GlobalAttributes)+parseGlobAttrType = oneOf [ stringRep GraphAttrs "graph"+ , stringRep NodeAttrs "node"+ , stringRep EdgeAttrs "edge"+ ]++determineType :: Attribute -> GlobalAttributes+determineType attr+ | usedByGraphs attr = GraphAttrs attr'+ | usedByClusters attr = GraphAttrs attr' -- Also covers SubGraph case+ | usedByNodes attr = NodeAttrs attr'+ | otherwise = EdgeAttrs attr' -- Must be for edges. where- nID = show $ nodeID n- nAs = nodeAttributes n-nodesToString c@(DotCluster {})- = ["subgraph cluster_" ++ clusterID c ++ " {"] ++ addTabs inner ++ ["}"]+ attr' = [attr]++invalidGlobal :: (Attribute -> Bool) -> GlobalAttributes+ -> [DotError a]+invalidGlobal f (GraphAttrs as) = map GraphError $ filter (not . f) as+invalidGlobal _ (NodeAttrs as) = map (NodeError Nothing)+ $ filter (not . usedByNodes) as+invalidGlobal _ (EdgeAttrs as) = map (EdgeError Nothing)+ $ filter (not . usedByEdges) as++-- -----------------------------------------------------------------------------++data DotSubGraph a = DotSG { isCluster :: Bool+ , subGraphID :: Maybe GraphID+ , subGraphStmts :: DotStatements a+ }+ deriving (Eq, Show, Read)++instance (PrintDot a) => PrintDot (DotSubGraph a) where+ unqtDot = printStmtBased printSubGraphID subGraphStmts++ unqtListToDot = printStmtBasedList printSubGraphID subGraphStmts++ listToDot = unqtListToDot++printSubGraphID :: DotSubGraph a -> DotCode+printSubGraphID s = sGraph'+ <+> bool clust' empty isCl+ <> maybe empty dtID (subGraphID s) where- inner = case clusterAttributes c of- [] -> nodes- a -> ("graph " ++ show a ++ ";") : nodes- nodes = concatMap nodesToString $ clusterElems c+ isCl = isCluster s+ dtID sId = bool (char '_') space isCl+ <> toDot sId +instance (ParseDot a) => ParseDot (DotSubGraph a) where+ parseUnqt = parseStmtBased parseSubGraphID -instance Parseable DotNode where- parse = do nId <- parse- as <- optional (whitespace >> parse)- char ';'- return DotNode { nodeID = nId- , nodeAttributes = fromMaybe [] as }+ parse = parseUnqt -- Don't want the option of quoting `adjustErr`- (++ "\nNot a valid DotNode")+ (++ "\n\nNot a valid Sub Graph") --- | Prefix each 'String' with a tab character.-addTabs :: [String] -> [String]-addTabs = map ('\t':)+ parseUnqtList = parseStmtBasedList parseSubGraphID + parseList = parseUnqtList++parseSubGraphID :: Parse (DotStatements a -> DotSubGraph a)+parseSubGraphID = do string sGraph+ whitespace'+ isCl <- liftM isJust+ $ optional (string clust)+ `discard` optional (character '_')+ sId <- optional parse+ return $ DotSG isCl sId++sGraph :: String+sGraph = "subgraph"++sGraph' :: DotCode+sGraph' = text sGraph++clust :: String+clust = "cluster"++clust' :: DotCode+clust' = text clust++instance Functor DotSubGraph where+ fmap f sg = sg { subGraphStmts = fmap f $ subGraphStmts sg }++invalidSubGraph :: DotSubGraph a -> [DotError a]+invalidSubGraph sg = invalidStmts valFunc (subGraphStmts sg)+ where+ valFunc = bool usedByClusters usedBySubGraphs (isCluster sg)++subGraphNodes :: DotSubGraph a -> [DotNode a]+subGraphNodes = statementNodes . subGraphStmts++subGraphEdges :: DotSubGraph a -> [DotEdge a]+subGraphEdges = statementEdges . subGraphStmts+ -- ----------------------------------------------------------------------------- +-- | A node in 'DotGraph' is either a singular node, or a cluster+-- containing nodes (or more clusters) within it.+-- At the moment, clusters are not parsed.+data DotNode a = DotNode { nodeID :: a+ , nodeAttributes :: Attributes+ }+ deriving (Eq, Show, Read)++instance (PrintDot a) => PrintDot (DotNode a) where+ unqtDot = printAttrBased printNodeID nodeAttributes++ unqtListToDot = printAttrBasedList printNodeID nodeAttributes++ listToDot = unqtListToDot++printNodeID :: (PrintDot a) => DotNode a -> DotCode+printNodeID = toDot . nodeID++instance (ParseDot a) => ParseDot (DotNode a) where+ parseUnqt = parseAttrBased parseNodeID++ parse = parseUnqt -- Don't want the option of quoting++ parseUnqtList = parseAttrBasedList parseNodeID++ parseList = parseUnqtList++parseNodeID :: (ParseDot a) => Parse (Attributes -> DotNode a)+parseNodeID = liftM DotNode parse++instance Functor DotNode where+ fmap f n = n { nodeID = f $ nodeID n }++invalidNode :: DotNode a -> [DotError a]+invalidNode n = map (NodeError (Just $ nodeID n))+ $ filter (not . usedByNodes) (nodeAttributes n)++-- -----------------------------------------------------------------------------+ -- | An edge in 'DotGraph'.-data DotEdge = DotEdge { edgeHeadNodeID :: Int- , edgeTailNodeID :: Int- , edgeAttributes :: [Attribute]- , directedEdge :: Bool- }- deriving (Eq, Read)+data DotEdge a = DotEdge { edgeFromNodeID :: a+ , edgeToNodeID :: a+ , directedEdge :: Bool+ , edgeAttributes :: Attributes+ }+ deriving (Eq, Show, Read) -invalidEdgeAttributes :: DotEdge -> [(DotEdge, Attribute)]-invalidEdgeAttributes e = map ((,) e)- . filter (not . usedByEdges)- $ edgeAttributes e+instance (PrintDot a) => PrintDot (DotEdge a) where+ unqtDot = printAttrBased printEdgeID edgeAttributes -instance Show DotEdge where- show e- = '\t' : (show (edgeHeadNodeID e)- ++ edge ++ show (edgeTailNodeID e) ++ attributes)- where- edge = " " ++ (if directedEdge e then dirEdge else undirEdge) ++ " "- attributes = case edgeAttributes e of- [] -> ";"- a -> ' ':(show a ++ ";")+ unqtListToDot = printAttrBasedList printEdgeID edgeAttributes + listToDot = unqtListToDot++printEdgeID :: (PrintDot a) => DotEdge a -> DotCode+printEdgeID e = unqtDot (edgeFromNodeID e)+ <+> bool dirEdge' undirEdge' (directedEdge e)+ <+> unqtDot (edgeToNodeID e)+++instance (ParseDot a) => ParseDot (DotEdge a) where+ parseUnqt = parseAttrBased parseEdgeID++ parse = parseUnqt -- Don't want the option of quoting++ -- Have to take into account edges of the type "n1 -> n2 -> n3", etc.+ parseUnqtList = liftM concat+ $ sepBy (whitespace' >> parseEdgeLine) newline'++ parseList = parseUnqtList++parseEdgeID :: (ParseDot a) => Parse (Attributes -> DotEdge a)+parseEdgeID = do eHead <- parse+ whitespace'+ eType <- parseEdgeType+ whitespace'+ eTail <- parse+ return $ DotEdge eHead eTail eType++parseEdgeType :: Parse Bool+parseEdgeType = stringRep True dirEdge+ `onFail`+ stringRep False undirEdge++parseEdgeLine :: (ParseDot a) => Parse [DotEdge a]+parseEdgeLine = liftM return parse+ `onFail`+ do n1 <- parse+ ens <- many1 $ do whitespace'+ eType <- parseEdgeType+ whitespace'+ n <- parse+ return (eType, n)+ let ens' = (True, n1) : ens+ efs = zipWith mkEdg ens' (tail ens')+ ef = return $ \ as -> map ($as) efs+ parseAttrBased ef+ where+ mkEdg (_, hn) (et, tn) = DotEdge hn tn et++instance Functor DotEdge where+ fmap f e = e { edgeFromNodeID = f $ edgeFromNodeID e+ , edgeToNodeID = f $ edgeToNodeID e+ }+ dirEdge :: String dirEdge = "->" +dirEdge' :: DotCode+dirEdge' = text dirEdge+ undirEdge :: String undirEdge = "--" -instance Parseable DotEdge where- parse = do whitespace'- eHead <- parse- whitespace- edgeType <- strings [dirEdge,undirEdge]- whitespace- eTail <- parse- as <- optional (whitespace >> parse)- char ';'- return DotEdge { edgeHeadNodeID = eHead- , edgeTailNodeID = eTail- , edgeAttributes = fromMaybe [] as- , directedEdge = edgeType == dirEdge- }- `adjustErr`- (++ "\nNot a valid DotEdge")+undirEdge' :: DotCode+undirEdge' = text undirEdge +invalidEdge :: DotEdge a -> [DotError a]+invalidEdge e = map (EdgeError eID)+ $ filter (not . usedByEdges) (edgeAttributes e)+ where+ eID = Just (edgeFromNodeID e, edgeToNodeID e)+ -- -----------------------------------------------------------------------------++-- Printing and parsing helpers.++printAttrBased :: (a -> DotCode) -> (a -> Attributes) -> a -> DotCode+printAttrBased ff fas a = dc <> semi+ where+ f = ff a+ dc = case fas a of+ [] -> f+ as -> f <+> toDot as++printAttrBasedList :: (a -> DotCode) -> (a -> Attributes)+ -> [a] -> DotCode+printAttrBasedList ff fas = vcat . map (printAttrBased ff fas)++parseAttrBased :: Parse (Attributes -> a) -> Parse a+parseAttrBased p = do f <- p+ whitespace'+ atts <- tryParseList+ lineEnd+ return $ f atts+ `adjustErr`+ (++ "\n\nNot a valid attribute-based structure")++parseAttrBasedList :: Parse (Attributes -> a) -> Parse [a]+parseAttrBasedList p = sepBy (whitespace' >> parseAttrBased p) newline'++lineEnd :: Parse ()+lineEnd = whitespace' >> character ';' >> return ()
Data/GraphViz/Types/Clustering.hs view
@@ -9,35 +9,35 @@ This module defines types for creating clusters. -}- module Data.GraphViz.Types.Clustering ( NodeCluster(..) , clustersToNodes ) where --- LT is defined in Attributes-import Prelude hiding (LT)-import qualified Prelude as P- import Data.GraphViz.Types import Data.GraphViz.Attributes -import Data.List(groupBy, sortBy, mapAccumL)-import Data.Graph.Inductive.Graph(Graph, LNode, labNodes)+import Data.Either(partitionEithers)+import Data.List(groupBy, sortBy)+import Data.Graph.Inductive.Graph(Graph, Node, LNode, labNodes) -- ----------------------------------------------------------------------------- -- | Define into which cluster a particular node belongs.--- Nodes can be nested to arbitrary depth.-data NodeCluster c a = N (LNode a) | C c (NodeCluster c a)+-- Clusters can be nested to arbitrary depth.+data NodeCluster c a = N (LNode a) -- ^ Indicates the actual Node in the Graph.+ | C c (NodeCluster c a) -- ^ Indicates that the+ -- 'NodeCluster' is in+ -- the Cluster /c/. deriving (Show) --- | Create the @'DotNode'@s for the given graph.+-- | Create the /Dot/ representation for the given graph. clustersToNodes :: (Ord c, Graph gr) => (LNode a -> NodeCluster c a)- -> (c -> [Attribute]) -> (LNode a -> [Attribute])- -> gr a b -> [DotNode]-clustersToNodes clusterBy fmtCluster fmtNode- = treesToNodes fmtCluster fmtNode+ -> (c -> Maybe GraphID) -> (c -> [GlobalAttributes])+ -> (LNode a -> Attributes) -> gr a b+ -> ([DotSubGraph Node], [DotNode Node])+clustersToNodes clusterBy cID fmtCluster fmtNode+ = treesToDot cID fmtCluster fmtNode . collapseNClusts . map (clustToTree . clusterBy) . labNodes@@ -45,7 +45,8 @@ -- ----------------------------------------------------------------------------- -- | A tree representation of a cluster.-data ClusterTree c a = NT (LNode a) | CT c [ClusterTree c a]+data ClusterTree c a = NT (LNode a)+ | CT c [ClusterTree c a] deriving (Show) -- | Convert a single node cluster into its tree representation.@@ -63,7 +64,7 @@ -- | Singleton nodes come first, and then ordering based upon the cluster. clustOrder :: (Ord c) => ClusterTree c a -> ClusterTree c a -> Ordering clustOrder (NT _) (NT _) = EQ-clustOrder (NT _) (CT _ _) = P.LT -- don't use the attribute LT+clustOrder (NT _) (CT _ _) = LT clustOrder (CT _ _) (NT _) = GT clustOrder (CT c1 _) (CT c2 _) = compare c1 c2 @@ -82,33 +83,33 @@ grpCls ns@((NT _):_) = ns grpCls cs@((CT c _):_) = [CT c (collapseNClusts $ concatMap getNodes cs)] ---- | Convert the cluster representation of the trees into @'DotNode'@s.--- Clusters will be labelled with @'Int'@s.-treesToNodes :: (c -> [Attribute]) -> (LNode a -> [Attribute])- -> [ClusterTree c a] -> [DotNode]-treesToNodes fmtCluster fmtNode = snd . treesToNodesFrom fmtCluster fmtNode 0---- | Start labelling the clusters with this @'Int'@.-treesToNodesFrom :: (c -> [Attribute]) -> (LNode a -> [Attribute])- -> Int -> [ClusterTree c a] -> (Int,[DotNode])-treesToNodesFrom fmtCluster fmtNode n = mapAccumL mkNodes n- where- mkNodes = treeToNode fmtCluster fmtNode---- | Convert this 'ClusterTree' into its 'DotNode' representation.-treeToNode :: (c -> [Attribute]) -> (LNode a -> [Attribute])- -> Int -> ClusterTree c a -> (Int, DotNode)-treeToNode _ fmtNode n (NT ln) = ( n- , DotNode { nodeID = fst ln- , nodeAttributes = fmtNode ln- }- )+-- | Convert the cluster representation of the trees into 'DotNode's+-- and 'DotSubGraph's (with @'isCluster' = 'True'@, and+-- @'subGraphID' = 'Nothing'@).+treesToDot :: (c -> Maybe GraphID) -> (c -> [GlobalAttributes])+ -> (LNode a -> Attributes) -> [ClusterTree c a]+ -> ([DotSubGraph Node], [DotNode Node])+treesToDot cID fmtCluster fmtNode+ = partitionEithers+ . map (treeToDot cID fmtCluster fmtNode) -treeToNode fmtCluster fmtNode n (CT c nts) = (n',clust)+-- | Convert this 'ClusterTree' into its /Dot/ representation.+treeToDot :: (c -> Maybe GraphID) -> (c -> [GlobalAttributes])+ -> (LNode a -> Attributes) -> ClusterTree c a+ -> Either (DotSubGraph Node) (DotNode Node)+treeToDot _ _ fmtNode (NT ln)+ = Right DotNode { nodeID = fst ln+ , nodeAttributes = fmtNode ln+ }+treeToDot cID fmtCluster fmtNode (CT c nts)+ = Left DotSG { isCluster = True+ , subGraphID = cID c+ , subGraphStmts = stmts+ } where- (n', nts') = treesToNodesFrom fmtCluster fmtNode (n+1) nts- clust = DotCluster { clusterID = show n- , clusterAttributes = fmtCluster c- , clusterElems = nts'- }+ stmts = DotStmts { attrStmts = fmtCluster c+ , subGraphs = cs+ , nodeStmts = ns+ , edgeStmts = []+ }+ (cs, ns) = treesToDot cID fmtCluster fmtNode nts
+ Data/GraphViz/Types/Internal.hs view
@@ -0,0 +1,63 @@+{- |+ Module : Data.GraphViz.Types.Internal+ Description : Internal functions+ Copyright : (c) Ivan Lazar Miljenovic+ License : 3-Clause BSD-style+ Maintainer : Ivan.Miljenovic@gmail.com++ This module defines internal functions.+-}+module Data.GraphViz.Types.Internal where++import Data.Char( isAsciiUpper+ , isAsciiLower+ , isDigit+ )++isIDString :: String -> Bool+isIDString [] = True+isIDString (f:os) = frstIDString f+ && all restIDString os++-- | First character of a non-quoted 'String' must match this.+frstIDString :: Char -> Bool+frstIDString c = any ($c) [ isAsciiUpper+ , isAsciiLower+ , (==) '_'+ , \ x -> x >= '\200' && x <= '\377'+ ]++-- | The rest of a non-quoted 'String' must match this.+restIDString :: Char -> Bool+restIDString c = frstIDString c || isDigit c++-- | Determine if this String represents a number.+isNumString :: String -> Bool+isNumString "" = False+isNumString str = case str of+ ('-':str') -> go str'+ _ -> go str+ where+ go [] = False+ go cs = case dropWhile isDigit cs of+ [] -> True+ ('.':ds) -> not (null ds) && all isDigit ds+ _ -> False++-- | GraphViz requires double quotes to be explicitly escaped.+escapeQuotes :: String -> String+escapeQuotes [] = []+escapeQuotes ('"':str) = '\\':'"': escapeQuotes str+escapeQuotes (c:str) = c : escapeQuotes str++-- | Remove explicit escaping of double quotes.+descapeQuotes :: String -> String+descapeQuotes [] = []+descapeQuotes ('\\':'"':str) = '"' : descapeQuotes str+descapeQuotes (c:str) = c : descapeQuotes str++-- | Fold over 'Bool's.+bool :: a -> a -> Bool -> a+bool t f b = if b+ then t+ else f
+ Data/GraphViz/Types/Parsing.hs view
@@ -0,0 +1,285 @@+{- |+ Module : Data.GraphViz.Types.Parsing+ Description : Helper functions for Parsing.+ Copyright : (c) Matthew Sackman, Ivan Lazar Miljenovic+ License : 3-Clause BSD-style+ Maintainer : Ivan.Miljenovic@gmail.com++ This module defines simple helper functions for use with+ "Text.ParserCombinators.Poly.Lazy".++ Note that the 'ParseDot' instances for 'Bool', etc. match those+ specified for use with GraphViz (e.g. non-zero integers are+ equivalent to 'True').++ You should not be using this module; rather, it is here for+ informative/documentative reasons. If you want to parse a+ @'Data.GraphViz.Types.DotGraph'@, you should use+ @'Data.GraphViz.Types.parseDotGraph'@ rather than its 'ParseDot'+ instance.+-}+module Data.GraphViz.Types.Parsing+ ( module Text.ParserCombinators.Poly.Lazy+ , Parse+ , ParseDot(..)+ , stringBlock+ , numString+ , quotedString+ , parseAndSpace+ , string+ , strings+ , hasString+ , character+ , noneOf+ , whitespace+ , whitespace'+ , optionalQuotedString+ , optionalQuoted+ , quotedParse+ , newline+ , newline'+ , parseComma+ , tryParseList+ , tryParseList'+ , skipToNewline+ , parseField+ , parseFields+ , parseFieldBool+ , parseFieldsBool+ , parseFieldDef+ , parseFieldsDef+ , commaSep+ , commaSep'+ , stringRep+ ) where++import Data.GraphViz.Types.Internal++import Text.ParserCombinators.Poly.Lazy+import Data.Char( digitToInt+ , isDigit+ , isSpace+ , toLower+ )+import Data.Function(on)+import Data.Maybe(isJust, fromMaybe)+import Data.Ratio((%))+import Control.Monad++-- -----------------------------------------------------------------------------+-- Based off code from Text.Parse in the polyparse library++-- | A @ReadS@-like type alias.+type Parse a = Parser Char a++class ParseDot a where+ parseUnqt :: Parse a++ parse :: Parse a+ parse = optionalQuoted parseUnqt++ parseUnqtList :: Parse [a]+ parseUnqtList = bracketSep (parseAndSpace $ character '[')+ (parseAndSpace $ parseComma)+ (parseAndSpace $ character ']')+ (parseAndSpace parse)++ parseList :: Parse [a]+ parseList = quotedParse parseUnqtList++instance ParseDot Int where+ parseUnqt = parseInt'++instance ParseDot Double where+ parseUnqt = parseFloat'++instance ParseDot Bool where+ parseUnqt = oneOf [ stringRep True "true"+ , stringRep False "false"+ , liftM (zero /=) parseInt'+ ]+ where+ zero :: Int+ zero = 0++instance ParseDot Char where+ -- Can't be a quote character.+ parseUnqt = satisfy ((/=) '"')++ parse = satisfy restIDString+ `onFail`+ quotedParse parseUnqt++ parseUnqtList = oneOf [ numString+ , stringBlock+ , quotedString+ ]++ parseList = oneOf [numString, stringBlock]+ `onFail`+ -- This will also take care of quoted versions of+ -- above.+ quotedParse quotedString++instance (ParseDot a) => ParseDot [a] where+ parseUnqt = parseUnqtList++ parse = parseList++numString :: Parse String+numString = liftM show parseInt'+ `onFail`+ liftM show parseFloat'++stringBlock :: Parse String+stringBlock = do frst <- satisfy frstIDString+ rest <- many (satisfy restIDString)+ return $ frst : rest++-- | Used when quotes are explicitly required;+quotedString :: Parse String+quotedString = many $ oneOf [ stringRep '"' "\\\""+ , satisfy ((/=) '"')+ ]++parseSigned :: Real a => Parse a -> Parse a+parseSigned p = do '-' <- next; commit (fmap negate p)+ `onFail`+ p++parseInt :: (Integral a) => Parse a+parseInt = do cs <- many1 (satisfy isDigit)+ return (foldl1 (\n d-> n*radix+d)+ (map (fromIntegral . digitToInt) cs))+ `adjustErr` (++ "\nexpected one or more digits")+ where+ radix = 10++parseInt' :: Parse Int+parseInt' = parseSigned parseInt++parseFloat :: (RealFrac a) => Parse a+parseFloat = do ds <- many1 (satisfy isDigit)+ frac <- (do '.' <- next+ many (satisfy isDigit)+ `adjustErrBad` (++"expected digit after .")+ `onFail` return [] )+ expn <- parseExp `onFail` return 0+ ( return . fromRational . (* (10^^(expn - length frac)))+ . (%1) . fst+ . runParser parseInt) (ds++frac)+ `onFail`+ do w <- many (satisfy (not.isSpace))+ case map toLower w of+ "nan" -> return (0/0)+ "infinity" -> return (1/0)+ _ -> fail "expected a floating point number"+ where parseExp = do 'e' <- fmap toLower next+ commit (do '+' <- next; parseInt+ `onFail`+ parseSigned parseInt)++parseFloat' :: Parse Double+parseFloat' = parseSigned parseFloat++-- -----------------------------------------------------------------------------++parseAndSpace :: Parse a -> Parse a+parseAndSpace p = p `discard` whitespace'++string :: String -> Parse String+string = mapM character++stringRep :: a -> String -> Parse a+stringRep v s = string s >> return v++strings :: [String] -> Parse String+strings = oneOf . map string++hasString :: String -> Parse Bool+hasString = liftM isJust . optional . string++character :: Char -> Parse Char+character c = satisfy (((==) `on` toLower) c)+ `adjustErr`+ (++ "\nnot the expected char: " ++ [c])++noneOf :: (Eq a) => [a] -> Parser a a+noneOf t = satisfy (\x -> all (/= x) t)++whitespace :: Parse String+whitespace = many1 (satisfy isSpace)++whitespace' :: Parse String+whitespace' = many (satisfy isSpace)++optionalQuotedString :: String -> Parse String+optionalQuotedString = optionalQuoted . string++optionalQuoted :: Parse a -> Parse a+optionalQuoted p = p+ `onFail`+ quotedParse p++quotedParse :: Parse a -> Parse a+quotedParse p = bracket quote quote p+ where+ quote = character '"'++newline :: Parse String+newline = oneOf $ map string ["\r\n", "\n", "\r"]++-- | Consume all whitespace and newlines until a line with+-- non-whitespace is reached. The whitespace on that line is+-- not consumed.+newline' :: Parse ()+newline' = many (whitespace' >> newline) >> return ()++skipToNewline :: Parse ()+skipToNewline = many (noneOf ['\n','\r']) >> newline >> return ()++parseField :: (ParseDot a) => String -> Parse a+parseField fld = do string fld+ whitespace'+ character '='+ whitespace'+ parse++parseFields :: (ParseDot a) => [String] -> Parse a+parseFields = oneOf . map parseField++parseFieldBool :: String -> Parse Bool+parseFieldBool = parseFieldDef True++parseFieldsBool :: [String] -> Parse Bool+parseFieldsBool = oneOf . map parseFieldBool++-- | For 'Bool'-like data structures where the presence of the field+-- name without a value implies a default value.+parseFieldDef :: (ParseDot a) => a -> String -> Parse a+parseFieldDef d fld = parseField fld+ `onFail`+ stringRep d fld++parseFieldsDef :: (ParseDot a) => a -> [String] -> Parse a+parseFieldsDef d = oneOf . map (parseFieldDef d)++commaSep :: (ParseDot a, ParseDot b) => Parse (a, b)+commaSep = commaSep' parse parse++commaSep' :: Parse a -> Parse b -> Parse (a,b)+commaSep' pa pb = do a <- pa+ whitespace'+ parseComma+ whitespace'+ b <- pb+ return (a,b)++parseComma :: Parse Char+parseComma = character ','++tryParseList :: (ParseDot a) => Parse [a]+tryParseList = tryParseList' parse++tryParseList' :: Parse [a] -> Parse [a]+tryParseList' = liftM (fromMaybe []) . optional
+ Data/GraphViz/Types/Printing.hs view
@@ -0,0 +1,151 @@+{- |+ Module : Data.GraphViz.Types.Printing+ Description : Helper functions for converting to Dot format.+ Copyright : (c) Ivan Lazar Miljenovic+ License : 3-Clause BSD-style+ Maintainer : Ivan.Miljenovic@gmail.com++ This module defines simple helper functions for use with+ "Text.PrettyPrint". It also re-exports all the pretty-printing+ combinators from that module.++ Note that the 'PrintDot' instances for 'Bool', etc. match those+ specified for use with GraphViz.++ You should only be using this module if you are writing custom node+ types for use with "Data.GraphViz.Types". For actual printing of+ code, use @'Data.GraphViz.Types.printDotGraph'@ (which produces a+ 'String' value).++ The Dot language specification specifies that any identifier is in+ one of four forms:++ * Any string of alphabetic ([a-zA-Z\200-\377]) characters, underscores ('_') or digits ([0-9]), not beginning with a digit;++ * a number [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? );++ * any double-quoted string (\"...\") possibly containing escaped quotes (\");++ * an HTML string (<...>).++ Due to these restrictions, you should only use 'text' when you are+ sure that the 'String' in question is static and quotes are+ definitely needed/unneeded; it is better to use the 'String'+ instance for 'PrintDot'. For more information, see the+ specification page:+ <http://graphviz.org/doc/info/lang.html>+-}+module Data.GraphViz.Types.Printing+ ( module Text.PrettyPrint+ , DotCode+ , renderDot+ , PrintDot(..)+ , wrap+ , commaDel+ , printField+ ) where++import Data.GraphViz.Types.Internal++-- Only implicitly import and re-export combinators.+import Text.PrettyPrint hiding ( Style(..)+ , Mode(..)+ , TextDetails(..)+ , render+ , style+ , renderStyle+ , fullRender+ )+import qualified Text.PrettyPrint as PP++-- -----------------------------------------------------------------------------++-- | A type alias to indicate what is being produced.+type DotCode = Doc++-- | Correctly render GraphViz output.+renderDot :: DotCode -> String+renderDot = PP.renderStyle style'+ where+ style' = PP.style { PP.mode = PP.ZigZagMode }++-- | A class used to correctly print parts of the GraphViz Dot language.+-- Minimal implementation is 'unqtDot'.+class PrintDot a where+ -- | The unquoted representation, for use when composing values to+ -- produce a larger printing value.+ unqtDot :: a -> DotCode++ -- | The actual quoted representation; this should be quoted if it+ -- contains characters not permitted a plain ID String, a number+ -- or it is not an HTML string.+ -- Defaults to 'unqtDot'.+ toDot :: a -> DotCode+ toDot = unqtDot++ -- | The correct way of representing a list of this value when+ -- printed; not all Dot values require this to be implemented.+ -- Defaults to Haskell-like list representation.+ unqtListToDot :: [a] -> DotCode+ unqtListToDot = brackets . hsep . punctuate comma+ . map unqtDot++ -- | The quoted form of 'unqtListToDot'; defaults to wrapping+ -- double quotes around the result of 'unqtListToDot' (since the+ -- default implementation has characters that must be quoted).+ listToDot :: [a] -> DotCode+ listToDot = doubleQuotes . unqtListToDot++instance PrintDot Int where+ unqtDot = int++instance PrintDot Double where+ unqtDot = double++instance PrintDot Bool where+ unqtDot True = text "true"+ unqtDot False = text "false"++instance PrintDot Char where+ unqtDot = char++ toDot = qtChar++ unqtListToDot = unqtString++ listToDot = qtString++-- | Check to see if this 'Char' needs to be quoted or not.+qtChar :: Char -> DotCode+qtChar c+ | restIDString c = char c -- Could be a number as well.+ | otherwise = doubleQuotes $ char c++-- | Escape quotes in Strings that need them.+unqtString :: String -> DotCode+unqtString str+ | isIDString str = text str+ | isNumString str = text str+ | otherwise = text $ escapeQuotes str++-- | Escape quotes and quote Strings that need them.+qtString :: String -> DotCode+qtString str+ | isIDString str = text str+ | isNumString str = text str+ -- Don't use unqtString as it re-runs isIDString+ | otherwise = doubleQuotes . text $ escapeQuotes str++instance (PrintDot a) => PrintDot [a] where+ unqtDot = unqtListToDot++ toDot = listToDot++wrap :: DotCode -> DotCode -> DotCode -> DotCode+wrap b a d = b <> d <> a++commaDel :: (PrintDot a, PrintDot b) => a -> b -> DotCode+commaDel a b = unqtDot a <> comma <> unqtDot b++printField :: (PrintDot a) => String -> a -> DotCode+printField f v = text f <> equals <> toDot v
TODO view
@@ -2,15 +2,8 @@ * Utilise the generic graph class once it is finalised. -* Allow user to choose whether or not the graph is meant to be- directed or undirected.--* Improve parsing to fully (or at least follow more closely) support Dot.--* Improve clustering/subgraph support.--* Use a PrettyPrinter rather than Show to generate Dot output.- * Improve Output support. * Find and fix the handle closing bug with graphvizWithHandle.++* Improve support for named Colors.
graphviz.cabal view
@@ -1,10 +1,10 @@ Name: graphviz-Version: 2999.1.0.2+Version: 2999.5.0.0 Stability: Beta Synopsis: GraphViz bindings for Haskell. Description: Provides convenient functions to convert FGL graphs into the Dot language used by the- GraphViz <http://graphviz.org/> suite of+ GraphViz (<http://graphviz.org/>) suite of programs with a large degree of customisation for layout, etc. @@ -33,15 +33,18 @@ process, array, fgl,- polyparse >= 1.1+ polyparse >= 1.1,+ pretty Exposed-Modules: Data.GraphViz Data.GraphViz.Types+ Data.GraphViz.Types.Parsing+ Data.GraphViz.Types.Printing Data.GraphViz.Commands Data.GraphViz.Attributes- Data.GraphViz.ParserCombinators Other-Modules: Data.GraphViz.Types.Clustering+ Data.GraphViz.Types.Internal Ghc-Options: -Wall Ghc-Prof-Options: -prof -auto-all