2009-02-01 02:07 UTC Creating Graphviz graphs from Ruby arrays

As part of my compiler project I wanted a way to visualize the programs, and since the syntax tree (so far at least) is represented with plain Ruby arrays I decided to throw together a script to use Graphviz to generate some graphs. I've written about using Graphviz previously here
The code does not make any assumptions tied to my compiler, but it's NOT an attempt at visualizing arbitrary object structures. You need to pass it an Array object, which can contain other arrays or objects that responds to #to_s.
NOTE: The code makes NO attempt to deal with structures that have loops - you'll run out of stack space soon enough if you try that. Feel free to post your fixes to do that in the comments (easy enough - just need to keep track of visited objects).

An example example. Given this:

 [:defun, :parse_quoted, [:c],
 [:while, [:and, [:ne, [:assign, :c, [:getchar]], -1], [:ne, :c, 34]], [:do,
  [:putchar, :c]
  ]
 ]
 ]


I generate this image (the gradients and shadows are thanks to my previously described XSL transform to pretty up the Graphviz SVG output):



The code is quite straightforward, though I'm not quite happy with the amount of monkey-patching. There were two easy choices: monkey-patching or lots of #is_a? calls, which made it horribly messy. I wouldn't advocate including this into a larger app without cleaning it up first, but as a quick hack it works well.

module ToDot
 def self.escape str
  str.gsub(/([<>{} |\])/) { "\"+$1 }
 end
end

class String def to_dot_label; '\"'+ToDot::escape(self)+'\"'; end end

class Array def to_dot_label; "..."; end

def to_dot_edge src, shorten " #{src}" + (shorten ? "" : ":#{object_id}") + " -> #{object_id};\n" end

def to_dot_subgraph return "" if nil ary = self[0].is_a?(Array) shorten = !ary && self[1..-1].detect{|o| !o.is_a?(Array)} == nil s = " #{object_id} [label=\"" if shorten s += self[0].to_dot_label + "\", shape=rect];\n" else s += collect { |o| "<#{o.object_id}> " + o.to_dot_label }.join("|") s += "\"];\n" end s += collect {|o| o.to_dot_edge(object_id,shorten) }.join s += collect {|o| o.to_dot_subgraph }.join s end end

class Object def to_dot_subgraph; end def to_dot_edge src, shorten; end def to_dot_label; ToDot::escape(to_s); end

def to_dot s = "digraph G {\n" s += " node [shape=record style=filled fillcolor=lightblue " s += "fontname=Verdana height=0.05 fontsize=10.0 ];\n" s += to_a.to_dot_subgraph s += "}\n" end end


As for how to use it:

require 'arytodot'

puts someArray.to_dot

Then pipe the output to "dot -Tsvg >output file" and use your favorite XSL processor to render an image from it. I used "rsvg file.svg file.png". If you want to use my XSL transform to pretty it up, follow the instructions in the article linked to above.

Here's the full "parser" example from my compiler series (click for the full size version):




Comments - Newest first

2009-03-31 18:51 UTC
The biggest problem would probably be that URL's are limited (to 1000 characters I believe).

Sounds like it'd be a nice option for a "pastebin" type of service, though, to have the pasted data processed in various ways.

2009-03-31 12:42 UTC
Oops, looked like your blog escaped my example. Trying again...

I had another though, and I'd really like to do this as a service, which is to post a service up in the cloud somewhere that would allow bloggers to use a IMG tag to embed diagrams. The SRC of the image would be like this:

SRC="http://abc.com/diagram/Customer-----*->Order"

2009-03-31 12:40 UTC
Thanks for the reply Vidar. The plugin is definately along the right lines.

Will look at the Javascript + Canvas option.

I had another though, and I'd really like to do this as a service, which is to post a service up in the cloud somewhere that would allow bloggers to do this:

And the result would be a little uml diagram :) What about that?

I'm looking for collaborators on developing the service, let me know if you're interested!

2009-03-31 12:27 UTC
Vidar Hokstad
Tobin,

There's a plugin for Trac that does this: http://trac-hacks.org/wiki/GraphvizPlugin

Trac is written in Python, but it might be a starting point.

In any case it's not all that hard since "dot" outputs to standard out by default, and reads from standard in, and if you use xsltproc for the XSL processing that can also read/write the standard streams.

The only downside is that "popen" is unidirectional and backtick only captures standard out so you might have to either write either the input or output to a file unless you want to mess around with fork and pipes.

The other "challenge" is that it's tricky at best to output SVG inline in a way that works cross browser (the best bet for cross browser inline images would be using javascript to draw on a canvas and use excanvas from Google Code to workaround IE's lack of support), so you'd probably want a separate URL endpoint that will have to parse out the dot code and do the processing mentioned above. Or you could have it do that on posting and write a static file somewhere.

Damn, now you've made me want to have inline dot support in my own blog - if I get time maybe I'll post some code (unless you beat me to it...)

2009-03-18 23:39 UTC
Looks great, I didn't even know you could style that stuff up.

What I *really* want to do is to be able to embed some DOT in my blog posts, and have a converter that detects it and replaces with an image like yours above.

Since you've been looking around this Ruby/GraphViz/Dot space, any idea where to start?

If the DOT thing goes, well, I'd like to create a DSL for automatically embedding UML in my blog posts and markdown documents. Eg:

Blog <>- 1 ------ * -> Post Post <><- * ------ * -> Tag

Thanks!

2009-02-03 07:44 UTC
slothbear
Nice. Useful on its own, and a good example to work from to create my own visuals. Thanks!
2009-02-01 19:07 UTC
Vidar Hokstad
Good catch - I'll have to look into that. In the meantime, the script is here
2009-02-01 00:06 UTC
David Brady
This is really interesting! Unfortunately, your source code did not survive the colorization process. Any chance you could post it without all the escapes taken out? I'd like to try it out but even hand-typing isn't giving any love.

Thanks!

Post a Comment

Basic HTML allowed. Javascript required for anti-spam check (I am testing a new anti-spam measure. Problems commenting? Please e-mail me: vidar@hokstad.com)

About me

E-mail: vidar@hokstad.com Skype: vhokstad
Twitter: vhokstad
View my LinkedIn profile.

I was born April 21st, 1975, in Oslo, Norway. Since 2000 I've been living in London, UK. I'm married and we just had our first child, Tristan Ikemefuna Hokstad.

I'm working for Aardvark Media as Director of Technology. I'm also currently on the board of SpatialQ, a startup in the GIS space, and an advisor to Skoach, a startup doing a time management app for people with ADD.

Twitter Updates

    follow me on Twitter