diff --git a/docs/docmaker.py b/docs/docmaker.py new file mode 100644 index 000000000..1560ad814 --- /dev/null +++ b/docs/docmaker.py @@ -0,0 +1,457 @@ +#!/usr/bin/env python +# +# DocMaker is a very simple program used to generate HTML documentation +# from the source files of the FreeType packages. +# + +import fileinput, sys, string + +# This function is used to parse a source file, and extract comment blocks +# from it. The following comment block formats are recognized : +# +# /************************** +# * +# * FORMAT1 +# * +# * +# * +# * +# *************************/ +# +# /**************************/ +# /* */ +# /* FORMAT2 */ +# /* */ +# /* */ +# /* */ +# /* */ +# +# /**************************/ +# /* */ +# /* FORMAT3 */ +# /* */ +# /* */ +# /* */ +# /* */ +# /**************************/ +# +# Each block is modeled as a simple list of text strings +# The function returns a list of blocks, i.e. a list of strings lists +# + +def make_block_list(): + """parse a file and extract comments blocks from it""" + + list = [] + block = [] + format = 0 + + for line in fileinput.input(): + + line = string.strip(line) + l = len(line) + + if format == 0: + if l > 3 and line[0:3] == '/**': + i = 3 + while (i < l) and (line[i] == '*'): + i = i+1 + + if i == l: + # this is '/**' followed by any number of '*', the + # beginning of a Format 1 block + # + block = []; + format = 1; + + elif (i == l-1) and (line[i] == '/'): + # this is '/**' followed by any number of '*', followed + # by a '/', i.e. the beginning of a Format 2 or 3 block + # + block = []; + format = 2; + + ############################################################## + # + # FORMAT 1 + # + elif format == 1: + + # if the line doesn't begin with a "*", something went + # wrong, and we must exit, and forget the current block.. + if (l == 0) or (line[0] != '*'): + block = [] + format = 0 + + # otherwise, we test for an end of block, which is an + # arbitrary number of '*', followed by '/' + else: + i = 1 + while (i < l) and (line[i] == '*'): + i = i+1 + + # test for the end of the block + if (i < l) and (line[i] == '/'): + if block != []: list.append( block ) + format = 0 + block = [] + + else: + block.append( line[1:] ) + + ############################################################## + # + # FORMAT 2 + # + elif format == 2: + + # if the line doesn't begin with '/*' and end with '*/', + # this is the end of the format 2 format.. + if (l < 4 ) or (line[:2] != '/*') or (line[-2:] != '*/'): + if block != []: list.append(block) + block = [] + format = 0 + continue + + # remove the start and end comment delimiters, then right-strip + # the line + line = string.rstrip(line[2:-2]) + + # check for end of a format2 block, i.e. a run of '*' + if string.count(line,'*') == l-4: + if block != []: list.append(block) + block = [] + format = 0 + else: + # otherwise, add the line to the current block + block.append(line) + + return list + + +# This function is only used for debugging +# +def dump_block_list( list ): + """dump a comment block list""" + for block in list: + print "----------------------------------------" + for line in block: + print line + print "---------the end-----------------------" + + + + + +###################################################################################### +# +# +# The DocParagraph is used to store either simple text paragraph or +# source code lines +# +# +# If the paragraph contains source code (use code=1 when initializing the +# object), self.lines is a list of source code strings +# +# Otherwise, self.lines is simply a list of words for the paragraph +# +class DocParagraph: + + def __init__(self,code=0,margin=0): + self.lines = [] + self.code = code + self.margin = margin + + def add(self,line): + + if self.code==0: + # get rid of unwanted spaces in the paragraph + self.lines.extend( string.split(line) ) + + else: + # remove margin whitespace + if string.strip( line[:self.margin] ) == "": line = line[self.margin:] + self.lines.append(line) + + + def dump(self): + + max_width = 50 + + if self.code == 0: + cursor = 0 + line = "" + + for word in self.lines: + + if cursor+len(word)+1 > max_width: + print line + cursor = 0 + line = "" + + line = line + word + " " + cursor = cursor + len(word) + 1 + + if cursor > 0: + print line + + else: + for line in self.lines: + print "--" + line + + print "" + + +###################################################################################### +# +# +# The DocContent class is used to store the content of a given marker +# Each DocContent can have its own text, plus a list of fields. Each field +# has its own text too +# +# Hence, the following lines : +# +# """ +# Some arbitraty text: +# +# fieldone :: some arbitraty text for this field, +# note that the text stretches to several lines +# +# fieldtwo :: some other text +# +# """ +# +# will be stored as (each text is a list of string: +# +# self.fields = [ "", "fieldone", "fieldtwo" ] +# self.texts = [ +# [ "some arbitraty text for this field," , +# "note that the text stretches to several lines" ], +# +# [ "some other text" ] +# ] +# +# +class DocContent: + + def __init__(self, paragraph_lines=[]): + + self.fields = [] + self.texts = [] + + code_mode = 0 + code_margin = 0 + + field = "" + text = [] + paragraph = None + + for aline in paragraph_lines: + + if code_mode == 0: + line = string.lstrip(aline) + l = len(line) + margin = len(aline) - l + + # if the line is empty, this is the end of the current + # paragraph + if line == "": + if paragraph: + text.append(paragraph) + paragraph = None + continue + + # test for the beginning of a code block, i.e.'{' is the first + # and only character on the line.. + # + if line == '{': + code_mode = 1 + code_margin = margin + if paragraph: + text.append(paragraph) + paragraph = DocParagraph( 1, margin ) + continue + + words = string.split(line) + + # test for a field delimiter on the start of the line, i.e. + # the oken `::' + # + if len(words) >= 2 and words[1] == "::": + if paragraph: + text.append(paragraph) + paragraph = None + + self.fields.append(field) + self.texts.append(text) + + field = words[0] + text = [] + words = words[2:] + + if len(words) > 0: + line = string.join(words) + if not paragraph: + paragraph = DocParagraph() + paragraph.add( line ) + + else: + line = aline + + # the code block ends with a line that has a single '}' on it + if line == " "*code_margin+'}': + text.append(paragraph) + paragraph = None + code_mode = 0 + code_margin = 0 + + # otherwise, add the line to the current paragraph + else: + paragraph.add(line) + + if paragraph: + text.append(paragraph) + + self.fields.append( field ) + self.texts.append( text ) + + + + def dump(self): + for i in range(len(self.fields)): + field = self.fields[i] + if field: print "" + + for paras in self.texts[i]: + paras.dump() + + if field: print " " + + +###################################################################################### +# +# +# The DocBlock class is used to store a given comment block. It contains +# a list of markers, as well as a list of contents for each marker. +# +# +class DocBlock: + + def __init__(self, block_line_list=[]): + self.markers = [] + self.contents = [] + + marker = "" + content = [] + alphanum = string.letters + string.digits + "_" + + for line in block_line_list: + line2 = string.lstrip(line) + l = len(line2) + margin = len(line) - l + + if l > 3 and line2[0] == '<': + i = 1 + while i < l and line2[i] in alphanum: i = i+1 + if i < l and line2[i] == '>': + if marker or content: + self.add( marker, content ) + marker = line2[1:i] + content = [] + line2 = string.lstrip(line2[i+1:]) + l = len(line2) + line = " "*margin + line2 + + content.append(line) + + if marker or content: + self.add( marker, content ) + + + def add( self, marker, lines ): + + # remove the first and last empty lines from the content list + l = len(lines) + if l > 0: + i = 0 + while l > 0 and string.strip(lines[l-1]) == "": l = l-1 + while i < l and string.strip(lines[i]) == "": i = i+1 + lines = lines[i:l] + l = len(lines) + + # add a new marker only if its marker and its content list aren't empty + if l > 0 and marker: + self.markers.append(marker) + self.contents.append(lines) + + def dump( self ): + for i in range( len(self.markers) ): + print "["+self.markers[i]+"]" + for line in self.contents[i]: + print "-- "+line + + def doc_contents(self): + contents = [] + for item in self.contents: + contents.append( DocContent(item) ) + return contents + + +def dump_doc_blocks( block_list ): + for block in block_list: + docblock = DocBlock(block) + docblock.dump() + print "<<------------------->>" + + +# +# +# +def dump_single_content( block_list ): + + block = block_list[0] + docblock = DocBlock(block) + + print "" + for i in range(len(docblock.markers)): + marker = docblock.markers[i] + contents = docblock.contents[i] + + print "" + doccontent = DocContent( contents ) + + doccontent.dump() + + print "" + + print "" + +def dump_doc_contents( block_list ): + + for block in block_list: + docblock = DocBlock(block) + print "" + + for i in range(len(docblock.markers)): + print "" + content = DocContent( docblock.contents[i] ) + content.dump() + print "" + print "" + +def main(argv): + """main program loop""" + print "extracting comment blocks from sources .." + list = make_block_list() + +# dump_block_list( list ) + +# dump_doc_blocks( list ) + +# print "dumping block contents .." + dump_doc_contents(list) + +# dump_single_content(list) + +# If called from the command line +if __name__=='__main__': main(sys.argv)