|
| 1 | +#lang racket |
| 2 | +(provide (all-defined-out)) |
| 3 | + |
| 4 | +(require "pretty-printer.rkt") |
| 5 | + |
| 6 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 7 | +;;;;; top-level graph |
| 8 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 9 | + |
| 10 | +; A graph is its name along with its list of contents |
| 11 | +; graph : String [Content] -> Graph |
| 12 | +(struct graph (name conts) #:transparent) |
| 13 | + |
| 14 | +; A subgraph is its name along with its list of contents |
| 15 | +; subgraph : String [Content] -> Graph |
| 16 | +(struct subgraph (name conts) #:transparent) |
| 17 | + |
| 18 | +; type Content = |
| 19 | +; | Node |
| 20 | +; | Edge |
| 21 | +; | ToMany ; Edge from one shared parent to many targets |
| 22 | +; | Attribute |
| 23 | +; | Subgraph |
| 24 | + |
| 25 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 26 | +;;;;; Graph internal structures |
| 27 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 28 | + |
| 29 | +;;;;; Nodes |
| 30 | + |
| 31 | +; type Node = |
| 32 | +; | String <- Just a node |
| 33 | +; | Symbol <- Just a node |
| 34 | +; | (String, [Property]) <- A node and some property |
| 35 | +; | SubG <- Subgraph (i.e. a set of nodes) |
| 36 | + |
| 37 | +; An edge defines the origin node and the target node by name |
| 38 | +; edge : Node Node [Property] -> Content |
| 39 | +(struct edge (orig targ props) #:transparent) |
| 40 | + |
| 41 | +; When one origin has many targets |
| 42 | +; edge : Node [Node] [Property] -> Content |
| 43 | +(struct to-many (orig targs props) #:transparent) |
| 44 | + |
| 45 | +; An attribute described whether it applies to the edges or |
| 46 | +; nodes and also has a list of properties |
| 47 | +; attr : Symbol [Property] -> Content |
| 48 | +(struct attr (type props) #:transparent) |
| 49 | + |
| 50 | +; A property is a parameter and its value |
| 51 | +(struct prop (par val) #:transparent) |
| 52 | + |
| 53 | +; Pair |
| 54 | +(struct pair (a b) #:transparent) |
| 55 | + |
| 56 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 57 | +;;;;; Constructors |
| 58 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 59 | + |
| 60 | +; Make an edge between two nodes with no properties |
| 61 | +; e : Node Node -> Content |
| 62 | +(define (e n1 n2) |
| 63 | + (edge n1 n2 '())) |
| 64 | + |
| 65 | +; Make edges between a list of nodes |
| 66 | +; es [Node] -> [Content] |
| 67 | +(define (es ns) |
| 68 | + (match ns |
| 69 | + [(cons n1 (cons n2 '())) (cons (edge n1 n2 '()) '())] |
| 70 | + [(cons n1 (cons n2 ns)) (cons (edge n1 n2 '()) (es (cons n2 ns)))] |
| 71 | + [_ (error "There must be at least 2 nodes given to es")])) |
| 72 | + |
| 73 | +(define (tm n1 ns) |
| 74 | + (to-many n1 ns '())) |
| 75 | + |
| 76 | +; Set a node to a specific shape |
| 77 | +; There are many, but we'll start with: |
| 78 | +; * box |
| 79 | +; * oval |
| 80 | +; * circle |
| 81 | +; * diamond |
| 82 | +; * rect |
| 83 | +; |
| 84 | +; shape : Symbol Node -> Node |
| 85 | +(define (shape sh node) |
| 86 | + (match node |
| 87 | + [(? string? n) (pair n (list (prop 'shape sh)))] |
| 88 | + [(? symbol? n) (pair (symbol->string n) (list (prop 'shape sh)))] |
| 89 | + [(pair n props) (pair n (cons (prop 'shape sh) props))])) |
| 90 | + |
| 91 | +; Attach meta-data to content, the parameter must be passed |
| 92 | +; as a label |
| 93 | +; attach : Symbol Symbol Content -> Content |
| 94 | +(define (attach param st cont) |
| 95 | + (match cont |
| 96 | + ; Nodes |
| 97 | + [(? string? n) (pair n (list (prop param st)))] |
| 98 | + [(? symbol? n) (pair (symbol->string n) (list (prop param st)))] |
| 99 | + [(pair n props) (pair n (cons (prop param st) props))] |
| 100 | + ; Edges |
| 101 | + [(edge o t props) (edge o t (cons (prop param st) props))] |
| 102 | + [(to-many o t props) (to-many o t (cons (prop param st) props))] |
| 103 | + ; Attribute |
| 104 | + [(attr t props) (attr t (cons (prop param st) props))])) |
| 105 | + |
| 106 | +; Color some Content |
| 107 | +; Known colors: |
| 108 | +; * red |
| 109 | +; * blue |
| 110 | +; * black |
| 111 | +; * darkgreen |
| 112 | +; * brown |
| 113 | +; * deeppink2 |
| 114 | +(define (color val cont) |
| 115 | + (attach 'color (string->symbol val) cont)) |
| 116 | + |
| 117 | +; Set the style of some contents |
| 118 | +; There are many, but we'll start with: |
| 119 | +; * dashed |
| 120 | +; * dotted |
| 121 | +; * bold |
| 122 | +; |
| 123 | +; style : Symbol Content -> Content |
| 124 | +(define (style st cont) |
| 125 | + (attach 'style st cont)) |
| 126 | + |
| 127 | +; Attach a label to Content |
| 128 | +(define (label lab cont) |
| 129 | + (attach 'label (string->symbol lab) cont)) |
| 130 | + |
| 131 | +; Attach a label to Content |
| 132 | +(define (label-str lab str) |
| 133 | + (attach 'label (string->symbol lab) (string->symbol str))) |
| 134 | + |
| 135 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 136 | +;;;;; Getters |
| 137 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 138 | + |
| 139 | +(define (get-direct-name cont) |
| 140 | + (match cont |
| 141 | + ; Nodes |
| 142 | + [(? string? n) (list n)] |
| 143 | + [(? symbol? n) (list n)] |
| 144 | + [(pair n props) (list n)] |
| 145 | + ; Edges |
| 146 | + [(edge o t props) '()] |
| 147 | + [(to-many o ts props) '()] |
| 148 | + ; Subgraph |
| 149 | + [(subgraph name cs) '()] |
| 150 | + ; Attribute |
| 151 | + [(attr t props) '()])) |
| 152 | + |
| 153 | + |
| 154 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 155 | +;;;;; Pretty-Printing |
| 156 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 157 | + |
| 158 | +; ppr-graph : Graph -> Seq |
| 159 | +(define (ppr-graph g) |
| 160 | + (match g |
| 161 | + [(graph n cs) |
| 162 | + (let ((header (+++ (str "digraph") (str n))) |
| 163 | + (body (vert (map ppr-cont cs)))) |
| 164 | + (+++ header (curl-ind body)))])) |
| 165 | + |
| 166 | +; ppr-cont : Content -> Seq |
| 167 | +(define (ppr-cont c) |
| 168 | + (match c |
| 169 | + ; Nodes |
| 170 | + [(? string? n) (str n)] |
| 171 | + [(? symbol? n) (sym n)] |
| 172 | + [(pair n props) (end-semi (+++ (str n) (ppr-props props)))] |
| 173 | + ; Edges |
| 174 | + [(edge o t props) (end-semi (ppr-to-target o t props))] |
| 175 | + [(to-many o ts props) |
| 176 | + (vert (map (lambda (x) (end-semi (ppr-to-target o x props))) ts))] |
| 177 | + ; Subgraph |
| 178 | + [(subgraph name cs) |
| 179 | + (let ((header (+++ (str "subgraph") (str name))) |
| 180 | + (body (vert (map (compose end-semi ppr-cont) cs)))) |
| 181 | + (+++ header (curl-ind body)))] |
| 182 | + ; Attribute |
| 183 | + [(attr t props) (end-semi (+++ (str t) (ppr-props props)))])) |
| 184 | + |
| 185 | +; Helper function for above |
| 186 | +(define (ppr-to-target o t props) |
| 187 | + (+++ (+-> (ppr-cont o) (ppr-cont t)) |
| 188 | + (ppr-props props))) |
| 189 | + |
| 190 | +; ppr-props : [Property] -> Seq |
| 191 | +(define (ppr-props ps) |
| 192 | + (match ps |
| 193 | + ['() (nil)] |
| 194 | + [ps (sqr (comma-sep (map ppr-prop ps)))])) |
| 195 | + |
| 196 | +; ppr-prop : Property -> Seq |
| 197 | +(define (ppr-prop p) |
| 198 | + (match p |
| 199 | + [(prop 'label val) (+= (sym 'label) (qt (sym val)))] |
| 200 | + [(prop par val) (+= (sym par) (sym val))])) |
0 commit comments