https://github.com/tkych/donuts.git
git clone 'https://github.com/tkych/donuts.git'
(ql:quickload :donuts)
Last Updated : 2012/06/29 21:28:17 tkych
Version : 0.3.1 (beta)
Donuts converts a graph represented by S-expression to the image. How to use donuts is simple. <> creates a node. → puts an edge two nodes. && makes a graph by bundling some nodes, edges and graphs. $$ outputs an image of the graph.
For further details, please see index (Under Translation) or index-ja (Japanease) in doc directory.
Graphviz is a collection of library and utility for drawing a graph. Dot language is description language, used in Graphviz. Graphviz is very useful. However, I (as a lisp programmer) think there are some points to do kaizen.
Since dot language is not Turing-complete, when we draw a graph, we don't take full advantage of the pattern in the graph.
Because dot language is so-called compiled language, development cycle is inconvenient.
Plain Common Lisp does not have ability to draw graph.
The goal of donuts is to draw graph in lispic way of thought (REPL, macro, CLOS, multi-paradigm style, and so on).
Graphviz by AT&T Labs
CL-PPCRE by Dr. Edmund Weitz
Trivial-Shell by Gary Warren King
(ql:quickload :donuts)
(in-package :donuts)
(dot-output (&& (-> 1 2)))
;output dot code in standard-output($$ (&& (-> 1 2)))
;output graph image to viewer(<> label) => node
(-> node1 node2) => edge
(&& . nodes-edges-graphs) => graph
($$ graph) => NIL ;output image to viewer
(DOT-OUTPUT graph) => NIL ;output dot code
DONUTS> (dot-output
(& (:label "example")
(-> (<> "a" :shape :box) "b" :color :red)))
digraph graph_ID_41 {
label="example";
node_ID_39 [label="a",shape=box];
node_ID_39 -> "b" [color=red];
}
NIL
DONUTS> ;; Example from http://graphviz.org/content/cluster
($ (:outfile "cluster.pdf")
(&& ([&] (:label "process #1" :style :filled :color :lightgrey)
(with-node (:style :filled :color :white)
(--> "a0" "a1" "a2" "a3")))
([&] (:label "process #2" :color :blue)
(with-node (:style :filled)
(--> "b0" "b1" "b2" "b3")))
(->> (<> "start" :shape :Mdiamond) "a0" "b0")
(==> "a3" "b3" (<> "end" :shape :Msquare))
(-> "a1" "b3")
(-> "a3" "a0")
(-> "b2" "a3")))
; Create cluster.pdf & Output image to viewer
NIL
DONUTS>
;; Example from http://www.linuxjournal.com/article/7275
;; num-day: total number of days in month
;; starting-day: 0 as Sun, 1 as Mon, ... , 6 as Sat
(defun generate-monthly-calendar (month year num-days starting-day)
(let ((month (generate-month-nodes month year))
(luminary7 (generate-luminary7-nodes))
(days (generate-day-nodes num-days starting-day)))
(apply #'&& (loop :for week :in (cons luminary7 (group days 7))
:collect (apply #'--> month week)))))
(defun generate-month-nodes (month year)
(<> (format nil "~@(~A~)\\n~D" month year) :shape :Msquare))
(defun generate-luminary7-nodes ()
(loop :for day :in '("Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat")
:collect (<> day :shape :egg :style :filled :color :lightgray)))
(defun generate-day-nodes (num-days starting-day)
(loop :for day :in (nconc (loop :repeat starting-day :collect "")
(loop :for d :from 1 :to num-days :collect d)
(loop :repeat (- (* 7 (if (and (= 28 num-days) (= 0 starting-day))
4 5)) ;for Feb starting Sun in common year
starting-day num-days)
:collect ""))
:collect (<> day :shape :box)))
;; from On Lisp, e.g. (group '(1 2 3 4) 2) => ((1 2) (3 4))
(defun group (lst n)
(if (zerop n) (error "zero length"))
(labels ((rec (lst acc)
(let ((rest (nthcdr n lst)))
(if (consp rest)
(rec rest (cons (subseq lst 0 n)
acc))
(nreverse (cons lst acc))))))
(if lst (rec lst nil) nil)))
($$ (& (:size "8,6":rankdir :LR)
(generate-monthly-calendar 'may 2012 31 2)))
; Output Calendar to Viewer
NIL
DONUTS>
;; Example from http://www.graphviz.org/doc/info/html2.gv
($$ (& (:rankdir :LR)
(with-node (:shape :plaintext)
(let ((a (<> (html (table :border 0 :cellborder 1 :cellspacing 0
(tr (td :rowspan 3 :bgcolor :yellow "class"))
(tr (td :port "here" :bgcolor :lightblue "qualfier"))))))
(b (<> (html (table :bgcolor :bisque
(tr (td :colspan 3 "elephant")
(td :rowspan 2 :bgcolor :chartreuse
:valign :bottom :align :right "two"))
(tr (td :colspan 2 :rowspan 2
(table :bgcolor :grey
(tr (td "corn"))
(tr (td :bgcolor :yellow "c"))
(tr (td "f"))))
(td :bgcolor :white "penguin"))
(tr (td :colspan 2 :border 4 :align :right :port "there" "4"))))
:shape :ellipse :style :filled))
(c (<> (html "long line 1" (br) "line 2" (br :align :left) "line 3" (br :align :right))))
(d (<> "d" :shape :triangle)))
(&&
(~ b c)
(-> (@ a :here) (@ b :there) :dir :both :arrowtail :diamond)
(-> c b)
(-> d c :label (html (table (tr (td :bgcolor :red :width 10)
(td "Edge labels" (br) "also")
(td :bgcolor :blue :width 10))))))))))
; Output example to Viewer
NIL
<donuts-code> ::= '('<output-op> <graph>')'|<node>|<edge>|<graph>|<html-like-label>|<tag>|<common-lisp-code>
<output-op> ::= 'dot-output'|'dot-pprint'|'$$'|'$' <attr-list>
<attr-list> ::= '('{<attr>}')'
<attr> ::= <attr-keyword> <attr-value>
<graph> ::= '(&&' <graph-elts>')'|'(&' <attr-list> <graph-elts>')'|<cluster>
<graph-elts> ::= nil|<pre-node>|<node>|<edge>|<graph>|<cluster>|<rank>|<with>|<graph-elts>{ <graph-elts>}
<cluster> ::= '([&]' <attr-list> <graph-elts>')'
<pre-node> ::= <number>|<string>
<node> ::= <pre-node>|'(<>' (<pre-node>|<html-like-label>){ <attr>}')'|<record>|'(@'<node> <port>[ <port>]')'
<record> ::= '([] "'<record-label>'"'{ <attr>}')'
<record-label> ::= <field>{'|'<field>}
<field> ::= [<filed-port> ]{char}|'{'<record-label>'}'
<filed-port> ::= <keyword>
<port> ::= <compass-port>|<filed-port>
<compass-port> ::= :n|:ne|:e|:se|:s|:sw|:w|:nw|:c|:_
<edge> ::= '('<edge-cons> <node> <node>{ <attr>}')'|'('<multi-edge-cons>{ <node>}')'|'(?' <node>{ <attr>}')'
<edge-cons> ::= '->'|'--'
<multi-edge-cons> ::= '-->'|'->>'|'---'|'-<'|'O'
<rank> ::= '(rank' <rank-keyword>{ <node>}')'|'(~'{ <node>}')'
<rank-keyword> ::= :same|:min|:max|:source|:sink
<with> ::= '('<with-op> <attr-list> <graph-elts> ')'
<with-op> ::= 'with-node'|'with-edge'
<html-like-label> ::= '(html'{ <tag>| <txt>}')'
<txt> ::= <string>|<number>
<tag> ::= '('<tag-cons> <tag-body>')'
<tag-cons> ::= 'table'|'font'|'i'|'b'|'u'|'sub'|'sup'|'br'|'hr'|'tr'|'vr'|'td'|'img'
<tag-body> ::= <tag>|<attr>|<txt>|<tag-body>{ <tag-body>}
Takaya OCHIAI <tkych.repl@gmail.com>
MIT License
Copyright (C) 2012 Takaya OCHIAI