graph-libs.scm 6.5 KB

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