ref: 205fc3faf29159a86c4f838edc587952afbb74aa
parent: a4e2894e03de65c5e0cd14b11b6fa30b14ed6769
author: David Turner <[email protected]>
date: Thu Oct 26 03:52:40 EDT 2000
updates to the API reference generators the basic parsing routines seem to work ok we now generate a list of DocBlock objects from a list of input file, we now need to sort them by "kind" (i.e. type/macro/functions) to generate several web pages, as well as a global index
--- a/docs/docmaker.py
+++ b/docs/docmaker.py
@@ -28,12 +28,9 @@
vlink="#51188E"
alink="#FF0000">
<center><h1>FreeType 2 API Reference</h1></center>
-<center><table width="80%"><tr><td>
"""
html_footer = """
-</td></tr>
-</table></center>
</body>
</html>
"""
@@ -49,12 +46,12 @@
para_header = "<p>"
para_footer = "</p>"
-block_header = """<table width="100%"><tr><td>"""
-block_footer = "</table>"
+block_header = """<center><hr width="550"><table width="550"><tr><td>"""
+block_footer = "</table></center>"
-source_header = """<table width="100%"><tr bgcolor="#D6E8FF" width="100%"><td><pre>
+source_header = """<center><table width="550"><tr bgcolor="#D6E8FF" width="100%"><td><pre>
"""
-source_footer = """</pre></table>
+source_footer = """</pre></table></center>
<br><br>
"""
@@ -144,175 +141,18 @@
#
-def make_block_list():
- """parse a file and extract comments blocks from it"""
- list = []
- block = []
- format = 0
-
- # we use "format" to store the state of our parser:
- #
- # 0 - wait for beginning of comment
- # 1 - parse comment format 1
- # 2 - parse comment format 2
- #
- # 4 - wait for beginning of source (or comment ??)
- # 5 - process source
- #
-
- comment = []
- source = []
- state = 0
-
- for line in fileinput.input():
-
- l = len( line )
- if l > 0 and line[l - 1] == '\012':
- line = line[0 : l - 1]
-
- # stripped version of the line
- line2 = string.strip( line )
- l = len( line2 )
-
- # if this line begins with a comment and we are processing some
- # source, exit to state 0
- #
- # unless we encounter something like:
- #
- # /*@.....
- # /*#.....
- #
- # /* @.....
- # /* #.....
- #
- if format >= 4 and l > 2 and line2[0 : 2] == '/*':
- if l < 4 or ( line2[3] != '@' and line2[3:4] != ' @' and
- line2[3] != '#' and line2[3:4] != ' #'):
- list.append( ( block, source ) )
- format = 0
-
- if format == 0: #### wait for beginning of comment ####
-
- if l > 3 and line2[0 : 3] == '/**':
- i = 3
- while i < l and line2[i] == '*':
- i = i + 1
-
- if i == l:
- # this is '/**' followed by any number of '*', the
- # beginning of a Format 1 block
- #
- block = []
- source = []
- format = 1
-
- elif i == l - 1 and line2[i] == '/':
- # this is '/**' followed by any number of '*', followed
- # by a '/', i.e. the beginning of a Format 2 or 3 block
- #
- block = []
- source = []
- 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 line2[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 line2[i] == '*':
- i = i + 1
-
- # test for the end of the block
- if i < l and line2[i] == '/':
- if block != []:
- format = 4
- else:
- format = 0
- else:
- # otherwise simply append line to current block
- block.append( line2 )
-
- continue
-
- ##############################################################
- #
- # 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 line2[: 2] != '/*' or line2[-2 :] != '*/':
- if block != []:
- format = 4
- else:
- format = 0
- else:
- # remove the start and end comment delimiters, then
- # right-strip the line
- line2 = string.rstrip( line2[2 : -2] )
-
- # check for end of a format2 block, i.e. a run of '*'
- if string.count( line2, '*' ) == l - 4:
- if block != []:
- format = 4
- else:
- format = 0
- else:
- # otherwise, add the line to the current block
- block.append( line2 )
-
- continue
-
-
-
- if format >= 4: #### source processing ####
-
- if l > 0:
- format = 5
-
- if format == 5:
- source.append( line )
-
-
- if format >= 4:
- list.append( [block, source] )
-
- 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[0]:
- print line
- for line in block[1]:
- print line
-
- print "---------the end-----------------------"
-
-
-
##############################################################################
#
# The DocCode class is used to store source code lines
#
+# self.lines contains a set of source code lines that will
+# be dumped as HTML in a <PRE> tag.
+#
+# the object is filled line by line by the parser, it strips the
+# leading "margin" space from each input line before storing it
+# in self.lines
+#
class DocCode:
def __init__( self, margin = 0 ):
@@ -325,27 +165,26 @@
line = line[self.margin :]
self.lines.append( line )
-
- def dump( self ):
- max_width = 50
-
+ def dump( self ):
for line in self.lines:
print "--" + line
-
print ""
+ def get_identifier( self ):
+ # this function should never be called
+ return "UNKNOWN_CODE_IDENTIFIER!!"
def dump_html( self ):
# clean the last empty lines
l = len( self.lines ) - 1
- while len > 0 and string.strip( lines[len - 1] ) == "":
- len = len - 1
+ while l > 0 and string.strip( self.lines[l - 1] ) == "":
+ l = l - 1
print code_header
- for line in self.lines[0 : len]:
- print lines
+ for line in self.lines[0 : l]:
+ print line
print code_footer
@@ -354,6 +193,8 @@
# The DocParagraph is used to store text paragraphs
# self.words is simply a list of words for the paragraph
#
+# the paragraph is filled line by line by the parser..
+#
class DocParagraph:
def __init__( self ):
@@ -370,7 +211,16 @@
#
last = len(self.words)
self.words[last:last] = string.split( line )
-
+
+ # this function is used to retrieve the first word of a given
+ # paragraph..
+ def get_identifier( self ):
+ if self.words:
+ return self.words[0]
+
+ # should never happen
+ return "UNKNOWN_PARA_IDENTIFIER!!"
+
def dump( self ):
@@ -391,7 +241,7 @@
if cursor > 0:
print line
- print ""
+ #print "�" #for debugging only
def dump_html( self ):
@@ -428,7 +278,12 @@
# ( "x", [ DocParagraph ] ),
# ( "y", [ DocParagraph, DocCode ] ) ]
#
+# in self.items
#
+# the DocContent object is entirely built at creation time, you must
+# pass a list of input text lines lin the "lines_list" parameter..
+#
+#
class DocContent:
def __init__( self, lines_list ):
@@ -436,10 +291,13 @@
code_mode = 0
code_margin = 0
text = []
- paragraph = None
- code = None
- elements = []
- field = None
+ paragraph = None # represents the current DocParagraph
+ code = None # represents the current DocCode
+
+ elements = [] # the list of elements for the current field,
+ # contains DocParagraph or DocCode objects
+
+ field = None # the current field
for aline in lines_list:
@@ -470,16 +328,21 @@
# the token `::'
#
if len( words ) >= 2 and words[1] == "::":
+
+ # start a new field - complete current paragraph if any
if paragraph:
elements.append( paragraph )
paragraph = None
+ # append previous "field" to self.items
self.items.append( ( field, elements ) )
+ # start new field and elements list
field = words[0]
elements = []
words = words[2 :]
-
+
+ # append remaining words to current paragraph
if len( words ) > 0:
line = string.join( words )
if not paragraph:
@@ -487,9 +350,12 @@
paragraph.add( line )
else:
+ # we're in code mode..
line = aline
# the code block ends with a line that has a single '}' on it
+ # that is located at the same column that the opening
+ # accolade..
if line == " " * code_margin + '}':
if code:
@@ -514,7 +380,16 @@
self.items.append( ( field, elements ) )
+ def get_identifier( self ):
+ if self.items:
+ item = self.items[0]
+ for element in item[1]:
+ return element.get_identifier()
+ # should never happen
+ return "UNKNOWN_CONTENT_IDENTIFIER!!"
+
+
def dump( self ):
for item in self.items:
field = item[0]
@@ -566,44 +441,67 @@
# 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.
#
+# "self.items" is a list of ( marker, contents ) elements, where
+# 'marker' is a lowercase marker string, and 'contents' is a DocContent
+# object
#
+# "self.source" is simply a list of text lines taken from the
+# uncommented source itself..
+#
+# finally, "self.identifier" is a simple identifier used to
+# uniquely identify the block
+#
class DocBlock:
def __init__( self, block_line_list = [], source_line_list = [] ):
- self.markers = []
- self.contents = []
- self.source = source_line_list
+ self.items = [] # current ( marker, contents ) list
+ self.identifier = None
+ marker = None # current marker
+ content = [] # current content lines list
+ alphanum = string.letters + string.digits + "_"
- 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:
+ ender = None
+ if line2[0] == '<':
+ ender = '>'
+ elif line2[0] == '@':
+ ender = ':'
+
+ if ender:
+ i = 1
+ while i < l and line2[i] in alphanum:
+ i = i + 1
+ if i < l and line2[i] == ender:
+ if marker and content:
+ self.add( marker, content )
+ marker = line2[1 : i]
+ content = []
+ line2 = string.lstrip( line2[i + 1 :] )
+ l = len( line2 )
+ line = " " * margin + line2
- 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:
+ if marker and content:
self.add( marker, content )
-
+ self.source = []
+ if self.items:
+ self.source = source_line_list
+
+
+ # this function is used to add a new element to self.items
+ # 'marker' is a marker string, or None
+ # 'lines' is a list of text lines used to compute a list of
+ # DocContent objects
+ #
def add( self, marker, lines ):
-
+
# remove the first and last empty lines from the content list
l = len( lines )
if l > 0:
@@ -617,124 +515,306 @@
# 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 )
+ content = DocContent(lines)
+ self.items.append( ( string.lower(marker), content ) )
+ if not self.identifier:
+ self.identifier = content.get_identifier()
+
+
def dump( self ):
- for i in range( len( self.markers ) ):
- print "[" + self.markers[i] + "]"
- for line in self.contents[i]:
- print "-- " + line
+ for i in range( len( self.items ) ):
+ print "[" + self.items[i][0] + "]"
+ content = self.items[i][1]
+ content.dump()
- def doc_contents( self ):
- contents = []
- for item in self.contents:
- contents.append( DocContent( item ) )
- return contents
+ def dump_html( self ):
+
+ types = [ 'type', 'struct', 'functype', 'function', 'constant',
+ 'enum', 'macro' ]
+ if not self.items:
+ return
+
+ # start of a block
+ print block_header
+
+ print "<h2>" + self.identifier + "</h2>"
-def dump_doc_blocks( block_list ):
- for block in block_list:
- docblock = DocBlock( block )
- docblock.dump()
- print "<<------------------->>"
+ # print source code
+ if not self.source:
+ return
+
+ lines = self.source
+ l = len( lines ) - 1
+ while l >= 0 and string.strip( lines[l] ) == "":
+ l = l - 1
+ print source_header
+ for line in lines[0 : l + 1]:
+ print line
+ print source_footer
+ # dump each (marker,content) element
+ for element in self.items:
+
+ marker = element[0]
+ content = element[1]
-#
-#
-#
-def dump_single_content( block_list ):
+ if marker == "description":
+ print "<ul>"
+ content.dump_html()
+ print "</ul>"
+
+ elif not (marker in types):
+ print "<h4>" + marker + "</h4>"
+ print "<ul>"
+ content.dump_html()
+ print "</ul>"
+
+ print ""
- block = block_list[0]
- docblock = DocBlock( block )
+ print block_footer
- print "<block>"
- for i in range( len( docblock.markers ) ):
- marker = docblock.markers[i]
- contents = docblock.contents[i]
-
- print "<marker " + marker + ">"
- doccontent = DocContent( contents )
- doccontent.dump()
-
- print "</marker>"
+# filter a given list of DocBlocks. Returns a new list
+# of DocBlock objects that only contains element whose
+# "type" (i.e. first marker) is in the "types" parameter
+#
+def filter_blocks( block_list, types ):
+
+ new_list = []
+ for block in block_list:
+ if block.items:
+ element = block.items[0]
+ marker = element[0]
+ if marker in types:
+ new_list.append( block )
+
+ return new_list
+
+
+# perform a lexicographical comparison of two DocBlock
+# objects. Returns -1, 0 or 1
+#
+def block_lexicographical_compare( b1, b2 ):
+ if not b1.identifier:
+ return -1
+ if not b2.identifier:
+ return 1
- print "</block>"
+ id1 = string.lower(b1.identifier)
+ id2 = string.lower(b2.identifier)
+ if id1 < id2:
+ return -1
+ elif id1 == id2:
+ return 0
+ else:
+ return 1
+
+def block_make_list( source_block_list ):
+ list = []
-def dump_doc_contents( block_list ):
+ for block in source_block_list:
+ docblock = DocBlock( block[0], block[1] )
+ list.append( docblock )
- for block in block_list:
- docblock = DocBlock( block )
- print "<block>"
-
- for i in range( len( docblock.markers ) ):
- print "<marker " + docblock.markers[i] + ">"
- content = DocContent( docblock.contents[i] )
- content.dump()
- print "</marker>"
- print "</block>"
+ return list
+# dump a list block as a single HTML page
+#
def dump_html_1( block_list ):
-
+
print html_header
- types = [ 'Type', 'Struct', 'FuncType', 'Function', 'Constant',
- 'Enumeration' ]
+ for block in block_list:
+ block.dump_html()
+
+ print html_footer
- for block in block_list:
-
- docblock = DocBlock( block[0], block[1] )
-
- if len( docblock.markers ) == 0:
- continue
+
+
+
+def make_block_list():
+ """parse a file and extract comments blocks from it"""
+
+ list = []
+ block = []
+ format = 0
+
+ # we use "format" to store the state of our parser:
+ #
+ # 0 - wait for beginning of comment
+ # 1 - parse comment format 1
+ # 2 - parse comment format 2
+ #
+ # 4 - wait for beginning of source (or comment ??)
+ # 5 - process source
+ #
+
+ comment = []
+ source = []
+ state = 0
+
+ for line in fileinput.input():
+
+ l = len( line )
+ if l > 0 and line[l - 1] == '\012':
+ line = line[0 : l - 1]
+
+ # stripped version of the line
+ line2 = string.strip( line )
+ l = len( line2 )
+
+ # if this line begins with a comment and we are processing some
+ # source, exit to state 0
+ #
+ # unless we encounter something like:
+ #
+ # /*@.....
+ # /*#.....
+ #
+ # /* @.....
+ # /* #.....
+ #
+ if format >= 4 and l > 2 and line2[0 : 2] == '/*':
+ if l < 4 or ( line2[3] != '@' and line2[3:4] != ' @' and
+ line2[3] != '#' and line2[3:4] != ' #'):
+ list.append( ( block, source ) )
+ format = 0
+
+ if format == 0: #### wait for beginning of comment ####
+
+ if l > 3 and line2[0 : 3] == '/**':
+ i = 3
+ while i < l and line2[i] == '*':
+ i = i + 1
+
+ if i == l:
+ # this is '/**' followed by any number of '*', the
+ # beginning of a Format 1 block
+ #
+ block = []
+ source = []
+ format = 1
+
+ elif i == l - 1 and line2[i] == '/':
+ # this is '/**' followed by any number of '*', followed
+ # by a '/', i.e. the beginning of a Format 2 or 3 block
+ #
+ block = []
+ source = []
+ 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 line2[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 line2[i] == '*':
+ i = i + 1
+
+ # test for the end of the block
+ if i < l and line2[i] == '/':
+ if block != []:
+ format = 4
+ else:
+ format = 0
+ else:
+ # otherwise simply append line to current block
+ block.append( line2[i:] )
+
+ continue
+
+ ##############################################################
+ #
+ # 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 line2[: 2] != '/*' or line2[-2 :] != '*/':
+ if block != []:
+ format = 4
+ else:
+ format = 0
+ else:
+ # remove the start and end comment delimiters, then
+ # right-strip the line
+ line2 = string.rstrip( line2[2 : -2] )
+
+ # check for end of a format2 block, i.e. a run of '*'
+ if string.count( line2, '*' ) == l - 4:
+ if block != []:
+ format = 4
+ else:
+ format = 0
+ else:
+ # otherwise, add the line to the current block
+ block.append( line2 )
+
+ continue
- print block_header
- for i in range( len( docblock.markers ) ):
- marker = docblock.markers[i]
- content = docblock.contents[i]
- dcontent = DocContent( content )
-
- if marker == "Description":
- print "<ul><p>"
- dcontent.dump()
- print "</p></ul>"
-
- elif marker in types:
- print "<h3><font color=blue>" + content[0] + "</font></h3>"
- else:
- print "<h4>" + marker + "</h4>"
- print "<ul><p>"
- dcontent.dump_html()
- print "</p></ul>"
-
- print ""
- print block_footer
-
- # print source code
- lines = block[1]
- l = len( lines ) - 1
- while l >= 0 and string.strip( lines[l] ) == "":
- l = l - 1
- print source_header
- for line in lines[0 : l + 1]:
- print line
- print source_footer
-
- print html_footer
+ if format >= 4: #### source processing ####
+
+ if l > 0:
+ format = 5
+
+ if format == 5:
+ source.append( line )
+
+
+ if format >= 4:
+ list.append( [block, source] )
+
+ 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[0]:
+ print line
+ for line in block[1]:
+ print line
+ print "---------the end-----------------------"
+
+
+
def main( argv ):
"""main program loop"""
sys.stderr.write( "extracting comment blocks from sources...\n" )
list = make_block_list()
+ list = block_make_list(list)
- dump_html_1( list )
+ list2 = filter_blocks( list, ['type','macro','enum','constant', 'functype'] )
+ #list2 = list
+ list2.sort( block_lexicographical_compare )
+
+ dump_html_1( list2 )
+ #dump_doc_blocks( list )
+ #dump_block_lists( list )
+ #dump_html_1( list )
# If called from the command line