File: Internal.hs

package info (click to toggle)
haskell-graphviz 2999.17.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,488 kB
  • sloc: haskell: 12,152; makefile: 2
file content (153 lines) | stat: -rw-r--r-- 5,195 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK hide #-}

{- |
   Module      : Data.GraphViz.Attributes.Internal
   Description : Internal Attribute value definitions
   Copyright   : (c) Ivan Lazar Miljenovic
   License     : 3-Clause BSD-style
   Maintainer  : Ivan.Miljenovic@gmail.com

   This module is defined so as to avoid exposing internal functions
   in the external API.  These are those that are needed for the
   testsuite.

 -}

module Data.GraphViz.Attributes.Internal
       ( PortName(..)
       , PortPos(..)
       , CompassPoint(..)
       , compassLookup
       , parseEdgeBasedPP
       ) where

import Data.GraphViz.Parsing
import Data.GraphViz.Printing

import           Data.Map       (Map)
import qualified Data.Map       as Map
import           Data.Maybe     (isNothing)
import           Data.Text.Lazy (Text)

-- -----------------------------------------------------------------------------

-- Note that printing and parsing of PortName values is specific to
-- where it's being used: record- and HTML-like labels print/parse
-- them differently from when they're on they're part of PortPos; the
-- default printing and parsing is done for the latter.

-- Should this really be exported from here?  Or in another common module?

-- | Specifies a name for ports (used also in record-based and
--   HTML-like labels).  Note that it is not valid for a 'PortName'
--   value to contain a colon (@:@) character; it is assumed that it
--   doesn't.
newtype PortName = PN { portName :: Text }
                 deriving (Eq, Ord, Show, Read)

instance PrintDot PortName where
  unqtDot = unqtDot . portName

  toDot = toDot . portName

instance ParseDot PortName where
  parseUnqt = PN <$> parseEscaped False [] ['"', ':']

  parse = quotedParse parseUnqt
          `onFail`
          unqtPortName

unqtPortName :: Parse PortName
unqtPortName = PN <$> quotelessString

-- -----------------------------------------------------------------------------

data PortPos = LabelledPort PortName (Maybe CompassPoint)
             | CompassPoint CompassPoint
             deriving (Eq, Ord, Show, Read)

instance PrintDot PortPos where
  unqtDot (LabelledPort n mc) = unqtDot n
                                <> maybe empty (colon <>) (fmap unqtDot mc)
  unqtDot (CompassPoint cp)   = unqtDot cp

  toDot (LabelledPort n Nothing) = toDot n
  toDot lp@LabelledPort{}        = dquotes $ unqtDot lp
  toDot cp                       = unqtDot cp

instance ParseDot PortPos where
  parseUnqt = do n <- parseUnqt
                 mc <- optional $ character ':' >> parseUnqt
                 return $ if isNothing mc
                          then checkPortName n
                          else LabelledPort n mc

  parse = quotedParse parseUnqt
          `onFail`
          fmap checkPortName unqtPortName

checkPortName    :: PortName -> PortPos
checkPortName pn = maybe (LabelledPort pn Nothing) CompassPoint
                   . (`Map.lookup` compassLookup)
                   $ portName pn

-- | When attached to a node in a DotEdge definition, the 'PortName'
--   and the 'CompassPoint' can be in separate quotes.
parseEdgeBasedPP :: Parse PortPos
parseEdgeBasedPP = liftA2 LabelledPort parse (fmap Just $ character ':' *> parse)
                   `onFail`
                   parse

data CompassPoint = North
                  | NorthEast
                  | East
                  | SouthEast
                  | South
                  | SouthWest
                  | West
                  | NorthWest
                  | CenterPoint
                  | NoCP
                  deriving (Eq, Ord, Bounded, Enum, 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
  -- Have to take care of longer parsing values first.
  parseUnqt = oneOf [ stringRep NorthEast "ne"
                    , stringRep NorthWest "nw"
                    , stringRep North "n"
                    , stringRep SouthEast "se"
                    , stringRep SouthWest "sw"
                    , stringRep South "s"
                    , stringRep East "e"
                    , stringRep West "w"
                    , stringRep CenterPoint "c"
                    , stringRep NoCP "_"
                    ]

compassLookup :: Map Text CompassPoint
compassLookup = Map.fromList [ ("ne", NorthEast)
                             , ("nw", NorthWest)
                             , ("n", North)
                             , ("e", East)
                             , ("se", SouthEast)
                             , ("sw", SouthWest)
                             , ("s", South)
                             , ("w", West)
                             , ("c", CenterPoint)
                             , ("_", NoCP)
                             ]

-- -----------------------------------------------------------------------------