[FREETYPE] Update to v2.6.3. CORE-10964
[reactos.git] / reactos / lib / 3rdparty / freetype / src / tools / docmaker / sources.py
1 #
2 # sources.py
3 #
4 # Convert source code comments to multi-line blocks (library file).
5 #
6 # Copyright 2002-2016 by
7 # David Turner.
8 #
9 # This file is part of the FreeType project, and may only be used,
10 # modified, and distributed under the terms of the FreeType project
11 # license, LICENSE.TXT. By continuing to use, modify, or distribute
12 # this file you indicate that you have read the license and
13 # understand and accept it fully.
14
15 #
16 # This library file contains definitions of classes needed to decompose C
17 # source code files into a series of multi-line `blocks'. There are two
18 # kinds of blocks.
19 #
20 # - Normal blocks, which contain source code or ordinary comments.
21 #
22 # - Documentation blocks, which have restricted formatting, and whose text
23 # always start with a documentation markup tag like `<Function>',
24 # `<Type>', etc.
25 #
26 # The routines to process the content of documentation blocks are contained
27 # in file `content.py'; the classes and methods found here only deal with
28 # text parsing and basic documentation block extraction.
29 #
30
31
32 import fileinput, re, sys, os, string
33
34
35 ################################################################
36 ##
37 ## SOURCE BLOCK FORMAT CLASS
38 ##
39 ## A simple class containing compiled regular expressions to detect
40 ## potential documentation format block comments within C source code.
41 ##
42 ## The `column' pattern must contain a group to `unbox' the content of
43 ## documentation comment blocks.
44 ##
45 ## Later on, paragraphs are converted to long lines, which simplifies the
46 ## regular expressions that act upon the text.
47 ##
48 class SourceBlockFormat:
49
50 def __init__( self, id, start, column, end ):
51 """Create a block pattern, used to recognize special documentation
52 blocks."""
53 self.id = id
54 self.start = re.compile( start, re.VERBOSE )
55 self.column = re.compile( column, re.VERBOSE )
56 self.end = re.compile( end, re.VERBOSE )
57
58
59 #
60 # Format 1 documentation comment blocks.
61 #
62 # /************************************/ (at least 2 asterisks)
63 # /* */
64 # /* */
65 # /* */
66 # /************************************/ (at least 2 asterisks)
67 #
68 start = r'''
69 \s* # any number of whitespace
70 /\*{2,}/ # followed by '/' and at least two asterisks then '/'
71 \s*$ # probably followed by whitespace
72 '''
73
74 column = r'''
75 \s* # any number of whitespace
76 /\*{1} # followed by '/' and precisely one asterisk
77 ([^*].*) # followed by anything (group 1)
78 \*{1}/ # followed by one asterisk and a '/'
79 \s*$ # probably followed by whitespace
80 '''
81
82 re_source_block_format1 = SourceBlockFormat( 1, start, column, start )
83
84
85 #
86 # Format 2 documentation comment blocks.
87 #
88 # /************************************ (at least 2 asterisks)
89 # *
90 # * (1 asterisk)
91 # *
92 # */ (1 or more asterisks)
93 #
94 start = r'''
95 \s* # any number of whitespace
96 /\*{2,} # followed by '/' and at least two asterisks
97 \s*$ # probably followed by whitespace
98 '''
99
100 column = r'''
101 \s* # any number of whitespace
102 \*{1}(?![*/]) # followed by precisely one asterisk not followed by `/'
103 (.*) # then anything (group1)
104 '''
105
106 end = r'''
107 \s* # any number of whitespace
108 \*+/ # followed by at least one asterisk, then '/'
109 '''
110
111 re_source_block_format2 = SourceBlockFormat( 2, start, column, end )
112
113
114 #
115 # The list of supported documentation block formats. We could add new ones
116 # quite easily.
117 #
118 re_source_block_formats = [re_source_block_format1, re_source_block_format2]
119
120
121 #
122 # The following regular expressions correspond to markup tags within the
123 # documentation comment blocks. They are equivalent despite their different
124 # syntax.
125 #
126 # A markup tag consists of letters or character `-', to be found in group 1.
127 #
128 # Notice that a markup tag _must_ begin a new paragraph.
129 #
130 re_markup_tag1 = re.compile( r'''\s*<((?:\w|-)*)>''' ) # <xxxx> format
131 re_markup_tag2 = re.compile( r'''\s*@((?:\w|-)*):''' ) # @xxxx: format
132
133 #
134 # The list of supported markup tags. We could add new ones quite easily.
135 #
136 re_markup_tags = [re_markup_tag1, re_markup_tag2]
137
138
139 #
140 # A regular expression to detect a cross reference, after markup tags have
141 # been stripped off.
142 #
143 # Two syntax forms are supported:
144 #
145 # @<name>
146 # @<name>[<id>]
147 #
148 # where both `<name>' and `<id>' consist of alphanumeric characters, `_',
149 # and `-'. Use `<id>' if there are multiple, valid `<name>' entries.
150 #
151 # Example: @foo[bar]
152 #
153 re_crossref = re.compile( r"""
154 @
155 (?P<name>(?:\w|-)+
156 (?:\[(?:\w|-)+\])?)
157 (?P<rest>.*)
158 """, re.VERBOSE )
159
160 #
161 # Two regular expressions to detect italic and bold markup, respectively.
162 # Group 1 is the markup, group 2 the rest of the line.
163 #
164 # Note that the markup is limited to words consisting of letters, digits,
165 # the characters `_' and `-', or an apostrophe (but not as the first
166 # character).
167 #
168 re_italic = re.compile( r"_((?:\w|-)(?:\w|'|-)*)_(.*)" ) # _italic_
169 re_bold = re.compile( r"\*((?:\w|-)(?:\w|'|-)*)\*(.*)" ) # *bold*
170
171 #
172 # This regular expression code to identify an URL has been taken from
173 #
174 # http://mail.python.org/pipermail/tutor/2002-September/017228.html
175 #
176 # (with slight modifications).
177 #
178 urls = r'(?:https?|telnet|gopher|file|wais|ftp)'
179 ltrs = r'\w'
180 gunk = r'/#~:.?+=&%@!\-'
181 punc = r'.:?\-'
182 any = "%(ltrs)s%(gunk)s%(punc)s" % { 'ltrs' : ltrs,
183 'gunk' : gunk,
184 'punc' : punc }
185 url = r"""
186 (
187 \b # start at word boundary
188 %(urls)s : # need resource and a colon
189 [%(any)s] +? # followed by one or more of any valid
190 # character, but be conservative and
191 # take only what you need to...
192 (?= # [look-ahead non-consumptive assertion]
193 [%(punc)s]* # either 0 or more punctuation
194 (?: # [non-grouping parentheses]
195 [^%(any)s] | $ # followed by a non-url char
196 # or end of the string
197 )
198 )
199 )
200 """ % {'urls' : urls,
201 'any' : any,
202 'punc' : punc }
203
204 re_url = re.compile( url, re.VERBOSE | re.MULTILINE )
205
206 #
207 # A regular expression that stops collection of comments for the current
208 # block.
209 #
210 re_source_sep = re.compile( r'\s*/\*\s*\*/' ) # /* */
211
212 #
213 # A regular expression to find possible C identifiers while outputting
214 # source code verbatim, covering things like `*foo' or `(bar'. Group 1 is
215 # the prefix, group 2 the identifier -- since we scan lines from left to
216 # right, sequentially splitting the source code into prefix and identifier
217 # is fully sufficient for our purposes.
218 #
219 re_source_crossref = re.compile( r'(\W*)(\w*)' )
220
221 #
222 # A regular expression that matches a list of reserved C source keywords.
223 #
224 re_source_keywords = re.compile( '''\\b ( typedef |
225 struct |
226 enum |
227 union |
228 const |
229 char |
230 int |
231 short |
232 long |
233 void |
234 signed |
235 unsigned |
236 \#include |
237 \#define |
238 \#undef |
239 \#if |
240 \#ifdef |
241 \#ifndef |
242 \#else |
243 \#endif ) \\b''', re.VERBOSE )
244
245
246 ################################################################
247 ##
248 ## SOURCE BLOCK CLASS
249 ##
250 ## There are two important fields in a `SourceBlock' object.
251 ##
252 ## self.lines
253 ## A list of text lines for the corresponding block.
254 ##
255 ## self.content
256 ## For documentation comment blocks only, this is the block content
257 ## that has been `unboxed' from its decoration. This is `None' for all
258 ## other blocks (i.e., sources or ordinary comments with no starting
259 ## markup tag)
260 ##
261 class SourceBlock:
262
263 def __init__( self, processor, filename, lineno, lines ):
264 self.processor = processor
265 self.filename = filename
266 self.lineno = lineno
267 self.lines = lines[:]
268 self.format = processor.format
269 self.content = []
270
271 if self.format == None:
272 return
273
274 words = []
275
276 # extract comment lines
277 lines = []
278
279 for line0 in self.lines:
280 m = self.format.column.match( line0 )
281 if m:
282 lines.append( m.group( 1 ) )
283
284 # now, look for a markup tag
285 for l in lines:
286 l = string.strip( l )
287 if len( l ) > 0:
288 for tag in re_markup_tags:
289 if tag.match( l ):
290 self.content = lines
291 return
292
293 def location( self ):
294 return "(" + self.filename + ":" + repr( self.lineno ) + ")"
295
296 # debugging only -- not used in normal operations
297 def dump( self ):
298 if self.content:
299 print "{{{content start---"
300 for l in self.content:
301 print l
302 print "---content end}}}"
303 return
304
305 fmt = ""
306 if self.format:
307 fmt = repr( self.format.id ) + " "
308
309 for line in self.lines:
310 print line
311
312
313 ################################################################
314 ##
315 ## SOURCE PROCESSOR CLASS
316 ##
317 ## The `SourceProcessor' is in charge of reading a C source file and
318 ## decomposing it into a series of different `SourceBlock' objects.
319 ##
320 ## A SourceBlock object consists of the following data.
321 ##
322 ## - A documentation comment block using one of the layouts above. Its
323 ## exact format will be discussed later.
324 ##
325 ## - Normal sources lines, including comments.
326 ##
327 ##
328 class SourceProcessor:
329
330 def __init__( self ):
331 """Initialize a source processor."""
332 self.blocks = []
333 self.filename = None
334 self.format = None
335 self.lines = []
336
337 def reset( self ):
338 """Reset a block processor and clean up all its blocks."""
339 self.blocks = []
340 self.format = None
341
342 def parse_file( self, filename ):
343 """Parse a C source file and add its blocks to the processor's
344 list."""
345 self.reset()
346
347 self.filename = filename
348
349 fileinput.close()
350 self.format = None
351 self.lineno = 0
352 self.lines = []
353
354 for line in fileinput.input( filename ):
355 # strip trailing newlines, important on Windows machines!
356 if line[-1] == '\012':
357 line = line[0:-1]
358
359 if self.format == None:
360 self.process_normal_line( line )
361 else:
362 if self.format.end.match( line ):
363 # A normal block end. Add it to `lines' and create a
364 # new block
365 self.lines.append( line )
366 self.add_block_lines()
367 elif self.format.column.match( line ):
368 # A normal column line. Add it to `lines'.
369 self.lines.append( line )
370 else:
371 # An unexpected block end. Create a new block, but
372 # don't process the line.
373 self.add_block_lines()
374
375 # we need to process the line again
376 self.process_normal_line( line )
377
378 # record the last lines
379 self.add_block_lines()
380
381 def process_normal_line( self, line ):
382 """Process a normal line and check whether it is the start of a new
383 block."""
384 for f in re_source_block_formats:
385 if f.start.match( line ):
386 self.add_block_lines()
387 self.format = f
388 self.lineno = fileinput.filelineno()
389
390 self.lines.append( line )
391
392 def add_block_lines( self ):
393 """Add the current accumulated lines and create a new block."""
394 if self.lines != []:
395 block = SourceBlock( self,
396 self.filename,
397 self.lineno,
398 self.lines )
399
400 self.blocks.append( block )
401 self.format = None
402 self.lines = []
403
404 # debugging only, not used in normal operations
405 def dump( self ):
406 """Print all blocks in a processor."""
407 for b in self.blocks:
408 b.dump()
409
410 # eof