diff --git a/class_decriptor.py b/class_decriptor.py index a6a1870..c8f1ffd 100644 --- a/class_decriptor.py +++ b/class_decriptor.py @@ -1,97 +1,98 @@ class Typename: def __init__(self,name,encapsulation): self.name = name self.encapsulation = encapsulation class Method: def __init__(self,name,args,ret,encapsulation,virtual,static): self.name = name self.virtual = virtual self.static = static self.args = dict() for k,v in args: self.args[v] = k self.ret = ret self.encapsulation = encapsulation if self.encapsulation == '': self.encapsulation = 'public' def __str__(self): sstr = self.encapsulation + " " if not self.virtual == '': sstr += self.virtual + " " sstr += self.ret + " " + self.name + "(" pairs = list(self.args.iteritems()) for k,_type in pairs[:-1]: sstr += _type + " " + k + ", " sstr += _type + " " + k + ")" return sstr class Member: - def __init__(self,name,_type,encapsulation): + def __init__(self,name,_type,encapsulation,static): self.name = name self.type = _type self.encapsulation = encapsulation + self.static = static def __str__(self): return self.encapsulation + " " + self.type + " " + self.name class ClassDescriptor: def __init__(self,name,inheritance=None): self.name = name self.inheritance = inheritance self.members = {'private':{},'public':{},'protected':{}} self.methods = {'private':{},'public':{},'protected':{}} self.types = {'private':{},'public':{},'protected':{}} def addMethod(self,name,args,ret,encapsulation,virtual,static): new_method = Method(name,args,ret,encapsulation,virtual,static) if name not in self.methods[encapsulation]: self.methods[encapsulation][name] = [] self.methods[encapsulation][name].append(new_method) - def addMember(self,name,_type,encapsulation): - new_member = Member(name,_type,encapsulation) + def addMember(self,name,_type,encapsulation,static): + new_member = Member(name,_type,encapsulation,static) self.members[encapsulation][name] = new_member def addType(self,name,encapsulation): new_type = Typename(name,encapsulation) self.types[encapsulation][name] = new_type def getMembers(self,encapsulation=None): return self.members[encapsulation] def getTypes(self,encapsulation=None): return self.types[encapsulation] def getMethods(self,encapsulation=None): return self.methods[encapsulation] def __str__(self): sstr = "Class " + self.name + "\n" if (self.inheritance): sstr += "Inherit: " sstr += ",".join(self.inheritance) + "\n" sstr += "Methods:\n" for encaps,meths in self.methods.iteritems(): sstr += encaps + ":\n" for name,m_list in meths.iteritems(): for m in m_list: sstr += str(m) + "\n" sstr += "\n" sstr += "Members:\n" for encaps,membs in self.members.iteritems(): sstr += encaps + ":\n" for name,m in membs.iteritems(): sstr += str(m) + "\n" return sstr if __name__ == '__main__': my_class = ClassDescriptor('dummy') my_class.addMethod('compute',[('int','arg1'),('double','arg2')],'bool','public','') my_class.addMember('res','double','private') print my_class diff --git a/class_dumper_dot.py b/class_dumper_dot.py index 064943b..99626a7 100644 --- a/class_dumper_dot.py +++ b/class_dumper_dot.py @@ -1,190 +1,196 @@ from class_dumper import ClassDumper class ClassDumperDOT(ClassDumper): def __init__(self): ClassDumper.__init__(self) self.encaps_symbol = {'public':'+','private':'-','protected':'#'} self.base_types = ['int','double', 'float', 'unsigned int'] self.base_types = self.base_types + [e + ' *' for e in self.base_types] + [e + ' &' for e in self.base_types] - print self.base_types def dump(self,class_file,output_filename): sstr = 'digraph "test"\n{' sstr += """ edge [fontname="Helvetica",fontsize="10",labelfontname="Helvetica",labelfontsize="10"]; node [fontname="Helvetica",fontsize="10",shape=record]; """ sstr += ClassDumper.dump(self,class_file) sstr += "}" with open(output_filename,'w') as f: f.write(sstr) f.close() def dumpFile(self,c): sstr = self.formatClassDeclaration(c) sstr += self.formatConstructors(c) sstr += self.formatMethods(c) sstr += self.formatMembers(c) sstr += '}"];\n' sstr += self.formatInheritance(c) sstr += self.formatCompositions(c) sstr += self.formatTypes(c) return sstr def formatClassDeclaration(self,c): sstr = '"{0}" '.format(c.name) sstr += '[label="{' + format(c.name) + "\\n" return sstr def formatInheritance(self,c): if c.inheritance is not None: sstr = "" for mother in c.inheritance: sstr += '"{0}" '.format(mother) + " -> " + '"{0}" '.format(c.name) sstr += '[style="solid",color="midnightblue",fontname="Helvetica",arrowtail="onormal",fontsize="10",dir="back"];\n' return sstr return "" def formatConstructors(self,c): sstr = "" for encaps in ['public','private', 'protected']: meths = c.getMethods(encaps) if c.name in meths: if sstr == "": sstr = "|" sstr += self.encaps_symbol[encaps] + " " for cons in meths[c.name]: sstr += self.formatMethod(c,cons) sstr += "\\l" if '~' + c.name in meths: if sstr == "": sstr = "|" sstr += self.encaps_symbol[encaps] + " " for cons in meths['~' + c.name]: sstr += self.formatMethod(c,cons) sstr += "\\l" return sstr def formatMethods(self,c): sstr = "" for encaps in ['public','private', 'protected']: meths = c.getMethods(encaps) meths_names = set(meths.keys()) - set([c.name,'~'+c.name]) meths_names = list(meths_names) if len(meths_names) is not 0: for n in meths_names: for m in meths[n]: if sstr == "": sstr = "|" sstr += self.encaps_symbol[encaps] + " " sstr += self.formatMethod(c,m) sstr += "\\l" return sstr def formatMembers(self,c): sstr = "" for encaps in ['public','private', 'protected']: membs = c.getMembers(encaps) if len(membs) is not 0: for n,m in membs.iteritems(): if sstr == "": sstr = "|" sstr += self.encaps_symbol[encaps] + " " sstr += self.formatMember(c,m) sstr += "\\l" return sstr def formatCompositions(self,c): - sstr = "" - + composition_set = set() for encaps in ['public','private', 'protected']: membs = c.getMembers(encaps) if len(membs) is not 0: for n,m in membs.iteritems(): if m.type in self.base_types: continue - sstr += '"{0}" '.format(m.type) + " -> " + '"{0}" '.format(c.name) - sstr += '[style="dashed",color="midnightblue",fontname="Helvetica",arrowtail="odiamond",fontsize="10",dir="back"];\n' + composition_set.add(m.type) + + sstr = "" + for t in composition_set: + sstr += '"{0}" '.format(t) + " -> " + '"{0}" '.format(c.name) + sstr += '[style="dashed",color="midnightblue",fontname="Helvetica",arrowtail="odiamond",fontsize="10",dir="back"];\n' + + return sstr def formatTypes(self,c): sstr = "" for encaps in ['public','private', 'protected']: if c.types[encaps] is not None: for t in c.types[encaps]: if t in self.base_types: continue sstr += '"{0}" '.format(c.name) + " -> " + '"{0}" '.format(t) sstr += '[style="solid",color="black",fontname="Helvetica",arrowtail="odiamond",fontsize="10",dir="back"];\n' return sstr return "" def formatMethod(self,c,m): arg_types = list(m.args.iteritems()) arg_types = [a for b,a in arg_types] sstr = "" if m.static: sstr += m.static + " " if m.ret: sstr += m.ret + " " sstr += m.name + "(" + ",".join(arg_types) + ")" if m.virtual == 'pure virtual': sstr += "=0" return sstr def formatMember(self,c,m): - return m.type + " " + m.name + sstr = "" + if m.static == 'static': sstr += 'static ' + return sstr + m.type + " " + m.name ################################################################ import argparse import subprocess import os if __name__ == '__main__': parser = argparse.ArgumentParser(description='DOT graph producer for class representation') parser.add_argument('--class_file','-c', help='The class file to process',required=True) parser.add_argument('--format','-f' , default="pdf", help='The format of the produced graph file') parser.add_argument('--output','-o' , help='The file to be produced',required=True) args = parser.parse_args() args = vars(args) dumper_class = ClassDumperDOT() dot_file = os.path.splitext(args['output'])[0] + ".dot" dumper_class.dump(args['class_file'],dot_file) exe = ['dot'] option_format = ['-T'+args['format'] ] option_output = ['-o', args['output'] ] option_input = ['test.dot'] subprocess.call(exe+option_format+option_output+option_input) diff --git a/class_reader.py b/class_reader.py index eced4b2..f93f6ac 100644 --- a/class_reader.py +++ b/class_reader.py @@ -1,105 +1,106 @@ import re from class_decriptor import ClassDescriptor class ClassReader: def __init__(self): self.classes = [] self.current_class = None def read(self,filename): f = open(filename,'r') self.line_cpt = 0 for line in f: self.readline(line) self.line_cpt += 1 if self.current_class is not None: self.classes.append(self.current_class) return self.classes def readline(self,line): line = line.split('#')[0] line = line.strip() if line == "": return if self.isNewClassTag(line): return if self.isNewMethodTag(line): return if self.isNewTypedef(line): return if self.isNewMemberTag(line): return else: raise Exception("could not parse line:{0}\n'".format(self.line_cpt) + line + "'") def isNewClassTag(self,line): ret = False m = re.match(r'class\s+(\S*)',line) if m: name = m.group(1) inheritance = None ret = True m = re.match(r'class\s+(\S*)\((.*)\)',line) if m: - name = m.group(1) - inheritance = m.group(2) + name = m.group(1).strip() + inheritance = m.group(2).strip() inheritance = inheritance.strip().split(',') inheritance = [e.strip() for e in inheritance] ret = True if ret == False: return False if self.current_class is not None: self.classes.append(self.current_class) self.current_class = ClassDescriptor(name,inheritance) return True def isNewMemberTag(self,line): if not line.find("(") == -1: return False if not line.find(")") == -1: return False - m = re.match(r'((?:public|protected|private)*)\s*((?:\S|(?:\s+\*)|(?:\s+\&))+)\s+(.*)',line) + m = re.match(r'((?:public|protected|private)*)\s*((?:static)?)\s*((?:\S|(?:\s+\*)|(?:\s+\&))+)\s+(.*)',line) if m: - encapsulation = m.group(1) - _type = m.group(2) - name = m.group(3) - self.current_class.addMember(name,_type,encapsulation) + encapsulation = m.group(1).strip() + static = m.group(2).strip() + _type = m.group(3).strip() + name = m.group(3).strip() + self.current_class.addMember(name,_type,encapsulation,static) return True return False def isNewTypedef(self,line): if not line.find("(") == -1: return False if not line.find(")") == -1: return False m = re.match(r'((?:public|protected|private)*)\s*typedef\s*(\S+)',line) if m: - encapsulation = m.group(1) - name = m.group(2) + encapsulation = m.group(1).strip() + name = m.group(2).strip() self.current_class.addType(name,encapsulation) return True return False def isNewMethodTag(self,line): - m = re.match(r'((?:public|protected|private)*)\s*((?:static)?)\s*((?:virtual|pure virtual)?)\s*(\S*)\s+([\S|~]*)\((.*)\)',line) + m = re.match(r'((?:public|protected|private)*)\s*((?:static)?)\s*((?:virtual|pure virtual)?)\s*(.*)\s+([\S|~]*)\((.*)\)',line) if m: encapsulation = m.group(1).strip() static = m.group(2).strip() virtual = m.group(3).strip() ret = m.group(4).strip() name = m.group(5).strip() args = m.group(6).strip().split(',') args = [tuple(e.strip().split(' ')) for e in args] args = [e for e in args if not e[0] == ''] self.current_class.addMethod(name,args,ret,encapsulation,virtual,static) return True return False if __name__ == '__main__': cls_reader = ClassReader() classes = cls_reader.read('test.classes') for c in classes: print c