graph-libs.scm 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/usr/bin/env guile
  2. !#
  3. (use-modules (ice-9 textual-ports)
  4. (ice-9 match)
  5. (ice-9 format)
  6. ((srfi srfi-1) #:select (append-map partition))
  7. (srfi srfi-9))
  8. ;; decl := edge | node | attr-decl | graph
  9. (define-record-type <edge>
  10. (make-edge src dst attrs)
  11. edge?
  12. (src edge-src)
  13. (dst edge-dst)
  14. (attrs edge-attrs))
  15. (define-record-type <node>
  16. (make-node id attrs)
  17. node?
  18. (id node-id)
  19. (attrs node-attrs))
  20. (define-record-type <attr-decl>
  21. (make-attr-decl kind attrs)
  22. attr-decl?
  23. (kind attr-decl-kind) ; 'node 'graph or 'edge
  24. (attrs attr-decl-attrs))
  25. (define-record-type <graph>
  26. (make-graph id decls attrs)
  27. graph?
  28. (id graph-id)
  29. (decls graph-decls)
  30. (attrs graph-attrs))
  31. (define (compute-node-attrs name)
  32. '())
  33. (define (module->decls name imports)
  34. (cons (make-node name (compute-node-attrs name))
  35. (map (lambda (mod)
  36. (make-edge name mod '((headport . n)
  37. (tailport . s))))
  38. imports)))
  39. (define (visit-r6rs-library name imports)
  40. ;; fixme: versions
  41. (define (import-name spec)
  42. (match spec
  43. (('only spec . _) (import-name spec))
  44. (('rename spec . _) (import-name spec))
  45. (('except spec . _) (import-name spec))
  46. (('prefix spec _) (import-name spec))
  47. (('library name) name)
  48. (spec spec)))
  49. (module->decls name (map import-name imports)))
  50. (define (visit-guile-library name imports)
  51. (define (import-name spec)
  52. (match spec
  53. ((name #:select _) name)
  54. ((name #:hide _) name)
  55. ((name #:prefix _) name)
  56. ((name #:renamer _) name)
  57. (name name)))
  58. (module->decls name (map import-name imports)))
  59. (define (visit-file file)
  60. (call-with-input-file file
  61. (lambda (port)
  62. (match (read port)
  63. (('library name exports ('import . specs) . body)
  64. (visit-r6rs-library name specs))
  65. (('define-module name . args)
  66. (let lp ((args args) (imports '()) (pure? #f))
  67. (match args
  68. (()
  69. (let ((imports (if pure? imports (cons '(guile) imports))))
  70. (visit-guile-library name imports)))
  71. ((#:pure . args) (lp args imports #t))
  72. ((#:use-module spec . args) (lp args (cons spec imports) pure?))
  73. ((#:export exports . args) (lp args imports pure?))
  74. (_ (error "unexpected define-module args" args)))))
  75. (expr
  76. (format (current-error-port) "~a: not a recognized library\n" file)
  77. '())))))
  78. (define (write-graph graph)
  79. (define (id-repr id)
  80. (match id
  81. (#f #f)
  82. ((? string?) id)
  83. (_ (object->string id))))
  84. (define (write-attr attr)
  85. (match attr
  86. ((k . v) (format #t "~s=~s;" (id-repr k) (id-repr v)))))
  87. (define (write-attr-stmt attr)
  88. (write-attr attr)
  89. (newline))
  90. (define (write-attr-list attrs)
  91. (unless (null? attrs)
  92. (format #t " [")
  93. (for-each write-attr attrs)
  94. (format #t "]")))
  95. (define (write-endpoint ep)
  96. (match ep
  97. (($ <graph>) (write-decl ep))
  98. (id (format #t "~s" (id-repr id)))))
  99. (define (write-decl decl)
  100. (match decl
  101. (($ <node> id attrs)
  102. (format #t "~s" (id-repr id))
  103. (write-attr-list attrs)
  104. (format #t ";\n"))
  105. (($ <edge> src dst attrs)
  106. (write-endpoint src)
  107. (format #t " -> ")
  108. (write-endpoint dst)
  109. (write-attr-list attrs)
  110. (format #t ";\n"))
  111. (($ <attr-decl> kind attrs)
  112. (format #t "~a" kind)
  113. (write-attr-list attrs)
  114. (format #t ";\n"))
  115. (($ <graph> id decls attrs)
  116. (format #t "subgraph ~@[~s ~]{\n" (id-repr id))
  117. (for-each write-attr-stmt attrs)
  118. (for-each write-decl decls)
  119. (format #t "}\n"))))
  120. (match graph
  121. (($ <graph> id decls attrs)
  122. (format #t "strict digraph ~@[~s ~]{\n" (id-repr id))
  123. (for-each write-attr-stmt attrs)
  124. (for-each write-decl decls)
  125. (format #t "}\n"))))
  126. (define (compute-graph decls)
  127. (define colors
  128. '(indianred steelblue limegreen aquamarine purple gold lightgrey hotpink))
  129. (define attributed-colors (make-hash-table))
  130. (define (get-color id)
  131. (or (hash-ref attributed-colors id)
  132. (let ((color (car colors)))
  133. (set! colors (cdr colors))
  134. (hash-set! attributed-colors id color)
  135. color)))
  136. (define (add-node-attrs id attrs)
  137. (match id
  138. ((id0 . id+)
  139. (append (match id+
  140. ((id1) `((label . ,id1)))
  141. (_ '()))
  142. `((color . ,(get-color id0))
  143. (style . filled)
  144. (shape . box))
  145. attrs))))
  146. (call-with-values (lambda () (partition node? decls))
  147. (lambda (nodes decls)
  148. (define node-defs (make-hash-table))
  149. (for-each (match-lambda
  150. (($ <node> id attrs)
  151. (hash-set! node-defs id #t)))
  152. nodes)
  153. (define synthesized-nodes '())
  154. (define (maybe-synthesize! id)
  155. (unless (hash-ref node-defs id)
  156. (hash-set! node-defs id #t)
  157. (set! synthesized-nodes
  158. (cons (make-node id (add-node-attrs id '()))
  159. synthesized-nodes))))
  160. (for-each (match-lambda
  161. (($ <edge> src dst attrs)
  162. (maybe-synthesize! src)
  163. (maybe-synthesize! dst)))
  164. decls)
  165. (make-graph
  166. #f
  167. (append synthesized-nodes
  168. (map (lambda (node)
  169. (match node
  170. (($ <node> id attrs)
  171. (make-node id (add-node-attrs id attrs)))))
  172. nodes)
  173. decls)
  174. '((concentrate . true)
  175. (nodesep . "0.02"))))))
  176. (when (batch-mode?)
  177. (match (program-arguments)
  178. ((arg0)
  179. (format (current-error-port) "usage: ~a FILE...\n" arg0)
  180. (exit 1))
  181. ((arg0 . libs)
  182. (write-graph (compute-graph (append-map visit-file libs))))))