|
@@ -0,0 +1,701 @@
|
|
|
|
|
+########################################################################
|
|
|
|
|
+#
|
|
|
|
|
+# Documentation generator for panda. There are three
|
|
|
|
|
+# major subsystems:
|
|
|
|
|
+#
|
|
|
|
|
+# * The module that reads interrogate databases.
|
|
|
|
|
+#
|
|
|
|
|
+# * The module that reads python source-files.
|
|
|
|
|
+#
|
|
|
|
|
+# *
|
|
|
|
|
+#
|
|
|
|
|
+########################################################################
|
|
|
|
|
+
|
|
|
|
|
+import os,sys,parser,symbol,token,types,re
|
|
|
|
|
+
|
|
|
|
|
+########################################################################
|
|
|
|
|
+#
|
|
|
|
|
+# assorted utility functions
|
|
|
|
|
+#
|
|
|
|
|
+########################################################################
|
|
|
|
|
+
|
|
|
|
|
+SECHEADER = re.compile("^[A-Z][a-z]+\s*:")
|
|
|
|
|
+JUNKHEADER = re.compile("^((Function)|(Access))\s*:")
|
|
|
|
|
+
|
|
|
|
|
+def readFile(fn):
|
|
|
|
|
+ try:
|
|
|
|
|
+ srchandle = open(fn, "r")
|
|
|
|
|
+ data = srchandle.read()
|
|
|
|
|
+ srchandle.close()
|
|
|
|
|
+ return data
|
|
|
|
|
+ except:
|
|
|
|
|
+ sys.exit("Cannot read "+fn)
|
|
|
|
|
+
|
|
|
|
|
+def writeFile(wfile,data):
|
|
|
|
|
+ try:
|
|
|
|
|
+ dsthandle = open(wfile, "wb")
|
|
|
|
|
+ dsthandle.write(data)
|
|
|
|
|
+ dsthandle.close()
|
|
|
|
|
+ except:
|
|
|
|
|
+ sys.exit("Cannot write "+wfile)
|
|
|
|
|
+
|
|
|
|
|
+def findFiles(dir, ext, ign, list):
|
|
|
|
|
+ for file in os.listdir(dir):
|
|
|
|
|
+ full = dir + "/" + file
|
|
|
|
|
+ if (ign.has_key(full)==0) and (ign.has_key(file)==0):
|
|
|
|
|
+ if (os.path.isfile(full)):
|
|
|
|
|
+ if (file.endswith(ext)):
|
|
|
|
|
+ list.append(full)
|
|
|
|
|
+ elif (os.path.isdir(full)):
|
|
|
|
|
+ findFiles(full, ext, ign, list)
|
|
|
|
|
+
|
|
|
|
|
+def textToHTML(comment,sep,delsection=None):
|
|
|
|
|
+ sections = [""]
|
|
|
|
|
+ included = {}
|
|
|
|
|
+ for line in comment.split("\n"):
|
|
|
|
|
+ line = line.lstrip(" ").lstrip(sep).lstrip(" ").rstrip("\r").rstrip(" ")
|
|
|
|
|
+ if (line == ""):
|
|
|
|
|
+ sections.append("")
|
|
|
|
|
+ elif (SECHEADER.match(line)):
|
|
|
|
|
+ sections.append(line)
|
|
|
|
|
+ else:
|
|
|
|
|
+ sections[-1] = sections[-1] + " " + line
|
|
|
|
|
+ total = ""
|
|
|
|
|
+ for sec in sections:
|
|
|
|
|
+ if (sec != ""):
|
|
|
|
|
+ sec = sec.replace("&","&")
|
|
|
|
|
+ sec = sec.replace("<","<")
|
|
|
|
|
+ sec = sec.replace(">",">")
|
|
|
|
|
+ sec = sec.replace(" "," ")
|
|
|
|
|
+ sec = sec.replace(" "," ")
|
|
|
|
|
+ if (delsection != None) and (delsection.match(sec)):
|
|
|
|
|
+ included[sec] = 1
|
|
|
|
|
+ if (included.has_key(sec)==0):
|
|
|
|
|
+ included[sec] = 1
|
|
|
|
|
+ total = total + sec + "<br>\n"
|
|
|
|
|
+ return total
|
|
|
|
|
+
|
|
|
|
|
+def linkToPage(page, text):
|
|
|
|
|
+ return '<a href="/apiref.php?page=' + page + '">' + text + '</a>'
|
|
|
|
|
+
|
|
|
|
|
+def convertToPythonFn(fn):
|
|
|
|
|
+ result = ""
|
|
|
|
|
+ lastc = 0
|
|
|
|
|
+ for c in fn:
|
|
|
|
|
+ if (c!="_"):
|
|
|
|
|
+ if (lastc=="_"):
|
|
|
|
|
+ result = result + c.upper()
|
|
|
|
|
+ else:
|
|
|
|
|
+ result = result + c
|
|
|
|
|
+ lastc = c
|
|
|
|
|
+ return result
|
|
|
|
|
+
|
|
|
|
|
+def getPandaVersion():
|
|
|
|
|
+ try:
|
|
|
|
|
+ f = file("dtool/PandaVersion.pp","r")
|
|
|
|
|
+ pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+PANDA_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)')
|
|
|
|
|
+ for line in f:
|
|
|
|
|
+ match = pattern.match(line,0)
|
|
|
|
|
+ if (match):
|
|
|
|
|
+ version = match.group(1)+"."+match.group(2)+"."+match.group(3)
|
|
|
|
|
+ break
|
|
|
|
|
+ f.close()
|
|
|
|
|
+ except: sys.exit("Cannot read version number from dtool/PandaVersion.pp")
|
|
|
|
|
+ return version
|
|
|
|
|
+
|
|
|
|
|
+########################################################################
|
|
|
|
|
+#
|
|
|
|
|
+# Interrogate Database Tokenizer
|
|
|
|
|
+#
|
|
|
|
|
+########################################################################
|
|
|
|
|
+
|
|
|
|
|
+class InterrogateTokenizer:
|
|
|
|
|
+ """
|
|
|
|
|
+ A big string, with a "parse pointer", and routines to
|
|
|
|
|
+ extract integers and strings. The token syntax is that
|
|
|
|
|
+ used by interrogate databases.
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self,fn):
|
|
|
|
|
+ self.fn = fn
|
|
|
|
|
+ self.pos = 0
|
|
|
|
|
+ self.data = readFile(fn)
|
|
|
|
|
+
|
|
|
|
|
+ def readint(self):
|
|
|
|
|
+ neg = 0
|
|
|
|
|
+ while (self.data[self.pos].isspace()):
|
|
|
|
|
+ self.pos += 1
|
|
|
|
|
+ if (self.data[self.pos] == "-"):
|
|
|
|
|
+ neg = 1
|
|
|
|
|
+ self.pos += 1
|
|
|
|
|
+ if (self.data[self.pos].isdigit()==0):
|
|
|
|
|
+ print "File position "+str(self.pos)
|
|
|
|
|
+ print "Text: "+self.data[self.pos:self.pos+50]
|
|
|
|
|
+ sys.exit("Syntax error in interrogate file format 0")
|
|
|
|
|
+ value = 0
|
|
|
|
|
+ while (self.data[self.pos].isdigit()):
|
|
|
|
|
+ value = value*10 + int(self.data[self.pos])
|
|
|
|
|
+ self.pos += 1
|
|
|
|
|
+ if (neg): value = -value
|
|
|
|
|
+ return value
|
|
|
|
|
+
|
|
|
|
|
+ def readstring(self):
|
|
|
|
|
+ length = self.readint()
|
|
|
|
|
+ if (self.data[self.pos].isspace()==0):
|
|
|
|
|
+ sys.exit("Syntax error in interrogate file format 1")
|
|
|
|
|
+ self.pos += 1
|
|
|
|
|
+ body = self.data[self.pos:self.pos+length]
|
|
|
|
|
+ if (len(body) != length):
|
|
|
|
|
+ sys.exit("Syntax error in interrogate file format 2")
|
|
|
|
|
+ self.pos += length
|
|
|
|
|
+ return body
|
|
|
|
|
+
|
|
|
|
|
+########################################################################
|
|
|
|
|
+#
|
|
|
|
|
+# Interrogate Database Storage/Parsing
|
|
|
|
|
+#
|
|
|
|
|
+########################################################################
|
|
|
|
|
+
|
|
|
|
|
+def parseInterrogateIntVec(tokzr):
|
|
|
|
|
+ length = tokzr.readint()
|
|
|
|
|
+ result = []
|
|
|
|
|
+ for i in range(length):
|
|
|
|
|
+ result.append(tokzr.readint())
|
|
|
|
|
+ return result
|
|
|
|
|
+
|
|
|
|
|
+class InterrogateFunction:
|
|
|
|
|
+ def __init__(self, tokzr, db):
|
|
|
|
|
+ self.db = db
|
|
|
|
|
+ self.index = tokzr.readint()
|
|
|
|
|
+ self.componentname = tokzr.readstring()
|
|
|
|
|
+ self.flags = tokzr.readint()
|
|
|
|
|
+ self.classindex = tokzr.readint()
|
|
|
|
|
+ self.scopedname = tokzr.readstring()
|
|
|
|
|
+ self.cwrappers = parseInterrogateIntVec(tokzr)
|
|
|
|
|
+ self.pythonwrappers = parseInterrogateIntVec(tokzr)
|
|
|
|
|
+ self.comment = tokzr.readstring()
|
|
|
|
|
+ self.prototype = tokzr.readstring()
|
|
|
|
|
+
|
|
|
|
|
+class InterrogateEnumValue:
|
|
|
|
|
+ def __init__(self, tokzr):
|
|
|
|
|
+ self.name = tokzr.readstring()
|
|
|
|
|
+ self.scopedname = tokzr.readstring()
|
|
|
|
|
+ self.value = tokzr.readint()
|
|
|
|
|
+
|
|
|
|
|
+class InterrogateDerivation:
|
|
|
|
|
+ def __init__(self, tokzr):
|
|
|
|
|
+ self.flags = tokzr.readint()
|
|
|
|
|
+ self.base = tokzr.readint()
|
|
|
|
|
+ self.upcast = tokzr.readint()
|
|
|
|
|
+ self.downcast = tokzr.readint()
|
|
|
|
|
+
|
|
|
|
|
+class InterrogateType:
|
|
|
|
|
+ def __init__(self, tokzr, db):
|
|
|
|
|
+ self.db = db
|
|
|
|
|
+ self.index = tokzr.readint()
|
|
|
|
|
+ self.componentname = tokzr.readstring()
|
|
|
|
|
+ self.flags = tokzr.readint()
|
|
|
|
|
+ self.scopedname = tokzr.readstring()
|
|
|
|
|
+ self.truename = tokzr.readstring()
|
|
|
|
|
+ self.outerclass = tokzr.readint()
|
|
|
|
|
+ self.atomictype = tokzr.readint()
|
|
|
|
|
+ self.wrappedtype = tokzr.readint()
|
|
|
|
|
+ self.constructors = parseInterrogateIntVec(tokzr)
|
|
|
|
|
+ self.destructor = tokzr.readint()
|
|
|
|
|
+ self.elements = parseInterrogateIntVec(tokzr)
|
|
|
|
|
+ self.methods = parseInterrogateIntVec(tokzr)
|
|
|
|
|
+ self.casts = parseInterrogateIntVec(tokzr)
|
|
|
|
|
+ self.derivations = []
|
|
|
|
|
+ nderivations = tokzr.readint()
|
|
|
|
|
+ for i in range(nderivations):
|
|
|
|
|
+ self.derivations.append(InterrogateDerivation(tokzr))
|
|
|
|
|
+ self.enumvalues = []
|
|
|
|
|
+ nenumvalues = tokzr.readint()
|
|
|
|
|
+ for i in range(nenumvalues):
|
|
|
|
|
+ self.enumvalues.append(InterrogateEnumValue(tokzr))
|
|
|
|
|
+ self.nested = parseInterrogateIntVec(tokzr)
|
|
|
|
|
+ self.comment = tokzr.readstring()
|
|
|
|
|
+
|
|
|
|
|
+class InterrogateParameter:
|
|
|
|
|
+ def __init__(self, tokzr):
|
|
|
|
|
+ self.name = tokzr.readstring()
|
|
|
|
|
+ self.parameterflags = tokzr.readint()
|
|
|
|
|
+ self.type = tokzr.readint()
|
|
|
|
|
+
|
|
|
|
|
+class InterrogateWrapper:
|
|
|
|
|
+ def __init__(self, tokzr, db):
|
|
|
|
|
+ self.db = db
|
|
|
|
|
+ self.index = tokzr.readint()
|
|
|
|
|
+ self.componentname = tokzr.readstring()
|
|
|
|
|
+ self.flags = tokzr.readint()
|
|
|
|
|
+ self.function = tokzr.readint()
|
|
|
|
|
+ self.returntype = tokzr.readint()
|
|
|
|
|
+ self.returnvaluedestructor = tokzr.readint()
|
|
|
|
|
+ self.uniquename = tokzr.readstring()
|
|
|
|
|
+ self.parameters = []
|
|
|
|
|
+ nparameters = tokzr.readint()
|
|
|
|
|
+ for i in range(nparameters):
|
|
|
|
|
+ self.parameters.append(InterrogateParameter(tokzr))
|
|
|
|
|
+
|
|
|
|
|
+class InterrogateDatabase:
|
|
|
|
|
+ def __init__(self, tokzr):
|
|
|
|
|
+ self.fn = tokzr.fn
|
|
|
|
|
+ self.magic = tokzr.readint()
|
|
|
|
|
+ version1 = tokzr.readint()
|
|
|
|
|
+ version2 = tokzr.readint()
|
|
|
|
|
+ if (version1 != 2) or (version2 != 2):
|
|
|
|
|
+ sys.exit("This program only understands interrogate file format 2.2")
|
|
|
|
|
+ self.library = tokzr.readstring()
|
|
|
|
|
+ self.libhash = tokzr.readstring()
|
|
|
|
|
+ self.module = tokzr.readstring()
|
|
|
|
|
+ self.functions = {}
|
|
|
|
|
+ self.wrappers = {}
|
|
|
|
|
+ self.types = {}
|
|
|
|
|
+ self.namedtypes = {}
|
|
|
|
|
+ count_functions = tokzr.readint()
|
|
|
|
|
+ for i in range(count_functions):
|
|
|
|
|
+ fn = InterrogateFunction(tokzr, self)
|
|
|
|
|
+ self.functions[fn.index] = fn
|
|
|
|
|
+ count_wrappers = tokzr.readint()
|
|
|
|
|
+ for i in range(count_wrappers):
|
|
|
|
|
+ wr = InterrogateWrapper(tokzr, self)
|
|
|
|
|
+ self.wrappers[wr.index] = wr
|
|
|
|
|
+ count_types = tokzr.readint()
|
|
|
|
|
+ for i in range(count_types):
|
|
|
|
|
+ tp = InterrogateType(tokzr, self)
|
|
|
|
|
+ self.types[tp.index] = tp
|
|
|
|
|
+ self.namedtypes[tp.scopedname] = tp
|
|
|
|
|
+
|
|
|
|
|
+########################################################################
|
|
|
|
|
+#
|
|
|
|
|
+# Pattern Matching for Python Parse Trees
|
|
|
|
|
+#
|
|
|
|
|
+########################################################################
|
|
|
|
|
+
|
|
|
|
|
+def printTree(tree,indent):
|
|
|
|
|
+ spacing = " "[:indent]
|
|
|
|
|
+ if isinstance(tree,types.TupleType) and isinstance(tree[0], types.IntType):
|
|
|
|
|
+ if symbol.sym_name.has_key(tree[0]):
|
|
|
|
|
+ for i in range(len(tree)):
|
|
|
|
|
+ if (i==0):
|
|
|
|
|
+ print spacing + "(symbol." + symbol.sym_name[tree[0]] + ","
|
|
|
|
|
+ else:
|
|
|
|
|
+ printTree(tree[i], indent+1)
|
|
|
|
|
+ print spacing + "),"
|
|
|
|
|
+ elif token.tok_name.has_key(tree[0]):
|
|
|
|
|
+ print spacing + "(token." + token.tok_name[tree[0]] + ", '" + tree[1] + "'),"
|
|
|
|
|
+ else:
|
|
|
|
|
+ print spacing + str(tree)
|
|
|
|
|
+ else:
|
|
|
|
|
+ print spacing + str(tree)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+COMPOUND_STMT_PATTERN = (
|
|
|
|
|
+ symbol.stmt,
|
|
|
|
|
+ (symbol.compound_stmt, ['compound'])
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+DOCSTRING_STMT_PATTERN = (
|
|
|
|
|
+ symbol.stmt,
|
|
|
|
|
+ (symbol.simple_stmt,
|
|
|
|
|
+ (symbol.small_stmt,
|
|
|
|
|
+ (symbol.expr_stmt,
|
|
|
|
|
+ (symbol.testlist,
|
|
|
|
|
+ (symbol.test,
|
|
|
|
|
+ (symbol.and_test,
|
|
|
|
|
+ (symbol.not_test,
|
|
|
|
|
+ (symbol.comparison,
|
|
|
|
|
+ (symbol.expr,
|
|
|
|
|
+ (symbol.xor_expr,
|
|
|
|
|
+ (symbol.and_expr,
|
|
|
|
|
+ (symbol.shift_expr,
|
|
|
|
|
+ (symbol.arith_expr,
|
|
|
|
|
+ (symbol.term,
|
|
|
|
|
+ (symbol.factor,
|
|
|
|
|
+ (symbol.power,
|
|
|
|
|
+ (symbol.atom,
|
|
|
|
|
+ (token.STRING, ['docstring'])
|
|
|
|
|
+ )))))))))))))))),
|
|
|
|
|
+ (token.NEWLINE, '')
|
|
|
|
|
+ ))
|
|
|
|
|
+
|
|
|
|
|
+DERIVATION_PATTERN = (
|
|
|
|
|
+ symbol.test,
|
|
|
|
|
+ (symbol.and_test,
|
|
|
|
|
+ (symbol.not_test,
|
|
|
|
|
+ (symbol.comparison,
|
|
|
|
|
+ (symbol.expr,
|
|
|
|
|
+ (symbol.xor_expr,
|
|
|
|
|
+ (symbol.and_expr,
|
|
|
|
|
+ (symbol.shift_expr,
|
|
|
|
|
+ (symbol.arith_expr,
|
|
|
|
|
+ (symbol.term,
|
|
|
|
|
+ (symbol.factor,
|
|
|
|
|
+ (symbol.power,
|
|
|
|
|
+ (symbol.atom,
|
|
|
|
|
+ (token.NAME, ['classname'])
|
|
|
|
|
+ )))))))))))))
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class ParseTreeInfo:
|
|
|
|
|
+ docstring = ''
|
|
|
|
|
+ name = ''
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self, tree, name):
|
|
|
|
|
+ """
|
|
|
|
|
+ The code can be a string (in which case it is parsed), or it
|
|
|
|
|
+ can be in parse tree form already.
|
|
|
|
|
+ """
|
|
|
|
|
+ self.name = name
|
|
|
|
|
+ self.class_info = {}
|
|
|
|
|
+ self.function_info = {}
|
|
|
|
|
+ self.derivs = {}
|
|
|
|
|
+ if isinstance(tree, types.StringType):
|
|
|
|
|
+ try:
|
|
|
|
|
+ tree = parser.suite(tree+"\n").totuple()
|
|
|
|
|
+ if (tree):
|
|
|
|
|
+ found, vars = self.match(DOCSTRING_STMT_PATTERN, tree[1])
|
|
|
|
|
+ if found:
|
|
|
|
|
+ self.docstring = vars["docstring"]
|
|
|
|
|
+ except:
|
|
|
|
|
+ print "CAUTION --- Parse failed: "+name
|
|
|
|
|
+ if isinstance(tree, types.TupleType):
|
|
|
|
|
+ self.extract_info(tree)
|
|
|
|
|
+
|
|
|
|
|
+ def match(self, pattern, data, vars=None):
|
|
|
|
|
+ """
|
|
|
|
|
+ pattern
|
|
|
|
|
+ Pattern to match against, possibly containing variables.
|
|
|
|
|
+ data
|
|
|
|
|
+ Data to be checked and against which variables are extracted.
|
|
|
|
|
+ vars
|
|
|
|
|
+ Dictionary of variables which have already been found. If not
|
|
|
|
|
+ provided, an empty dictionary is created.
|
|
|
|
|
+
|
|
|
|
|
+ The `pattern' value may contain variables of the form ['varname']
|
|
|
|
|
+ which are allowed to parseTreeMatch anything. The value that is
|
|
|
|
|
+ parseTreeMatched is returned as part of a dictionary which maps
|
|
|
|
|
+ 'varname' to the parseTreeMatched value. 'varname' is not required
|
|
|
|
|
+ to be a string object, but using strings makes patterns and the code
|
|
|
|
|
+ which uses them more readable. This function returns two values: a
|
|
|
|
|
+ boolean indicating whether a parseTreeMatch was found and a
|
|
|
|
|
+ dictionary mapping variable names to their associated values.
|
|
|
|
|
+ """
|
|
|
|
|
+ if vars is None:
|
|
|
|
|
+ vars = {}
|
|
|
|
|
+ if type(pattern) is types.ListType: # 'variables' are ['varname']
|
|
|
|
|
+ vars[pattern[0]] = data
|
|
|
|
|
+ return 1, vars
|
|
|
|
|
+ if type(pattern) is not types.TupleType:
|
|
|
|
|
+ return (pattern == data), vars
|
|
|
|
|
+ if len(data) != len(pattern):
|
|
|
|
|
+ return 0, vars
|
|
|
|
|
+ for pattern, data in map(None, pattern, data):
|
|
|
|
|
+ same, vars = self.match(pattern, data, vars)
|
|
|
|
|
+ if not same:
|
|
|
|
|
+ break
|
|
|
|
|
+ return same, vars
|
|
|
|
|
+
|
|
|
|
|
+ def extract_info(self, tree):
|
|
|
|
|
+ # extract docstring
|
|
|
|
|
+ found = 0
|
|
|
|
|
+ if len(tree) == 2:
|
|
|
|
|
+ found, vars = self.match(DOCSTRING_STMT_PATTERN[1], tree[1])
|
|
|
|
|
+ elif len(tree) >= 4:
|
|
|
|
|
+ found, vars = self.match(DOCSTRING_STMT_PATTERN, tree[3])
|
|
|
|
|
+ if found:
|
|
|
|
|
+ self.docstring = eval(vars['docstring'])
|
|
|
|
|
+ # discover inner definitions
|
|
|
|
|
+ for node in tree[1:]:
|
|
|
|
|
+ found, vars = self.match(COMPOUND_STMT_PATTERN, node)
|
|
|
|
|
+ if found:
|
|
|
|
|
+ cstmt = vars['compound']
|
|
|
|
|
+ if cstmt[0] == symbol.funcdef:
|
|
|
|
|
+ name = cstmt[2][1]
|
|
|
|
|
+ self.function_info[name] = ParseTreeInfo(cstmt and cstmt[-1] or None, name)
|
|
|
|
|
+ self.function_info[name].prototype = self.extract_tokens("", cstmt[3])
|
|
|
|
|
+ elif cstmt[0] == symbol.classdef:
|
|
|
|
|
+ name = cstmt[2][1]
|
|
|
|
|
+ self.class_info[name] = ParseTreeInfo(cstmt and cstmt[-1] or None, name)
|
|
|
|
|
+ self.extract_derivs(self.class_info[name],cstmt)
|
|
|
|
|
+
|
|
|
|
|
+ def extract_derivs(self, classinfo, tree):
|
|
|
|
|
+ if (len(tree)==8):
|
|
|
|
|
+ derivs = tree[4]
|
|
|
|
|
+ for deriv in derivs[1:]:
|
|
|
|
|
+ found, vars = self.match(DERIVATION_PATTERN, deriv)
|
|
|
|
|
+ if (found):
|
|
|
|
|
+ classinfo.derivs[vars["classname"]] = 1
|
|
|
|
|
+
|
|
|
|
|
+ def extract_tokens(self, str, tree):
|
|
|
|
|
+ if (isinstance(tree, types.TupleType)):
|
|
|
|
|
+ if (token.tok_name.has_key(tree[0])):
|
|
|
|
|
+ str = str + tree[1]
|
|
|
|
|
+ if (tree[1]==","): str=str+" "
|
|
|
|
|
+ elif (symbol.sym_name.has_key(tree[0])):
|
|
|
|
|
+ for sub in tree[1:]:
|
|
|
|
|
+ str = self.extract_tokens(str, sub)
|
|
|
|
|
+ return str
|
|
|
|
|
+
|
|
|
|
|
+########################################################################
|
|
|
|
|
+#
|
|
|
|
|
+# The code database contains:
|
|
|
|
|
+#
|
|
|
|
|
+# - a list of InterrogateDatabase objects representing C++ modules.
|
|
|
|
|
+# - a list of ParseTreeInfo objects representing python modules.
|
|
|
|
|
+#
|
|
|
|
|
+# Collectively, these make up all the data about all the code.
|
|
|
|
|
+#
|
|
|
|
|
+########################################################################
|
|
|
|
|
+
|
|
|
|
|
+class CodeDatabase:
|
|
|
|
|
+ def __init__(self, cxxlist, pylist):
|
|
|
|
|
+ self.types = {}
|
|
|
|
|
+ self.funcs = {}
|
|
|
|
|
+ self.goodtypes = {}
|
|
|
|
|
+ self.globalfn = []
|
|
|
|
|
+ for cxx in cxxlist:
|
|
|
|
|
+ print "Reading source file "+cxx
|
|
|
|
|
+ tokzr = InterrogateTokenizer(cxx)
|
|
|
|
|
+ idb = InterrogateDatabase(tokzr)
|
|
|
|
|
+ for type in idb.types.values():
|
|
|
|
|
+ if (type.flags & 8192) or (self.types.has_key(type.scopedname)==0):
|
|
|
|
|
+ self.types[type.scopedname] = type
|
|
|
|
|
+ if (type.flags & 8192) and (type.atomictype == 0) and (type.scopedname.count(" ")==0) and (type.scopedname.count(":")==0):
|
|
|
|
|
+ self.goodtypes[type.scopedname] = type
|
|
|
|
|
+ for func in idb.functions.values():
|
|
|
|
|
+ type = idb.types.get(func.classindex)
|
|
|
|
|
+ func.pyname = convertToPythonFn(func.componentname)
|
|
|
|
|
+ if (type == None):
|
|
|
|
|
+ self.funcs["GLOBAL."+func.pyname] = func
|
|
|
|
|
+ self.globalfn.append("GLOBAL."+func.pyname)
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.funcs[type.scopedname+"."+func.pyname] = func
|
|
|
|
|
+ for py in pylist:
|
|
|
|
|
+ print "Reading source file "+py
|
|
|
|
|
+ pyinf = ParseTreeInfo(readFile(py), py)
|
|
|
|
|
+ for type in pyinf.class_info.keys():
|
|
|
|
|
+ typinf = pyinf.class_info[type]
|
|
|
|
|
+ self.types[type] = typinf
|
|
|
|
|
+ self.goodtypes[type] = typinf
|
|
|
|
|
+ for func in typinf.function_info.keys():
|
|
|
|
|
+ self.funcs[type+"."+func] = typinf.function_info[func]
|
|
|
|
|
+ for func in pyinf.function_info.keys():
|
|
|
|
|
+ self.funcs["GLOBAL."+func] = pyinf.function_info[func]
|
|
|
|
|
+ self.globalfn.append("GLOBAL."+func)
|
|
|
|
|
+
|
|
|
|
|
+ def getClassList(self):
|
|
|
|
|
+ return self.goodtypes.keys()
|
|
|
|
|
+
|
|
|
|
|
+ def getGlobalFunctionList(self):
|
|
|
|
|
+ return self.globalfn
|
|
|
|
|
+
|
|
|
|
|
+ def getClassComment(self, cn):
|
|
|
|
|
+ type = self.types.get(cn)
|
|
|
|
|
+ if (isinstance(type, InterrogateType)):
|
|
|
|
|
+ return textToHTML(type.comment,"/")
|
|
|
|
|
+ elif (isinstance(type, ParseTreeInfo)):
|
|
|
|
|
+ return textToHTML(type.docstring,"#")
|
|
|
|
|
+ else:
|
|
|
|
|
+ return ""
|
|
|
|
|
+
|
|
|
|
|
+ def getClassParents(self, cn):
|
|
|
|
|
+ type = self.types.get(cn)
|
|
|
|
|
+ if (isinstance(type, InterrogateType)):
|
|
|
|
|
+ parents = []
|
|
|
|
|
+ for deriv in type.derivations:
|
|
|
|
|
+ basetype = type.db.types[deriv.base]
|
|
|
|
|
+ parents.append(basetype.scopedname)
|
|
|
|
|
+ return parents
|
|
|
|
|
+ elif (isinstance(type, ParseTreeInfo)):
|
|
|
|
|
+ return type.derivs.keys()
|
|
|
|
|
+ else:
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
+ def buildInheritance(self, inheritance, cn):
|
|
|
|
|
+ if (inheritance.count(cn) == 0):
|
|
|
|
|
+ inheritance.append(cn)
|
|
|
|
|
+ for parent in self.getClassParents(cn):
|
|
|
|
|
+ self.buildInheritance(inheritance, parent)
|
|
|
|
|
+
|
|
|
|
|
+ def getInheritance(self, cn):
|
|
|
|
|
+ inheritance = []
|
|
|
|
|
+ self.buildInheritance(inheritance, cn)
|
|
|
|
|
+ return inheritance
|
|
|
|
|
+
|
|
|
|
|
+ def getClassMethods(self, cn):
|
|
|
|
|
+ type = self.types.get(cn)
|
|
|
|
|
+ result = []
|
|
|
|
|
+ if (isinstance(type, InterrogateType)):
|
|
|
|
|
+ for method in type.methods:
|
|
|
|
|
+ func = type.db.functions[method]
|
|
|
|
|
+ if (func.classindex == type.index):
|
|
|
|
|
+ result.append(type.scopedname+"."+func.pyname)
|
|
|
|
|
+ elif (isinstance(type, ParseTreeInfo)):
|
|
|
|
|
+ for method in type.function_info.keys():
|
|
|
|
|
+ result.append(type.name + "." + method)
|
|
|
|
|
+ return result
|
|
|
|
|
+
|
|
|
|
|
+ def getFunctionName(self, fn):
|
|
|
|
|
+ func = self.funcs.get(fn)
|
|
|
|
|
+ if (isinstance(func, InterrogateFunction)):
|
|
|
|
|
+ return func.pyname
|
|
|
|
|
+ elif (isinstance(func, ParseTreeInfo)):
|
|
|
|
|
+ return func.name
|
|
|
|
|
+ else:
|
|
|
|
|
+ return fn
|
|
|
|
|
+
|
|
|
|
|
+ def getFunctionPrototype(self, fn):
|
|
|
|
|
+ func = self.funcs.get(fn)
|
|
|
|
|
+ if (isinstance(func, InterrogateFunction)):
|
|
|
|
|
+ proto = func.prototype
|
|
|
|
|
+ proto = proto.replace(" inline "," ")
|
|
|
|
|
+ if (proto.startswith("inline ")): proto = proto[7:]
|
|
|
|
|
+ proto = proto.replace("basic_string< char >", "string")
|
|
|
|
|
+ return textToHTML(proto,"")
|
|
|
|
|
+ elif (isinstance(func, ParseTreeInfo)):
|
|
|
|
|
+ return textToHTML("def "+func.name+func.prototype,"")
|
|
|
|
|
+ return fn
|
|
|
|
|
+
|
|
|
|
|
+ def getFunctionComment(self, fn):
|
|
|
|
|
+ func = self.funcs.get(fn)
|
|
|
|
|
+ if (isinstance(func, InterrogateFunction)):
|
|
|
|
|
+ return textToHTML(func.comment, "/", JUNKHEADER)
|
|
|
|
|
+ elif (isinstance(func, ParseTreeInfo)):
|
|
|
|
|
+ return textToHTML(func.docstring, "#")
|
|
|
|
|
+ return fn
|
|
|
|
|
+
|
|
|
|
|
+########################################################################
|
|
|
|
|
+#
|
|
|
|
|
+# The "Class Rename Dictionary" - Yech.
|
|
|
|
|
+#
|
|
|
|
|
+########################################################################
|
|
|
|
|
+
|
|
|
|
|
+CLASS_RENAME_DICT = {
|
|
|
|
|
+ "Loader" : "PandaLoader" ,
|
|
|
|
|
+ "String" : "CString" ,
|
|
|
|
|
+ "LMatrix4f" : "Mat4" ,
|
|
|
|
|
+ "LMatrix3f" : "Mat3" ,
|
|
|
|
|
+ "LVecBase4f" : "VBase4" ,
|
|
|
|
|
+ "LVector4f" : "Vec4" ,
|
|
|
|
|
+ "LPoint4f" : "Point4" ,
|
|
|
|
|
+ "LVecBase3f" : "VBase3" ,
|
|
|
|
|
+ "LVector3f" : "Vec3" ,
|
|
|
|
|
+ "LPoint3f" : "Point3" ,
|
|
|
|
|
+ "LVecBase2f" : "VBase2" ,
|
|
|
|
|
+ "LVector2f" : "Vec2" ,
|
|
|
|
|
+ "LPoint2f" : "Point2" ,
|
|
|
|
|
+ "LQuaternionf" : "Quat" ,
|
|
|
|
|
+ "LMatrix4d" : "Mat4D" ,
|
|
|
|
|
+ "LMatrix3d" : "Mat3D" ,
|
|
|
|
|
+ "LVecBase4d" : "VBase4D" ,
|
|
|
|
|
+ "LVector4d" : "Vec4D" ,
|
|
|
|
|
+ "LPoint4d" : "Point4D" ,
|
|
|
|
|
+ "LVecBase3d" : "VBase3D" ,
|
|
|
|
|
+ "LVector3d" : "Vec3D" ,
|
|
|
|
|
+ "LPoint3d" : "Point3D" ,
|
|
|
|
|
+ "LVecBase2d" : "VBase2D" ,
|
|
|
|
|
+ "LVector2d" : "Vec2D" ,
|
|
|
|
|
+ "LPoint2d" : "Point2D" ,
|
|
|
|
|
+ "LQuaterniond" : "QuatD" ,
|
|
|
|
|
+ "Plane" : "PlaneBase" ,
|
|
|
|
|
+ "Planef" : "Plane" ,
|
|
|
|
|
+ "Planed" : "PlaneD" ,
|
|
|
|
|
+ "Frustum" : "FrustumBase" ,
|
|
|
|
|
+ "Frustumf" : "Frustum" ,
|
|
|
|
|
+ "Frustumd" : "FrustumD"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+########################################################################
|
|
|
|
|
+#
|
|
|
|
|
+# Main Program
|
|
|
|
|
+#
|
|
|
|
|
+########################################################################
|
|
|
|
|
+
|
|
|
|
|
+def getCodeDatabase():
|
|
|
|
|
+ ignore = {}
|
|
|
|
|
+ ignore["__init__.py"] = 1
|
|
|
|
|
+ ignore["direct/src/directscripts"] = 1
|
|
|
|
|
+ ignore["direct/src/extensions"] = 1
|
|
|
|
|
+ ignore["direct/src/extensions_native"] = 1
|
|
|
|
|
+ ignore["direct/src/ffi"] = 1
|
|
|
|
|
+ cxxfiles = []
|
|
|
|
|
+ pyfiles = []
|
|
|
|
|
+ findFiles("built/pandac/input", ".in", ignore, cxxfiles)
|
|
|
|
|
+ findFiles("direct", ".py", ignore, pyfiles)
|
|
|
|
|
+ return CodeDatabase(cxxfiles, pyfiles)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def generateFunctionDocs(code, method):
|
|
|
|
|
+ name = code.getFunctionName(method)
|
|
|
|
|
+ proto = code.getFunctionPrototype(method)
|
|
|
|
|
+ comment = code.getFunctionComment(method)
|
|
|
|
|
+ if (comment == ""): comment = "Undocumented function.<br>\n"
|
|
|
|
|
+ chunk = '<table class="codecomment1" width="100%"><tr><td>' + "\n"
|
|
|
|
|
+ chunk = chunk + '<a name="' + name + '"><b>' + name + "</b></a><br>\n"
|
|
|
|
|
+ chunk = chunk + proto + "<br>\n"
|
|
|
|
|
+ chunk = chunk + comment
|
|
|
|
|
+ chunk = chunk + "</td></tr></table>\n"
|
|
|
|
|
+ return chunk
|
|
|
|
|
+
|
|
|
|
|
+def generateDocs(code):
|
|
|
|
|
+ dir = "built/pandac/docs"
|
|
|
|
|
+ classes = code.getClassList()[:]
|
|
|
|
|
+ classes.sort()
|
|
|
|
|
+ xclasses = classes[:]
|
|
|
|
|
+ for type in classes:
|
|
|
|
|
+ print "Generating page for class "+type
|
|
|
|
|
+ body = "<h1>" + type + "</h1>\n"
|
|
|
|
|
+ comment = code.getClassComment(type)
|
|
|
|
|
+ body = body + "<ul>\n" + comment + "</ul>\n\n"
|
|
|
|
|
+ inheritance = code.getInheritance(type)
|
|
|
|
|
+ body = body + "<h2>Inheritance:</h2>\n<ul>\n"
|
|
|
|
|
+ for inh in inheritance:
|
|
|
|
|
+ line = " " + linkToPage(inh,inh) + " : "
|
|
|
|
|
+ for parent in code.getClassParents(inh):
|
|
|
|
|
+ line = line + linkToPage(parent,parent) + " "
|
|
|
|
|
+ body = body + line + "<br>\n"
|
|
|
|
|
+ body = body + "</ul>\n"
|
|
|
|
|
+ for sclass in inheritance:
|
|
|
|
|
+ methods = code.getClassMethods(sclass)[:]
|
|
|
|
|
+ methods.sort()
|
|
|
|
|
+ if (len(methods) > 0):
|
|
|
|
|
+ body = body + "<h2>Methods of "+sclass+":</h2>\n<ul>\n"
|
|
|
|
|
+ for method in methods:
|
|
|
|
|
+ fn = code.getFunctionName(method)
|
|
|
|
|
+ body = body + '<a href="#' + fn + '">' + fn + "</a><br>\n"
|
|
|
|
|
+ body = body + "</ul>\n"
|
|
|
|
|
+ body = body + "</ul>\n"
|
|
|
|
|
+ for sclass in inheritance:
|
|
|
|
|
+ methods = code.getClassMethods(sclass)[:]
|
|
|
|
|
+ methods.sort()
|
|
|
|
|
+ for method in methods:
|
|
|
|
|
+ body = body + generateFunctionDocs(code, method)
|
|
|
|
|
+ writeFile(dir + "/" + type + ".html", body)
|
|
|
|
|
+ if (CLASS_RENAME_DICT.has_key(type)):
|
|
|
|
|
+ modtype = CLASS_RENAME_DICT[type]
|
|
|
|
|
+ writeFile(dir + "/" + modtype + ".html", body)
|
|
|
|
|
+ xclasses.append(modtype)
|
|
|
|
|
+ xclasses.sort()
|
|
|
|
|
+ pversion = getPandaVersion()
|
|
|
|
|
+ index = "<h1>List of Classes - Panda " + pversion + "</h1>\n<ul>\n"
|
|
|
|
|
+ for type in xclasses:
|
|
|
|
|
+ index = index + linkToPage(type,type) + "<br>\n"
|
|
|
|
|
+ index = index + "</ul>\n"
|
|
|
|
|
+ index = index + "<h1>List of Global Functions - Panda " + pversion + "</h1>\n<ul>\n"
|
|
|
|
|
+ fnlist = code.getGlobalFunctionList()[:]
|
|
|
|
|
+ fnlist.sort()
|
|
|
|
|
+ for func in fnlist:
|
|
|
|
|
+ fn = code.getFunctionName(func)
|
|
|
|
|
+ index = index + '<a href="#' + fn + '">' + fn + "</a><br>\n"
|
|
|
|
|
+ index = index + "</ul>\n"
|
|
|
|
|
+ for func in fnlist:
|
|
|
|
|
+ index = index + generateFunctionDocs(code, func)
|
|
|
|
|
+ writeFile(dir + "/index.html", index)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+generateDocs(getCodeDatabase())
|
|
|
|
|
+
|