/modules/freetype2/src/tools/docmaker/sources.py
Python | 347 lines | 289 code | 9 blank | 49 comment | 0 complexity | cfe5d21b8ece2c8d37d8ca88ac551411 MD5 | raw file
Possible License(s): JSON, LGPL-2.1, LGPL-3.0, AGPL-1.0, MIT, MPL-2.0-no-copyleft-exception, Apache-2.0, GPL-2.0, BSD-2-Clause, MPL-2.0, BSD-3-Clause, 0BSD
- # Sources (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009
- # David Turner <david@freetype.org>
- #
- #
- # this file contains definitions of classes needed to decompose
- # C sources files into a series of multi-line "blocks". There are
- # two kinds of blocks:
- #
- # - normal blocks, which contain source code or ordinary comments
- #
- # - documentation blocks, which have restricted formatting, and
- # whose text always start with a documentation markup tag like
- # "<Function>", "<Type>", etc..
- #
- # the routines used to process the content of documentation blocks
- # are not contained here, but in "content.py"
- #
- # the classes and methods found here only deal with text parsing
- # and basic documentation block extraction
- #
- import fileinput, re, sys, os, string
- ################################################################
- ##
- ## BLOCK FORMAT PATTERN
- ##
- ## A simple class containing compiled regular expressions used
- ## to detect potential documentation format block comments within
- ## C source code
- ##
- ## note that the 'column' pattern must contain a group that will
- ## be used to "unbox" the content of documentation comment blocks
- ##
- class SourceBlockFormat:
- def __init__( self, id, start, column, end ):
- """create a block pattern, used to recognize special documentation blocks"""
- self.id = id
- self.start = re.compile( start, re.VERBOSE )
- self.column = re.compile( column, re.VERBOSE )
- self.end = re.compile( end, re.VERBOSE )
- #
- # format 1 documentation comment blocks look like the following:
- #
- # /************************************/
- # /* */
- # /* */
- # /* */
- # /************************************/
- #
- # we define a few regular expressions here to detect them
- #
- start = r'''
- \s* # any number of whitespace
- /\*{2,}/ # followed by '/' and at least two asterisks then '/'
- \s*$ # probably followed by whitespace
- '''
- column = r'''
- \s* # any number of whitespace
- /\*{1} # followed by '/' and precisely one asterisk
- ([^*].*) # followed by anything (group 1)
- \*{1}/ # followed by one asterisk and a '/'
- \s*$ # probably followed by whitespace
- '''
- re_source_block_format1 = SourceBlockFormat( 1, start, column, start )
- #
- # format 2 documentation comment blocks look like the following:
- #
- # /************************************ (at least 2 asterisks)
- # *
- # *
- # *
- # *
- # **/ (1 or more asterisks at the end)
- #
- # we define a few regular expressions here to detect them
- #
- start = r'''
- \s* # any number of whitespace
- /\*{2,} # followed by '/' and at least two asterisks
- \s*$ # probably followed by whitespace
- '''
- column = r'''
- \s* # any number of whitespace
- \*{1}(?!/) # followed by precisely one asterisk not followed by `/'
- (.*) # then anything (group1)
- '''
- end = r'''
- \s* # any number of whitespace
- \*+/ # followed by at least one asterisk, then '/'
- '''
- re_source_block_format2 = SourceBlockFormat( 2, start, column, end )
- #
- # the list of supported documentation block formats, we could add new ones
- # relatively easily
- #
- re_source_block_formats = [re_source_block_format1, re_source_block_format2]
- #
- # the following regular expressions corresponds to markup tags
- # within the documentation comment blocks. they're equivalent
- # despite their different syntax
- #
- # notice how each markup tag _must_ begin a new line
- #
- re_markup_tag1 = re.compile( r'''\s*<(\w*)>''' ) # <xxxx> format
- re_markup_tag2 = re.compile( r'''\s*@(\w*):''' ) # @xxxx: format
- #
- # the list of supported markup tags, we could add new ones relatively
- # easily
- #
- re_markup_tags = [re_markup_tag1, re_markup_tag2]
- #
- # used to detect a cross-reference, after markup tags have been stripped
- #
- re_crossref = re.compile( r'@(\w*)(.*)' )
- #
- # used to detect italic and bold styles in paragraph text
- #
- re_italic = re.compile( r"_(\w(\w|')*)_(.*)" ) # _italic_
- re_bold = re.compile( r"\*(\w(\w|')*)\*(.*)" ) # *bold*
- #
- # used to detect the end of commented source lines
- #
- re_source_sep = re.compile( r'\s*/\*\s*\*/' )
- #
- # used to perform cross-reference within source output
- #
- re_source_crossref = re.compile( r'(\W*)(\w*)' )
- #
- # a list of reserved source keywords
- #
- re_source_keywords = re.compile( '''\\b ( typedef |
- struct |
- enum |
- union |
- const |
- char |
- int |
- short |
- long |
- void |
- signed |
- unsigned |
- \#include |
- \#define |
- \#undef |
- \#if |
- \#ifdef |
- \#ifndef |
- \#else |
- \#endif ) \\b''', re.VERBOSE )
- ################################################################
- ##
- ## SOURCE BLOCK CLASS
- ##
- ## A SourceProcessor is in charge of reading a C source file
- ## and decomposing it into a series of different "SourceBlocks".
- ## each one of these blocks can be made of the following data:
- ##
- ## - A documentation comment block that starts with "/**" and
- ## whose exact format will be discussed later
- ##
- ## - normal sources lines, including comments
- ##
- ## the important fields in a text block are the following ones:
- ##
- ## self.lines : a list of text lines for the corresponding block
- ##
- ## self.content : for documentation comment blocks only, this is the
- ## block content that has been "unboxed" from its
- ## decoration. This is None for all other blocks
- ## (i.e. sources or ordinary comments with no starting
- ## markup tag)
- ##
- class SourceBlock:
- def __init__( self, processor, filename, lineno, lines ):
- self.processor = processor
- self.filename = filename
- self.lineno = lineno
- self.lines = lines[:]
- self.format = processor.format
- self.content = []
- if self.format == None:
- return
- words = []
- # extract comment lines
- lines = []
- for line0 in self.lines:
- m = self.format.column.match( line0 )
- if m:
- lines.append( m.group( 1 ) )
- # now, look for a markup tag
- for l in lines:
- l = string.strip( l )
- if len( l ) > 0:
- for tag in re_markup_tags:
- if tag.match( l ):
- self.content = lines
- return
- def location( self ):
- return "(" + self.filename + ":" + repr( self.lineno ) + ")"
- # debugging only - not used in normal operations
- def dump( self ):
- if self.content:
- print "{{{content start---"
- for l in self.content:
- print l
- print "---content end}}}"
- return
- fmt = ""
- if self.format:
- fmt = repr( self.format.id ) + " "
- for line in self.lines:
- print line
- ################################################################
- ##
- ## SOURCE PROCESSOR CLASS
- ##
- ## The SourceProcessor is in charge of reading a C source file
- ## and decomposing it into a series of different "SourceBlock"
- ## objects.
- ##
- ## each one of these blocks can be made of the following data:
- ##
- ## - A documentation comment block that starts with "/**" and
- ## whose exact format will be discussed later
- ##
- ## - normal sources lines, include comments
- ##
- ##
- class SourceProcessor:
- def __init__( self ):
- """initialize a source processor"""
- self.blocks = []
- self.filename = None
- self.format = None
- self.lines = []
- def reset( self ):
- """reset a block processor, clean all its blocks"""
- self.blocks = []
- self.format = None
- def parse_file( self, filename ):
- """parse a C source file, and add its blocks to the processor's list"""
- self.reset()
- self.filename = filename
- fileinput.close()
- self.format = None
- self.lineno = 0
- self.lines = []
- for line in fileinput.input( filename ):
- # strip trailing newlines, important on Windows machines!
- if line[-1] == '\012':
- line = line[0:-1]
- if self.format == None:
- self.process_normal_line( line )
- else:
- if self.format.end.match( line ):
- # that's a normal block end, add it to 'lines' and
- # create a new block
- self.lines.append( line )
- self.add_block_lines()
- elif self.format.column.match( line ):
- # that's a normal column line, add it to 'lines'
- self.lines.append( line )
- else:
- # humm.. this is an unexpected block end,
- # create a new block, but don't process the line
- self.add_block_lines()
- # we need to process the line again
- self.process_normal_line( line )
- # record the last lines
- self.add_block_lines()
- def process_normal_line( self, line ):
- """process a normal line and check whether it is the start of a new block"""
- for f in re_source_block_formats:
- if f.start.match( line ):
- self.add_block_lines()
- self.format = f
- self.lineno = fileinput.filelineno()
- self.lines.append( line )
- def add_block_lines( self ):
- """add the current accumulated lines and create a new block"""
- if self.lines != []:
- block = SourceBlock( self, self.filename, self.lineno, self.lines )
- self.blocks.append( block )
- self.format = None
- self.lines = []
- # debugging only, not used in normal operations
- def dump( self ):
- """print all blocks in a processor"""
- for b in self.blocks:
- b.dump()
- # eof