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