PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/freetype2/src/tools/docmaker/sources.py

https://bitbucket.org/thinker/mozilla-central
Python | 347 lines | 289 code | 9 blank | 49 comment | 0 complexity | cfe5d21b8ece2c8d37d8ca88ac551411 MD5 | raw file
Possible License(s): JSON, 0BSD, LGPL-3.0, BSD-2-Clause, MIT, MPL-2.0-no-copyleft-exception, BSD-3-Clause, GPL-2.0, AGPL-1.0, MPL-2.0, Apache-2.0, LGPL-2.1
  1. # Sources (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009
  2. # David Turner <david@freetype.org>
  3. #
  4. #
  5. # this file contains definitions of classes needed to decompose
  6. # C sources files into a series of multi-line "blocks". There are
  7. # two kinds of blocks:
  8. #
  9. # - normal blocks, which contain source code or ordinary comments
  10. #
  11. # - documentation blocks, which have restricted formatting, and
  12. # whose text always start with a documentation markup tag like
  13. # "<Function>", "<Type>", etc..
  14. #
  15. # the routines used to process the content of documentation blocks
  16. # are not contained here, but in "content.py"
  17. #
  18. # the classes and methods found here only deal with text parsing
  19. # and basic documentation block extraction
  20. #
  21. import fileinput, re, sys, os, string
  22. ################################################################
  23. ##
  24. ## BLOCK FORMAT PATTERN
  25. ##
  26. ## A simple class containing compiled regular expressions used
  27. ## to detect potential documentation format block comments within
  28. ## C source code
  29. ##
  30. ## note that the 'column' pattern must contain a group that will
  31. ## be used to "unbox" the content of documentation comment blocks
  32. ##
  33. class SourceBlockFormat:
  34. def __init__( self, id, start, column, end ):
  35. """create a block pattern, used to recognize special documentation blocks"""
  36. self.id = id
  37. self.start = re.compile( start, re.VERBOSE )
  38. self.column = re.compile( column, re.VERBOSE )
  39. self.end = re.compile( end, re.VERBOSE )
  40. #
  41. # format 1 documentation comment blocks look like the following:
  42. #
  43. # /************************************/
  44. # /* */
  45. # /* */
  46. # /* */
  47. # /************************************/
  48. #
  49. # we define a few regular expressions here to detect them
  50. #
  51. start = r'''
  52. \s* # any number of whitespace
  53. /\*{2,}/ # followed by '/' and at least two asterisks then '/'
  54. \s*$ # probably followed by whitespace
  55. '''
  56. column = r'''
  57. \s* # any number of whitespace
  58. /\*{1} # followed by '/' and precisely one asterisk
  59. ([^*].*) # followed by anything (group 1)
  60. \*{1}/ # followed by one asterisk and a '/'
  61. \s*$ # probably followed by whitespace
  62. '''
  63. re_source_block_format1 = SourceBlockFormat( 1, start, column, start )
  64. #
  65. # format 2 documentation comment blocks look like the following:
  66. #
  67. # /************************************ (at least 2 asterisks)
  68. # *
  69. # *
  70. # *
  71. # *
  72. # **/ (1 or more asterisks at the end)
  73. #
  74. # we define a few regular expressions here to detect them
  75. #
  76. start = r'''
  77. \s* # any number of whitespace
  78. /\*{2,} # followed by '/' and at least two asterisks
  79. \s*$ # probably followed by whitespace
  80. '''
  81. column = r'''
  82. \s* # any number of whitespace
  83. \*{1}(?!/) # followed by precisely one asterisk not followed by `/'
  84. (.*) # then anything (group1)
  85. '''
  86. end = r'''
  87. \s* # any number of whitespace
  88. \*+/ # followed by at least one asterisk, then '/'
  89. '''
  90. re_source_block_format2 = SourceBlockFormat( 2, start, column, end )
  91. #
  92. # the list of supported documentation block formats, we could add new ones
  93. # relatively easily
  94. #
  95. re_source_block_formats = [re_source_block_format1, re_source_block_format2]
  96. #
  97. # the following regular expressions corresponds to markup tags
  98. # within the documentation comment blocks. they're equivalent
  99. # despite their different syntax
  100. #
  101. # notice how each markup tag _must_ begin a new line
  102. #
  103. re_markup_tag1 = re.compile( r'''\s*<(\w*)>''' ) # <xxxx> format
  104. re_markup_tag2 = re.compile( r'''\s*@(\w*):''' ) # @xxxx: format
  105. #
  106. # the list of supported markup tags, we could add new ones relatively
  107. # easily
  108. #
  109. re_markup_tags = [re_markup_tag1, re_markup_tag2]
  110. #
  111. # used to detect a cross-reference, after markup tags have been stripped
  112. #
  113. re_crossref = re.compile( r'@(\w*)(.*)' )
  114. #
  115. # used to detect italic and bold styles in paragraph text
  116. #
  117. re_italic = re.compile( r"_(\w(\w|')*)_(.*)" ) # _italic_
  118. re_bold = re.compile( r"\*(\w(\w|')*)\*(.*)" ) # *bold*
  119. #
  120. # used to detect the end of commented source lines
  121. #
  122. re_source_sep = re.compile( r'\s*/\*\s*\*/' )
  123. #
  124. # used to perform cross-reference within source output
  125. #
  126. re_source_crossref = re.compile( r'(\W*)(\w*)' )
  127. #
  128. # a list of reserved source keywords
  129. #
  130. re_source_keywords = re.compile( '''\\b ( typedef |
  131. struct |
  132. enum |
  133. union |
  134. const |
  135. char |
  136. int |
  137. short |
  138. long |
  139. void |
  140. signed |
  141. unsigned |
  142. \#include |
  143. \#define |
  144. \#undef |
  145. \#if |
  146. \#ifdef |
  147. \#ifndef |
  148. \#else |
  149. \#endif ) \\b''', re.VERBOSE )
  150. ################################################################
  151. ##
  152. ## SOURCE BLOCK CLASS
  153. ##
  154. ## A SourceProcessor is in charge of reading a C source file
  155. ## and decomposing it into a series of different "SourceBlocks".
  156. ## each one of these blocks can be made of the following data:
  157. ##
  158. ## - A documentation comment block that starts with "/**" and
  159. ## whose exact format will be discussed later
  160. ##
  161. ## - normal sources lines, including comments
  162. ##
  163. ## the important fields in a text block are the following ones:
  164. ##
  165. ## self.lines : a list of text lines for the corresponding block
  166. ##
  167. ## self.content : for documentation comment blocks only, this is the
  168. ## block content that has been "unboxed" from its
  169. ## decoration. This is None for all other blocks
  170. ## (i.e. sources or ordinary comments with no starting
  171. ## markup tag)
  172. ##
  173. class SourceBlock:
  174. def __init__( self, processor, filename, lineno, lines ):
  175. self.processor = processor
  176. self.filename = filename
  177. self.lineno = lineno
  178. self.lines = lines[:]
  179. self.format = processor.format
  180. self.content = []
  181. if self.format == None:
  182. return
  183. words = []
  184. # extract comment lines
  185. lines = []
  186. for line0 in self.lines:
  187. m = self.format.column.match( line0 )
  188. if m:
  189. lines.append( m.group( 1 ) )
  190. # now, look for a markup tag
  191. for l in lines:
  192. l = string.strip( l )
  193. if len( l ) > 0:
  194. for tag in re_markup_tags:
  195. if tag.match( l ):
  196. self.content = lines
  197. return
  198. def location( self ):
  199. return "(" + self.filename + ":" + repr( self.lineno ) + ")"
  200. # debugging only - not used in normal operations
  201. def dump( self ):
  202. if self.content:
  203. print "{{{content start---"
  204. for l in self.content:
  205. print l
  206. print "---content end}}}"
  207. return
  208. fmt = ""
  209. if self.format:
  210. fmt = repr( self.format.id ) + " "
  211. for line in self.lines:
  212. print line
  213. ################################################################
  214. ##
  215. ## SOURCE PROCESSOR CLASS
  216. ##
  217. ## The SourceProcessor is in charge of reading a C source file
  218. ## and decomposing it into a series of different "SourceBlock"
  219. ## objects.
  220. ##
  221. ## each one of these blocks can be made of the following data:
  222. ##
  223. ## - A documentation comment block that starts with "/**" and
  224. ## whose exact format will be discussed later
  225. ##
  226. ## - normal sources lines, include comments
  227. ##
  228. ##
  229. class SourceProcessor:
  230. def __init__( self ):
  231. """initialize a source processor"""
  232. self.blocks = []
  233. self.filename = None
  234. self.format = None
  235. self.lines = []
  236. def reset( self ):
  237. """reset a block processor, clean all its blocks"""
  238. self.blocks = []
  239. self.format = None
  240. def parse_file( self, filename ):
  241. """parse a C source file, and add its blocks to the processor's list"""
  242. self.reset()
  243. self.filename = filename
  244. fileinput.close()
  245. self.format = None
  246. self.lineno = 0
  247. self.lines = []
  248. for line in fileinput.input( filename ):
  249. # strip trailing newlines, important on Windows machines!
  250. if line[-1] == '\012':
  251. line = line[0:-1]
  252. if self.format == None:
  253. self.process_normal_line( line )
  254. else:
  255. if self.format.end.match( line ):
  256. # that's a normal block end, add it to 'lines' and
  257. # create a new block
  258. self.lines.append( line )
  259. self.add_block_lines()
  260. elif self.format.column.match( line ):
  261. # that's a normal column line, add it to 'lines'
  262. self.lines.append( line )
  263. else:
  264. # humm.. this is an unexpected block end,
  265. # create a new block, but don't process the line
  266. self.add_block_lines()
  267. # we need to process the line again
  268. self.process_normal_line( line )
  269. # record the last lines
  270. self.add_block_lines()
  271. def process_normal_line( self, line ):
  272. """process a normal line and check whether it is the start of a new block"""
  273. for f in re_source_block_formats:
  274. if f.start.match( line ):
  275. self.add_block_lines()
  276. self.format = f
  277. self.lineno = fileinput.filelineno()
  278. self.lines.append( line )
  279. def add_block_lines( self ):
  280. """add the current accumulated lines and create a new block"""
  281. if self.lines != []:
  282. block = SourceBlock( self, self.filename, self.lineno, self.lines )
  283. self.blocks.append( block )
  284. self.format = None
  285. self.lines = []
  286. # debugging only, not used in normal operations
  287. def dump( self ):
  288. """print all blocks in a processor"""
  289. for b in self.blocks:
  290. b.dump()
  291. # eof