]>
git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/m2/tools-src/def2doc.py
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)
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 GNU Modula-2; see the file COPYING. If not, write to the
21 # Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
29 Base_Libs
= ['gm2-libs', 'Base libraries', 'Basic M2F compatible libraries']
31 PIM_Log_Desc
= 'PIM and Logitech 3.0 compatible libraries'
32 PIM_Log
= ['gm2-libs-log', 'PIM and Logitech 3.0 Compatible', PIM_Log_Desc
]
33 PIM_Cor_Desc
= 'PIM compatible process support'
34 PIM_Cor
= ['gm2-libs-coroutines', 'PIM coroutine support', PIM_Cor_Desc
]
35 ISO_Libs
= ['gm2-libs-iso', 'M2 ISO Libraries', 'ISO defined libraries']
37 library_classifications
= [Base_Libs
, PIM_Log
, PIM_Cor
, ISO_Libs
]
40 state_none
, state_var
, state_type
, state_const
= range(4)
42 block_none
, block_code
, block_text
, block_index
= range(4)
47 self
._state
_state
= state_none
48 self
._block
= block_none
51 return self
._state
_state
53 def set_state(self
, value
):
54 self
._state
_state
= value
57 return self
._state
_state
== state_const
60 return self
._state
_state
== state_type
63 return self
._state
_state
== state_var
68 def _change_block(self
, new_block
):
69 if self
._block
!= new_block
:
70 self
._block
= new_block
71 self
._emit
_block
_desc
()
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')
80 self
._change
_block
(block_code
)
83 self
._change
_block
(block_index
)
91 def emit_node(name
, nxt
, previous
, up
):
93 output
.write('@node ' + name
+ ', ' + nxt
+ ', ')
94 output
.write(previous
+ ', ' + up
+ '\n')
96 output
.write('@c @node ' + name
+ ', ' + nxt
+ ', ')
97 output
.write(previous
+ ', ' + up
+ '\n')
100 def emit_section(name
):
102 output
.write('@section ' + name
+ '\n')
104 output
.write(name
+ '\n')
105 output
.write('=' * len(name
) + '\n')
108 def emit_sub_section(name
):
110 output
.write('@subsection ' + name
+ '\n')
112 output
.write(name
+ '\n')
113 output
.write('-' * len(name
) + '\n')
116 def display_library_class():
117 # display_library_class displays a node for a library directory and invokes
118 # a routine to summarize each module.
121 nxt
= library_classifications
[1][1]
123 lib
= library_classifications
[i
]
125 emit_node(lib
[1], nxt
, previous
, args
.up
)
128 display_modules(lib
[1], lib
[0], args
.builddir
, args
.sourcedir
)
130 output
.write('@c ' + '-' * 60 + '\n')
133 if i
== len(library_classifications
):
135 lib
= library_classifications
[i
]
136 if i
+1 == len(library_classifications
):
139 nxt
= library_classifications
[i
+1][1]
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')
149 output
.write('@c ' + '=' * 60 + '\n')
153 def 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()
160 def 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):
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:
177 def 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')
188 def emit_index(entry
, tag
):
192 output
.write('@findex ' + entry
.rstrip() + '\n')
194 output
.write('@findex ' + entry
.rstrip() + ' ' + tag
+ '\n')
198 output
.write(' ' * 3 + entry
.rstrip() + '\n')
201 output
.write(' ' * 3 + 'pair: ' + entry
.rstrip() + '; ' + tag
+ '\n')
204 def check_index(line
):
205 # check_index - create an index entry for a PROCEDURE, TYPE, CONST or VAR.
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):
216 if (len(line
) > 1) and (line
[0:2] == '(*'):
217 state_obj
.set_state(state_none
)
219 state_obj
.set_state(state_var
)
222 state_obj
.set_state(state_type
)
224 elif line
== 'CONST':
225 state_obj
.set_state(state_const
)
226 if state_obj
.is_var():
227 words
= line
.split(',')
231 if word
.find(':') == -1:
232 emit_index(word
, '(var)')
234 var
= word
.split(':')
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)')
245 if (len(word
) > 1) and (word
[1] == ';'):
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(';')
254 if word
.find('=') != -1:
255 var
= word
.split('=')
257 emit_index(var
[0], '(const)')
259 name
= procedure
.split('(')
267 def 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. *)')
277 def emit_texinfo_content(f
, line
):
279 output
.write(line
.rstrip() + '\n')
281 if len(line
.rstrip()) == 0:
284 if (line
.find('(*') != -1):
285 remove_fields(f
, line
)
287 output
.write(line
.rstrip() + '\n')
289 output
.write(line
.rstrip() + '\n')
294 line
= line
.replace('{', '@{').replace('}', '@}')
295 line
= demangle_system_datatype(line
, 0)
296 output
.write(line
+ '\n')
301 def emit_sphinx_content(f
, line
):
305 indent
= ' ' * indentation
306 output
.write(indent
+ line
.rstrip() + '\n')
308 if len(line
.rstrip()) == 0:
311 if (line
.find('(*') != -1):
312 remove_fields(f
, line
)
314 output
.write(indent
+ line
.rstrip() + '\n')
316 output
.write(indent
+ line
.rstrip() + '\n')
322 line
= demangle_system_datatype(line
, indentation
)
323 output
.write(indent
+ line
+ '\n')
328 def emit_example_content(f
, line
):
330 return emit_texinfo_content(f
, line
)
332 return emit_sphinx_content(f
, line
)
335 def emit_example_begin():
337 output
.write('@example\n')
340 def emit_example_end():
342 output
.write('@end example\n')
345 def emit_page(need_page
):
346 if need_page
and args
.texinfo
:
347 output
.write('@page\n')
350 def 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.
354 with
open(find_file(dir_
, build
, source
, file), 'r') as f
:
357 while (line
.find('(*') != -1):
358 remote_initial_comments(f
, line
)
360 while (line
.find('DEFINITION') == -1):
363 f
= emit_example_content(f
, line
)
368 def parse_modules(up
, dir_
, build
, source
, list_of_modules
):
371 if len(list_of_modules
) > 1:
372 nxt
= dir_
+ '/' + list_of_modules
[1][:-4]
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)
380 previous
= dir_
+ '/' + list_of_modules
[i
][:-4]
382 if i
+1 < len(list_of_modules
):
383 nxt
= dir_
+ '/' + list_of_modules
[i
+1][:-4]
389 # do_cat displays the contents of file, name, to stdout
390 with
open(name
, 'r') as file:
391 line
= file.readline()
393 output
.write(line
.rstrip() + '\n')
394 line
= file.readline()
397 def module_menu(dir_
, build
, source
):
398 # module_menu generates a simple menu for all definition modules
400 output
.write('@menu\n')
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())
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')
416 def 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_
)):
420 elif os
.path
.isdir(source
) and os
.path
.exists(os
.path
.join(source
, dir_
)):
426 def found_file(dir_
, build
, source
, file):
427 # found_file return True if file is found in build/dir/file or
429 name
= os
.path
.join(os
.path
.join(build
, dir_
), file)
430 if os
.path
.exists(name
):
432 name
= os
.path
.join(os
.path
.join(source
, dir_
), file)
433 if os
.path
.exists(name
):
438 def 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
):
444 name2
= os
.path
.join(os
.path
.join(source
, dir_
), file)
445 if os
.path
.exists(name2
):
447 sys
.stderr
.write('file cannot be found in either ' + name1
)
448 sys
.stderr
.write(' or ' + name2
+ '\n')
452 def 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
):
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
)
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())
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
)
480 line
= 'directory ' + dir_
+ ' not found in either '
481 line
+= build
+ ' or ' + source
482 sys
.stderr
.write(line
+ '\n')
485 def display_copyright():
486 output
.write('@c Copyright (C) 2000-2024 Free Software Foundation, Inc.\n')
487 output
.write('@c This file is part of GNU Modula-2.\n')
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.
496 parser
= argparse
.ArgumentParser()
497 parser
.add_argument('-v', '--verbose', help='generate progress messages',
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()
519 if args
.inputfile
is None:
522 display_library_class()
524 parse_definition('.', args
.sourcedir
, args
.builddir
,
525 args
.inputfile
, False)
530 args
= collect_args()
531 if args
.outputfile
is None:
535 with
open(args
.outputfile
, 'w') as output
: