1 #!/usr/bin/env python 2 3 """ 4 A module providing elementary graph support for programs. 5 6 Copyright (C) 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from micropython.data import Class, Function 23 from micropython.errors import TableError 24 import sys, os 25 26 def get_graph(program, out=None, filename=None): 27 28 """ 29 Using the importer maintained by the given 'program', write out the graph 30 definition for consumption by the Graphviz tools such as the dot program. 31 32 If the optional 'out' parameter is set to a stream, that stream will be 33 used to write out the graph definition; otherwise, standard output will be 34 used. 35 36 If the optional 'filename' is specified, the leafname minus extension will 37 be used as the name of the graph. 38 39 A command line like the following can be used to produce viewable graph 40 files: 41 42 dot -T svg -o output.svg output.dot 43 """ 44 45 filename = filename and os.path.splitext(os.path.split(filename)[-1])[0] or "program" 46 47 importer = program.get_importer() 48 objtable = program.get_object_table() 49 50 out = out or sys.stdout 51 print >>out, 'digraph %s {' % filename 52 print >>out, ' ratio=auto;' 53 print >>out, ' center=true;' 54 print >>out, ' rankdir=LR;' 55 56 for from_name, attributes in importer.inferred_name_references.items(): 57 from_attribute = get_unit(objtable, from_name) 58 if from_attribute: 59 for objname, attrname in attributes: 60 if not have_unit(objtable, objname, attrname): 61 continue 62 colour = get_colour(from_name, objname, attrname, importer) 63 print >>out, ' "%s" -> "%s.%s" [color="%s"];' % (from_name, objname, attrname, colour) 64 65 print >>out, "}" 66 67 def get_colour(from_name, objname, attrname, importer): 68 try: 69 if (objname, attrname) in importer.specific_name_references[from_name]: 70 return "red" 71 except KeyError: 72 pass 73 return "black" 74 75 def get_unit(objtable, attribute): 76 t = attribute.rsplit(".", 1) 77 try: 78 objname, attrname = t 79 if not have_unit(objtable, objname, attrname, Function): 80 return None 81 return attribute 82 except ValueError: 83 return t[0] 84 85 def have_unit(objtable, objname, attrname, allowed_types=None): 86 allowed_types = allowed_types or (Class, Function) 87 try: 88 attr = objtable.access(objname, attrname) 89 except TableError: 90 #print >>sys.stderr, "%s.%s not found!" % (objname, attrname) 91 return False 92 93 for value in attr.get_values(): 94 if isinstance(value, allowed_types): 95 return True 96 return False 97 98 # vim: tabstop=4 expandtab shiftwidth=4