#!/usr/bin/env python import os import StringIO import string import re re_block_comment = re.compile(r"^(.*)(/\*\*.*?\*/)(.*)$", re.M) re_block_comment_2 = re.compile(r"^(.*)(/\*.*?\*/)(.*)$", re.M) def split_type_and_name(x): x = x.strip() name = x.split(" ")[-1] type_ = x[0 : len(x) - len(name)].strip() return name, type_ def unpointer(x): while x.endswith("*"): x = x[ : -1] return x def lower_class_name(x): result = ("".join(("_" + item.lower()) if item in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" else item for item in x))[1:] return result def format_parameters(parameters): if parameters: return "(%s)" % ", ".join(["%s : %s" % (name_, format_type(type_)) for name_, type_ in parameters]) else: return "" # aaaargh def remove_comments(text): text = re_block_comment.subn(r"\1\3", text)[0] text = re_block_comment_2.subn(r"\1\3", text)[0] return text def remove_comments(text): result = StringIO.StringIO() state = 0 for c in text: if state == 0: # "" if c == "/": state = 1 else: state = 0 result.write(c) elif state == 1: # "/" if c == "*": state = 2 else: state = 0 result.write("/") elif state == 2: # "/*" if c == "*": state = 3 else: state = 2 elif state == 3: # "/*"..."*" if c == "/": state = 0 elif c == "*": #result.write("*") state = 3 else: #result.write("*") state = 2 return result.getvalue() def format_type(type_): return type_.replace("struct ", "").replace("*", "").replace("const ", "").replace("unsigned ", "u") def add_dummy_parameter(x): return x[0], x[1], None print """ digraph G { fontname = "Bitstream Vera Sans" fontsize = 8 node [ fontname = "Bitstream Vera Sans" fontsize = 8 shape = "record" ] edge [ fontname = "Bitstream Vera Sans" fontsize = 8 ] """ # TODO parse structs, ignore functions. # TODO what about typedefs? classes = {} superclasses = [] in_struct = None names = ["../%s" % name for name in os.listdir("..")] for name in names: if not os.path.isfile(name): continue if not name.endswith(".h") and not name.endswith(".c"): continue if name.endswith("view.c") or name.endswith("view.h"): # unused continue B_is_header = name.endswith(".h") f = file(name, "rb") text = f.read() f.close() text = remove_comments(text) assert(text.find("/*") == -1) f = StringIO.StringIO(text) for line in f.readlines(): if in_struct: if line.find("}") > -1: entries = struct_body.getvalue().split("\n") q = [ add_dummy_parameter(split_type_and_name(entry.replace(";", "").strip())) for entry in entries if entry.strip() != ""] for entry in q: if entry[0] == "super": super_type = entry[1] if super_type.startswith("struct "): super_type = super_type[len("struct ") : ] superclasses.append((in_struct, super_type.replace("*", ""))) elif not entry[0].startswith("__pad"): classes[in_struct].append(entry) in_struct = None elif line.strip() != "": struct_body.write(line) elif line.find("(") > -1: # function, maybe if not B_is_header: continue i = line.find("(") prefix = line[ : i].strip() prefix_parts = prefix.split(" ") if len(prefix_parts) >= 2: name = prefix_parts[-1] result_type = prefix[ : len(prefix) - len(name)].strip() while True: if result_type.startswith("static "): result_type = result_type[len("static ") : ] elif result_type.startswith("inline "): result_type = result_type[len("inline ") : ] else: break name = name.strip() parameter_declaration = line[i + 1 : ] i = parameter_declaration.find(")") assert(i > -1) parameter_declaration = parameter_declaration[ : i] parameters = map(string.strip, parameter_declaration.split(",")) parameters = map(split_type_and_name, parameters) if len(parameters) > 0: maybe_class_name = unpointer(parameters[0][1]) if maybe_class_name.startswith("struct "): maybe_class_name = maybe_class_name[len("struct ") : ].strip() if maybe_class_name in classes: if name.startswith(maybe_class_name + "_"): name = name[len(maybe_class_name + "_") : ] lowered_class_name = lower_class_name(maybe_class_name) + "_" if name.startswith(lowered_class_name): name = name[len(lowered_class_name) : ] if lowered_class_name.endswith("_item_"): lowered_class_name = lowered_class_name[ : len(lowered_class_name) - len("item_")] if name.startswith(lowered_class_name): name = name[len(lowered_class_name) : ] classes[maybe_class_name].append((name, result_type, parameters)) elif line.startswith("struct"): # class decl line = line[len("struct") : ].strip() if line.find("{") > -1: name = line[ : line.find("{")].strip() in_struct = name struct_body = StringIO.StringIO() else: assert(line.find(";") > -1) name = line[ : line.find(";")] name = name.strip() if name.find(" ") > -1: # variable declaration. continue if name not in classes: classes[name] = [] f.close() for class_name, class_declaration in classes.items(): formatted_attributes = "".join(sorted(["%s %s%s : %s\\l" % ("+" if parameters else "#", name, format_parameters(parameters), format_type(type_)) for name, type_, parameters in class_declaration])) print """%(class_name)s [ label = "{%(class_name)s|%(formatted_attributes)s}" ]""" % locals() print """ edge [ arrowhead = "empty" ] """ for class_name, super_class_name in superclasses: print "%s -> %s" % (class_name, super_class_name) print """ edge [ arrowhead = "none" headlabel = "1..1" taillabel = "1..1" ] """ for class_name, class_declaration in classes.items(): for name_, type_, parameters_ in class_declaration: if name_ == "super": continue if not parameters_: # attribute formatted_type = format_type(type_) if formatted_type in classes: print "%s -> %s" % (class_name, formatted_type) #print "View -> Buffer" print "}"