CRIS: Add new peephole2 "lra_szext_decomposed_indir_plus"
[official-gcc.git] / gcc / m2 / tools-src / def2doc.py
blob3ccf32450d02d6dca19041950dd88f733e36e90d
1 #!/usr/bin/env python3
3 # def2doc.py creates texi library documentation for all exported procedures.
4 # Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 # Copyright (C) 2000-2024 Free Software Foundation, Inc.
7 # This file is part of GNU Modula-2.
9 # GNU Modula-2 is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3, or (at your option)
12 # any later version.
14 # GNU Modula-2 is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with GCC; see the file COPYING3. If not see
21 # <http://www.gnu.org/licenses/>.
24 import argparse
25 import os
26 import sys
28 Base_Libs = ['gm2-libs', 'Base libraries', 'Basic M2F compatible libraries']
30 PIM_Log_Desc = 'PIM and Logitech 3.0 compatible libraries'
31 PIM_Log = ['gm2-libs-log', 'PIM and Logitech 3.0 Compatible', PIM_Log_Desc]
32 PIM_Cor_Desc = 'PIM compatible process support'
33 PIM_Cor = ['gm2-libs-coroutines', 'PIM coroutine support', PIM_Cor_Desc]
34 ISO_Libs = ['gm2-libs-iso', 'M2 ISO Libraries', 'ISO defined libraries']
36 library_classifications = [Base_Libs, PIM_Log, PIM_Cor, ISO_Libs]
38 # state_states
39 state_none, state_var, state_type, state_const = range(4)
40 # block states
41 block_none, block_code, block_text, block_index = range(4)
44 class state:
45 def __init__(self):
46 self._state_state = state_none
47 self._block = block_none
49 def get_state(self):
50 return self._state_state
52 def set_state(self, value):
53 self._state_state = value
55 def is_const(self):
56 return self._state_state == state_const
58 def is_type(self):
59 return self._state_state == state_type
61 def is_var(self):
62 return self._state_state == state_var
64 def get_block(self):
65 return self._block
67 def _change_block(self, new_block):
68 if self._block != new_block:
69 self._block = new_block
70 self._emit_block_desc()
72 def _emit_block_desc(self):
73 if self._block == block_code:
74 output.write('.. code-block:: modula2\n')
75 elif self._block == block_index:
76 output.write('.. index::\n')
78 def to_code(self):
79 self._change_block(block_code)
81 def to_index(self):
82 self._change_block(block_index)
85 def init_state():
86 global state_obj
87 state_obj = state()
90 def emit_node(name, nxt, previous, up):
91 if args.texinfo:
92 output.write('@node ' + name + ', ' + nxt + ', ')
93 output.write(previous + ', ' + up + '\n')
94 elif args.sphinx:
95 output.write('@c @node ' + name + ', ' + nxt + ', ')
96 output.write(previous + ', ' + up + '\n')
99 def emit_section(name):
100 if args.texinfo:
101 output.write('@section ' + name + '\n')
102 elif args.sphinx:
103 output.write(name + '\n')
104 output.write('=' * len(name) + '\n')
107 def emit_sub_section(name):
108 if args.texinfo:
109 output.write('@subsection ' + name + '\n')
110 elif args.sphinx:
111 output.write(name + '\n')
112 output.write('-' * len(name) + '\n')
115 def display_library_class():
116 # display_library_class displays a node for a library directory and invokes
117 # a routine to summarize each module.
118 global args
119 previous = ''
120 nxt = library_classifications[1][1]
121 i = 0
122 lib = library_classifications[i]
123 while True:
124 emit_node(lib[1], nxt, previous, args.up)
125 emit_section(lib[1])
126 output.write('\n')
127 display_modules(lib[1], lib[0], args.builddir, args.sourcedir)
128 output.write('\n')
129 output.write('@c ' + '-' * 60 + '\n')
130 previous = lib[1]
131 i += 1
132 if i == len(library_classifications):
133 break
134 lib = library_classifications[i]
135 if i+1 == len(library_classifications):
136 nxt = ''
137 else:
138 nxt = library_classifications[i+1][1]
141 def display_menu():
142 # display_menu displays the top level menu for library documentation.
143 output.write('@menu\n')
144 for lib in library_classifications:
145 output.write('* ' + lib[1] + '::' + lib[2] + '\n')
146 output.write('@end menu\n')
147 output.write('\n')
148 output.write('@c ' + '=' * 60 + '\n')
149 output.write('\n')
152 def remote_initial_comments(file, line):
153 # remote_initial_comments removes any (* *) at the top
154 # of the definition module.
155 while (line.find('*)') == -1):
156 line = file.readline()
159 def removeable_field(line):
160 # removeable_field - returns True if a comment field should be removed
161 # from the definition module.
162 field_list = ['Author', 'Last edit', 'LastEdit', 'Last update',
163 'Date', 'Title', 'Revision']
164 for field in field_list:
165 if (line.find(field) != -1) and (line.find(':') != -1):
166 return True
167 ignore_list = ['System', 'SYSTEM']
168 for ignore_field in ignore_list:
169 if line.find(ignore_field) != -1:
170 if line.find(':') != -1:
171 if line.find('Description:') == -1:
172 return True
173 return False
176 def remove_fields(file, line):
177 # remove_fields removes Author/Date/Last edit/SYSTEM/Revision
178 # fields from a comment within the start of a definition module.
179 while (line.find('*)') == -1):
180 if not removeable_field(line):
181 line = line.rstrip().replace('{', '@{').replace('}', '@}')
182 output.write(line + '\n')
183 line = file.readline()
184 output.write(line.rstrip() + '\n')
187 def emit_index(entry, tag):
188 global state_obj
189 if args.texinfo:
190 if tag == '':
191 output.write('@findex ' + entry.rstrip() + '\n')
192 else:
193 output.write('@findex ' + entry.rstrip() + ' ' + tag + '\n')
194 elif args.sphinx:
195 if tag == '':
196 state_obj.to_index()
197 output.write(' ' * 3 + entry.rstrip() + '\n')
198 else:
199 state_obj.to_index()
200 output.write(' ' * 3 + 'pair: ' + entry.rstrip() + '; ' + tag + '\n')
203 def check_index(line):
204 # check_index - create an index entry for a PROCEDURE, TYPE, CONST or VAR.
205 global state_obj
207 words = line.split()
208 procedure = ''
209 if (len(words) > 1) and (words[0] == 'PROCEDURE'):
210 state_obj.set_state(state_none)
211 if (words[1] == '__BUILTIN__') and (len(words) > 2):
212 procedure = words[2]
213 else:
214 procedure = words[1]
215 if (len(line) > 1) and (line[0:2] == '(*'):
216 state_obj.set_state(state_none)
217 elif line == 'VAR':
218 state_obj.set_state(state_var)
219 return
220 elif line == 'TYPE':
221 state_obj.set_state(state_type)
222 return
223 elif line == 'CONST':
224 state_obj.set_state(state_const)
225 if state_obj.is_var():
226 words = line.split(',')
227 for word in words:
228 word = word.lstrip()
229 if word != '':
230 if word.find(':') == -1:
231 emit_index(word, '(var)')
232 elif len(word) > 0:
233 var = word.split(':')
234 if len(var) > 0:
235 emit_index(var[0], '(var)')
236 if state_obj.is_type():
237 words = line.lstrip()
238 if words.find('=') != -1:
239 word = words.split('=')
240 if (len(word[0]) > 0) and (word[0][0] != '_'):
241 emit_index(word[0].rstrip(), '(type)')
242 else:
243 word = words.split()
244 if (len(word) > 1) and (word[1] == ';'):
245 # hidden type
246 if (len(word[0]) > 0) and (word[0][0] != '_'):
247 emit_index(word[0].rstrip(), '(type)')
248 if state_obj.is_const():
249 words = line.split(';')
250 for word in words:
251 word = word.lstrip()
252 if word != '':
253 if word.find('=') != -1:
254 var = word.split('=')
255 if len(var) > 0:
256 emit_index(var[0], '(const)')
257 if procedure != '':
258 name = procedure.split('(')
259 if name[0] != '':
260 proc = name[0]
261 if proc[-1] == ';':
262 proc = proc[:-1]
263 if proc != '':
264 emit_index(proc, '')
266 def demangle_system_datatype(line, indent):
267 # The spaces in front align in the export qualified list.
268 indent += len ('EXPORT QUALIFIED ')
269 line = line.replace('@SYSTEM_DATATYPES@',
270 '\n' + indent * ' ' + 'Target specific data types.')
271 line = line.replace('@SYSTEM_TYPES@',
272 '(* Target specific data types. *)')
273 return line
276 def emit_texinfo_content(f, line):
277 global state_obj
278 output.write(line.rstrip() + '\n')
279 line = f.readline()
280 if len(line.rstrip()) == 0:
281 output.write('\n')
282 line = f.readline()
283 if (line.find('(*') != -1):
284 remove_fields(f, line)
285 else:
286 output.write(line.rstrip() + '\n')
287 else:
288 output.write(line.rstrip() + '\n')
289 line = f.readline()
290 while line:
291 line = line.rstrip()
292 check_index(line)
293 line = line.replace('{', '@{').replace('}', '@}')
294 line = demangle_system_datatype(line, 0)
295 output.write(line + '\n')
296 line = f.readline()
297 return f
300 def emit_sphinx_content(f, line):
301 global state_obj
302 state_obj.to_code()
303 indentation = 4
304 indent = ' ' * indentation
305 output.write(indent + line.rstrip() + '\n')
306 line = f.readline()
307 if len(line.rstrip()) == 0:
308 output.write('\n')
309 line = f.readline()
310 if (line.find('(*') != -1):
311 remove_fields(f, line)
312 else:
313 output.write(indent + line.rstrip() + '\n')
314 else:
315 output.write(indent + line.rstrip() + '\n')
316 line = f.readline()
317 while line:
318 line = line.rstrip()
319 check_index(line)
320 state_obj.to_code()
321 line = demangle_system_datatype(line, indentation)
322 output.write(indent + line + '\n')
323 line = f.readline()
324 return f
327 def emit_example_content(f, line):
328 if args.texinfo:
329 return emit_texinfo_content(f, line)
330 elif args.sphinx:
331 return emit_sphinx_content(f, line)
334 def emit_example_begin():
335 if args.texinfo:
336 output.write('@example\n')
339 def emit_example_end():
340 if args.texinfo:
341 output.write('@end example\n')
344 def emit_page(need_page):
345 if need_page and args.texinfo:
346 output.write('@page\n')
349 def parse_definition(dir_, source, build, file, need_page):
350 # parse_definition reads a definition module and creates
351 # indices for procedures, constants, variables and types.
352 output.write('\n')
353 with open(find_file(dir_, build, source, file), 'r') as f:
354 init_state()
355 line = f.readline()
356 while (line.find('(*') != -1):
357 remote_initial_comments(f, line)
358 line = f.readline()
359 while (line.find('DEFINITION') == -1):
360 line = f.readline()
361 emit_example_begin()
362 f = emit_example_content(f, line)
363 emit_example_end()
364 emit_page(need_page)
367 def parse_modules(up, dir_, build, source, list_of_modules):
368 previous = ''
369 i = 0
370 if len(list_of_modules) > 1:
371 nxt = dir_ + '/' + list_of_modules[1][:-4]
372 else:
373 nxt = ''
374 while i < len(list_of_modules):
375 emit_node(dir_ + '/' + list_of_modules[i][:-4], nxt, previous, up)
376 emit_sub_section(dir_ + '/' + list_of_modules[i][:-4])
377 parse_definition(dir_, source, build, list_of_modules[i], True)
378 output.write('\n')
379 previous = dir_ + '/' + list_of_modules[i][:-4]
380 i = i + 1
381 if i+1 < len(list_of_modules):
382 nxt = dir_ + '/' + list_of_modules[i+1][:-4]
383 else:
384 nxt = ''
387 def do_cat(name):
388 # do_cat displays the contents of file, name, to stdout
389 with open(name, 'r') as file:
390 line = file.readline()
391 while line:
392 output.write(line.rstrip() + '\n')
393 line = file.readline()
396 def module_menu(dir_, build, source):
397 # module_menu generates a simple menu for all definition modules
398 # in dir
399 output.write('@menu\n')
400 list_of_files = []
401 if os.path.exists(os.path.join(source, dir_)):
402 list_of_files += os.listdir(os.path.join(source, dir_))
403 if os.path.exists(os.path.join(source, dir_)):
404 list_of_files += os.listdir(os.path.join(build, dir_))
405 list_of_files = list(dict.fromkeys(list_of_files).keys())
406 list_of_files.sort()
407 for file in list_of_files:
408 if found_file(dir_, build, source, file):
409 if (len(file) > 4) and (file[-4:] == '.def'):
410 output.write('* ' + dir_ + '/' + file[:-4] + '::' + file + '\n')
411 output.write('@end menu\n')
412 output.write('\n')
415 def check_directory(dir_, build, source):
416 # check_directory - returns True if dir exists in either build or source.
417 if os.path.isdir(build) and os.path.exists(os.path.join(build, dir_)):
418 return True
419 elif os.path.isdir(source) and os.path.exists(os.path.join(source, dir_)):
420 return True
421 else:
422 return False
425 def found_file(dir_, build, source, file):
426 # found_file return True if file is found in build/dir/file or
427 # source/dir/file.
428 name = os.path.join(os.path.join(build, dir_), file)
429 if os.path.exists(name):
430 return True
431 name = os.path.join(os.path.join(source, dir_), file)
432 if os.path.exists(name):
433 return True
434 return False
437 def find_file(dir_, build, source, file):
438 # find_file return the path to file searching in build/dir/file
439 # first then source/dir/file.
440 name1 = os.path.join(os.path.join(build, dir_), file)
441 if os.path.exists(name1):
442 return name1
443 name2 = os.path.join(os.path.join(source, dir_), file)
444 if os.path.exists(name2):
445 return name2
446 sys.stderr.write('file cannot be found in either ' + name1)
447 sys.stderr.write(' or ' + name2 + '\n')
448 os.sys.exit(1)
451 def display_modules(up, dir_, build, source):
452 # display_modules walks though the files in dir and parses
453 # definition modules and includes README.texi
454 if check_directory(dir_, build, source):
455 if args.texinfo:
456 ext = '.texi'
457 elif args.sphinx:
458 ext = '.rst'
459 else:
460 ext = ''
461 if found_file(dir_, build, source, 'README' + ext):
462 do_cat(find_file(dir_, build, source, 'README' + ext))
463 module_menu(dir_, build, source)
464 list_of_files = []
465 if os.path.exists(os.path.join(source, dir_)):
466 list_of_files += os.listdir(os.path.join(source, dir_))
467 if os.path.exists(os.path.join(source, dir_)):
468 list_of_files += os.listdir(os.path.join(build, dir_))
469 list_of_files = list(dict.fromkeys(list_of_files).keys())
470 list_of_files.sort()
471 list_of_modules = []
472 for file in list_of_files:
473 if found_file(dir_, build, source, file):
474 if (len(file) > 4) and (file[-4:] == '.def'):
475 list_of_modules += [file]
476 list_of_modules.sort()
477 parse_modules(up, dir_, build, source, list_of_modules)
478 else:
479 line = 'directory ' + dir_ + ' not found in either '
480 line += build + ' or ' + source
481 sys.stderr.write(line + '\n')
484 def display_copyright():
485 output.write('@c Copyright (C) 2000-2024 Free Software Foundation, Inc.\n')
486 output.write('@c This file is part of GNU Modula-2.\n')
487 output.write("""
488 @c Permission is granted to copy, distribute and/or modify this document
489 @c under the terms of the GNU Free Documentation License, Version 1.2 or
490 @c any later version published by the Free Software Foundation.
491 """)
494 def collect_args():
495 parser = argparse.ArgumentParser()
496 parser.add_argument('-v', '--verbose', help='generate progress messages',
497 action='store_true')
498 parser.add_argument('-b', '--builddir', help='set the build directory',
499 default='.', action='store')
500 parser.add_argument('-f', '--inputfile', help='set the input file',
501 default=None, action='store')
502 parser.add_argument('-o', '--outputfile', help='set the output file',
503 default=None, action='store')
504 parser.add_argument('-s', '--sourcedir', help='set the source directory',
505 default='.', action='store')
506 parser.add_argument('-t', '--texinfo',
507 help='generate texinfo documentation',
508 default=False, action='store_true')
509 parser.add_argument('-u', '--up', help='set the up node',
510 default='', action='store')
511 parser.add_argument('-x', '--sphinx', help='generate sphinx documentation',
512 default=False, action='store_true')
513 args = parser.parse_args()
514 return args
517 def handle_file():
518 if args.inputfile is None:
519 display_copyright()
520 display_menu()
521 display_library_class()
522 else:
523 parse_definition('.', args.sourcedir, args.builddir,
524 args.inputfile, False)
527 def main():
528 global args, output
529 args = collect_args()
530 if args.outputfile is None:
531 output = sys.stdout
532 handle_file()
533 else:
534 with open(args.outputfile, 'w') as output:
535 handle_file()
538 main()