CL-DOT is a small package for easily generating dot (a program in the Graphviz suite) output from arbitrary Lisp data.

Once again it was time to generate some output suitable for dot. Nothing new, stuff that I've done at least four times in three languages (including twice in Lisp). But this time around the data I was going to graph was sufficiently complicated (at least a dozen different kinds of nodes) that it made sense to actually think about it, and not write an unextensible 50-line function to do the work.

There's a protocol of four generic functions, one of which must be defined for every object in the graph (OBJECT-NODE) and three others which are used for expressing different relations between objects in the graph (OBJECT-POINTS-TO, OBJECT-POINTED-TO-BY and OBJECT-KNOWS-OF). For example:

;; Conses
(defmethod cl-dot:object-node ((object cons))
  (make-instance 'cl-dot:node
                 :attributes '(:label "cell \\N"
                               :shape :box)))
(defmethod cl-dot:object-points-to ((object cons))
  (list (car object)
        (make-instance 'cl-dot:attributed
                       :object (cdr object)
                       ;; Make the CDR edges more important than the
                       ;; CAR edges
                       :attributes '(:weight 3))))
;; Symbols
(defmethod cl-dot:object-node ((object symbol))
  (make-instance 'cl-dot:node
                 :attributes `(:label ,object
                               :shape :hexagon
                               :style :filled
                               :color :black
                               :fillcolor "#ccccff")))

To generate a graph object for your data, call GENERATE-GRAPH. From the graph object you can either generate dot-format output to some stream with PRINT-GRAPH, or call dot directly on the data with DOT-GRAPH. For example:

(let* ((data '(a b c #1=(b z) c d #1#))
       (graph (cl-dot:generate-graph data)))
  (cl-dot:dot-graph graph "/tmp/test1ps"))

You can also specify attributes for the whole graph:

(let* ((data '(a b c #1=(b z) c d #1#))
       (graph (cl-dot:generate-graph data '(:rankdir "LR"))))
  (cl-dot:dot-graph graph "/tmp/test2ps"))

ASDF-INSTALL the package and read the README for more details.