aboutsummaryrefslogtreecommitdiff
path: root/gcc/m2/tools-src/def2doc.py
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/m2/tools-src/def2doc.py')
-rw-r--r--gcc/m2/tools-src/def2doc.py539
1 files changed, 539 insertions, 0 deletions
diff --git a/gcc/m2/tools-src/def2doc.py b/gcc/m2/tools-src/def2doc.py
new file mode 100644
index 0000000..d1637ee
--- /dev/null
+++ b/gcc/m2/tools-src/def2doc.py
@@ -0,0 +1,539 @@
+#!/usr/bin/env python3
+
+# def2doc.py creates texi library documentation for all exported procedures.
+# Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
+
+# Copyright (C) 2000-2022 Free Software Foundation, Inc.
+# This file is part of GNU Modula-2.
+#
+# GNU Modula-2 is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Modula-2 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Modula-2; see the file COPYING. If not, write to the
+# Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+import argparse
+import os
+import sys
+
+Base_Libs = ['gm2-libs', 'Base libraries', 'Basic M2F compatible libraries']
+
+PIM_Log_Desc = 'PIM and Logitech 3.0 compatible libraries'
+PIM_Log = ['gm2-libs-pim', 'PIM and Logitech 3.0 Compatible', PIM_Log_Desc]
+PIM_Cor_Desc = 'PIM compatible process support'
+PIM_Cor = ['gm2-libs-coroutines', 'PIM coroutine support', PIM_Cor_Desc]
+ISO_Libs = ['gm2-libs-iso', 'M2 ISO Libraries', 'ISO defined libraries']
+
+library_classifications = [Base_Libs, PIM_Log, PIM_Cor, ISO_Libs]
+
+# state_states
+state_none, state_var, state_type, state_const = range(4)
+# block states
+block_none, block_code, block_text, block_index = range(4)
+
+
+class state:
+ def __init__(self):
+ self._state_state = state_none
+ self._block = block_none
+
+ def get_state(self):
+ return self._state_state
+
+ def set_state(self, value):
+ self._state_state = value
+
+ def is_const(self):
+ return self._state_state == state_const
+
+ def is_type(self):
+ return self._state_state == state_type
+
+ def is_var(self):
+ return self._state_state == state_var
+
+ def get_block(self):
+ return self._block
+
+ def _change_block(self, new_block):
+ if self._block != new_block:
+ self._block = new_block
+ self._emit_block_desc()
+
+ def _emit_block_desc(self):
+ if self._block == block_code:
+ output.write('.. code-block:: modula2\n')
+ elif self._block == block_index:
+ output.write('.. index::\n')
+
+ def to_code(self):
+ self._change_block(block_code)
+
+ def to_index(self):
+ self._change_block(block_index)
+
+
+def init_state():
+ global state_obj
+ state_obj = state()
+
+
+def emit_node(name, nxt, previous, up):
+ if args.texinfo:
+ output.write('@node ' + name + ', ' + nxt + ', ')
+ output.write(previous + ', ' + up + '\n')
+ elif args.sphinx:
+ output.write('@c @node ' + name + ', ' + nxt + ', ')
+ output.write(previous + ', ' + up + '\n')
+
+
+def emit_section(name):
+ if args.texinfo:
+ output.write('@section ' + name + '\n')
+ elif args.sphinx:
+ output.write(name + '\n')
+ output.write('=' * len(name) + '\n')
+
+
+def emit_sub_section(name):
+ if args.texinfo:
+ output.write('@subsection ' + name + '\n')
+ elif args.sphinx:
+ output.write(name + '\n')
+ output.write('-' * len(name) + '\n')
+
+
+def display_library_class():
+ # display_library_class displays a node for a library directory and invokes
+ # a routine to summarize each module.
+ global args
+ previous = ''
+ nxt = library_classifications[1][1]
+ i = 0
+ lib = library_classifications[i]
+ while True:
+ emit_node(lib[1], nxt, previous, args.up)
+ emit_section(lib[1])
+ output.write('\n')
+ display_modules(lib[1], lib[0], args.builddir, args.sourcedir)
+ output.write('\n')
+ output.write('@c ' + '-' * 60 + '\n')
+ previous = lib[1]
+ i += 1
+ if i == len(library_classifications):
+ break
+ lib = library_classifications[i]
+ if i+1 == len(library_classifications):
+ nxt = ''
+ else:
+ nxt = library_classifications[i+1][1]
+
+
+def display_menu():
+ # display_menu displays the top level menu for library documentation.
+ output.write('@menu\n')
+ for lib in library_classifications:
+ output.write('* ' + lib[1] + '::' + lib[2] + '\n')
+ output.write('@end menu\n')
+ output.write('\n')
+ output.write('@c ' + '=' * 60 + '\n')
+ output.write('\n')
+
+
+def remote_initial_comments(file, line):
+ # remote_initial_comments removes any (* *) at the top
+ # of the definition module.
+ while (line.find('*)') == -1):
+ line = file.readline()
+
+
+def removeable_field(line):
+ # removeable_field - returns True if a comment field should be removed
+ # from the definition module.
+ field_list = ['Author', 'Last edit', 'LastEdit', 'Last update',
+ 'Date', 'Title', 'Revision']
+ for field in field_list:
+ if (line.find(field) != -1) and (line.find(':') != -1):
+ return True
+ ignore_list = ['System', 'SYSTEM']
+ for ignore_field in ignore_list:
+ if line.find(ignore_field) != -1:
+ if line.find(':') != -1:
+ if line.find('Description:') == -1:
+ return True
+ return False
+
+
+def remove_fields(file, line):
+ # remove_fields removes Author/Date/Last edit/SYSTEM/Revision
+ # fields from a comment within the start of a definition module.
+ while (line.find('*)') == -1):
+ if not removeable_field(line):
+ line = line.rstrip().replace('{', '@{').replace('}', '@}')
+ output.write(line + '\n')
+ line = file.readline()
+ output.write(line.rstrip() + '\n')
+
+
+def emit_index(entry, tag):
+ global state_obj
+ if args.texinfo:
+ if tag == '':
+ output.write('@findex ' + entry.rstrip() + '\n')
+ else:
+ output.write('@findex ' + entry.rstrip() + ' ' + tag + '\n')
+ elif args.sphinx:
+ if tag == '':
+ state_obj.to_index()
+ output.write(' ' * 3 + entry.rstrip() + '\n')
+ else:
+ state_obj.to_index()
+ output.write(' ' * 3 + 'pair: ' + entry.rstrip() + '; ' + tag + '\n')
+
+
+def check_index(line):
+ # check_index - create an index entry for a PROCEDURE, TYPE, CONST or VAR.
+ global state_obj
+
+ words = line.split()
+ procedure = ''
+ if (len(words) > 1) and (words[0] == 'PROCEDURE'):
+ state_obj.set_state(state_none)
+ if (words[1] == '__BUILTIN__') and (len(words) > 2):
+ procedure = words[2]
+ else:
+ procedure = words[1]
+ if (len(line) > 1) and (line[0:2] == '(*'):
+ state_obj.set_state(state_none)
+ elif line == 'VAR':
+ state_obj.set_state(state_var)
+ return
+ elif line == 'TYPE':
+ state_obj.set_state(state_type)
+ return
+ elif line == 'CONST':
+ state_obj.set_state(state_const)
+ if state_obj.is_var():
+ words = line.split(',')
+ for word in words:
+ word = word.lstrip()
+ if word != '':
+ if word.find(':') == -1:
+ emit_index(word, '(var)')
+ elif len(word) > 0:
+ var = word.split(':')
+ if len(var) > 0:
+ emit_index(var[0], '(var)')
+ if state_obj.is_type():
+ words = line.lstrip()
+ if words.find('=') != -1:
+ word = words.split('=')
+ if (len(word[0]) > 0) and (word[0][0] != '_'):
+ emit_index(word[0].rstrip(), '(type)')
+ else:
+ word = words.split()
+ if (len(word) > 1) and (word[1] == ';'):
+ # hidden type
+ if (len(word[0]) > 0) and (word[0][0] != '_'):
+ emit_index(word[0].rstrip(), '(type)')
+ if state_obj.is_const():
+ words = line.split(';')
+ for word in words:
+ word = word.lstrip()
+ if word != '':
+ if word.find('=') != -1:
+ var = word.split('=')
+ if len(var) > 0:
+ emit_index(var[0], '(const)')
+ if procedure != '':
+ name = procedure.split('(')
+ if name[0] != '':
+ proc = name[0]
+ if proc[-1] == ';':
+ proc = proc[:-1]
+ if proc != '':
+ emit_index(proc, '')
+
+def demangle_system_datatype(line, indent):
+ # The spaces in front align in the export qualified list.
+ indent += len ('EXPORT QUALIFIED ')
+ line = line.replace('@SYSTEM_DATATYPES@',
+ '\n' + indent * ' ' + 'Target specific data types.')
+ line = line.replace('@SYSTEM_TYPES@',
+ '(* Target specific data types. *)')
+ return line
+
+
+def emit_texinfo_content(f, line):
+ global state_obj
+ output.write(line.rstrip() + '\n')
+ line = f.readline()
+ if len(line.rstrip()) == 0:
+ output.write('\n')
+ line = f.readline()
+ if (line.find('(*') != -1):
+ remove_fields(f, line)
+ else:
+ output.write(line.rstrip() + '\n')
+ else:
+ output.write(line.rstrip() + '\n')
+ line = f.readline()
+ while line:
+ line = line.rstrip()
+ check_index(line)
+ line = line.replace('{', '@{').replace('}', '@}')
+ line = demangle_system_datatype(line, 0)
+ output.write(line + '\n')
+ line = f.readline()
+ return f
+
+
+def emit_sphinx_content(f, line):
+ global state_obj
+ state_obj.to_code()
+ indentation = 4
+ indent = ' ' * indentation
+ output.write(indent + line.rstrip() + '\n')
+ line = f.readline()
+ if len(line.rstrip()) == 0:
+ output.write('\n')
+ line = f.readline()
+ if (line.find('(*') != -1):
+ remove_fields(f, line)
+ else:
+ output.write(indent + line.rstrip() + '\n')
+ else:
+ output.write(indent + line.rstrip() + '\n')
+ line = f.readline()
+ while line:
+ line = line.rstrip()
+ check_index(line)
+ state_obj.to_code()
+ line = demangle_system_datatype(line, indentation)
+ output.write(indent + line + '\n')
+ line = f.readline()
+ return f
+
+
+def emit_example_content(f, line):
+ if args.texinfo:
+ return emit_texinfo_content(f, line)
+ elif args.sphinx:
+ return emit_sphinx_content(f, line)
+
+
+def emit_example_begin():
+ if args.texinfo:
+ output.write('@example\n')
+
+
+def emit_example_end():
+ if args.texinfo:
+ output.write('@end example\n')
+
+
+def emit_page(need_page):
+ if need_page and args.texinfo:
+ output.write('@page\n')
+
+
+def parse_definition(dir_, source, build, file, need_page):
+ # parse_definition reads a definition module and creates
+ # indices for procedures, constants, variables and types.
+ output.write('\n')
+ with open(find_file(dir_, build, source, file), 'r') as f:
+ init_state()
+ line = f.readline()
+ while (line.find('(*') != -1):
+ remote_initial_comments(f, line)
+ line = f.readline()
+ while (line.find('DEFINITION') == -1):
+ line = f.readline()
+ emit_example_begin()
+ f = emit_example_content(f, line)
+ emit_example_end()
+ emit_page(need_page)
+
+
+def parse_modules(up, dir_, build, source, list_of_modules):
+ previous = ''
+ i = 0
+ if len(list_of_modules) > 1:
+ nxt = dir_ + '/' + list_of_modules[1][:-4]
+ else:
+ nxt = ''
+ while i < len(list_of_modules):
+ emit_node(dir_ + '/' + list_of_modules[i][:-4], nxt, previous, up)
+ emit_sub_section(dir_ + '/' + list_of_modules[i][:-4])
+ parse_definition(dir_, source, build, list_of_modules[i], True)
+ output.write('\n')
+ previous = dir_ + '/' + list_of_modules[i][:-4]
+ i = i + 1
+ if i+1 < len(list_of_modules):
+ nxt = dir_ + '/' + list_of_modules[i+1][:-4]
+ else:
+ nxt = ''
+
+
+def do_cat(name):
+ # do_cat displays the contents of file, name, to stdout
+ with open(name, 'r') as file:
+ line = file.readline()
+ while line:
+ output.write(line.rstrip() + '\n')
+ line = file.readline()
+
+
+def module_menu(dir_, build, source):
+ # module_menu generates a simple menu for all definition modules
+ # in dir
+ output.write('@menu\n')
+ list_of_files = []
+ if os.path.exists(os.path.join(source, dir_)):
+ list_of_files += os.listdir(os.path.join(source, dir_))
+ if os.path.exists(os.path.join(source, dir_)):
+ list_of_files += os.listdir(os.path.join(build, dir_))
+ list_of_files = list(dict.fromkeys(list_of_files).keys())
+ list_of_files.sort()
+ for file in list_of_files:
+ if found_file(dir_, build, source, file):
+ if (len(file) > 4) and (file[-4:] == '.def'):
+ output.write('* ' + dir_ + '/' + file[:-4] + '::' + file + '\n')
+ output.write('@end menu\n')
+ output.write('\n')
+
+
+def check_directory(dir_, build, source):
+ # check_directory - returns True if dir exists in either build or source.
+ if os.path.isdir(build) and os.path.exists(os.path.join(build, dir_)):
+ return True
+ elif os.path.isdir(source) and os.path.exists(os.path.join(source, dir_)):
+ return True
+ else:
+ return False
+
+
+def found_file(dir_, build, source, file):
+ # found_file return True if file is found in build/dir/file or
+ # source/dir/file.
+ name = os.path.join(os.path.join(build, dir_), file)
+ if os.path.exists(name):
+ return True
+ name = os.path.join(os.path.join(source, dir_), file)
+ if os.path.exists(name):
+ return True
+ return False
+
+
+def find_file(dir_, build, source, file):
+ # find_file return the path to file searching in build/dir/file
+ # first then source/dir/file.
+ name1 = os.path.join(os.path.join(build, dir_), file)
+ if os.path.exists(name1):
+ return name1
+ name2 = os.path.join(os.path.join(source, dir_), file)
+ if os.path.exists(name2):
+ return name2
+ sys.stderr.write('file cannot be found in either ' + name1)
+ sys.stderr.write(' or ' + name2 + '\n')
+ os.sys.exit(1)
+
+
+def display_modules(up, dir_, build, source):
+ # display_modules walks though the files in dir and parses
+ # definition modules and includes README.texi
+ if check_directory(dir_, build, source):
+ if args.texinfo:
+ ext = '.texi'
+ elif args.sphinx:
+ ext = '.rst'
+ else:
+ ext = ''
+ if found_file(dir_, build, source, 'README' + ext):
+ do_cat(find_file(dir_, build, source, 'README' + ext))
+ module_menu(dir_, build, source)
+ list_of_files = []
+ if os.path.exists(os.path.join(source, dir_)):
+ list_of_files += os.listdir(os.path.join(source, dir_))
+ if os.path.exists(os.path.join(source, dir_)):
+ list_of_files += os.listdir(os.path.join(build, dir_))
+ list_of_files = list(dict.fromkeys(list_of_files).keys())
+ list_of_files.sort()
+ list_of_modules = []
+ for file in list_of_files:
+ if found_file(dir_, build, source, file):
+ if (len(file) > 4) and (file[-4:] == '.def'):
+ list_of_modules += [file]
+ list_of_modules.sort()
+ parse_modules(up, dir_, build, source, list_of_modules)
+ else:
+ line = 'directory ' + dir_ + ' not found in either '
+ line += build + ' or ' + source
+ sys.stderr.write(line + '\n')
+
+
+def display_copyright():
+ output.write('@c Copyright (C) 2000-2022 Free Software Foundation, Inc.\n')
+ output.write('@c This file is part of GNU Modula-2.\n')
+ output.write("""
+@c Permission is granted to copy, distribute and/or modify this document
+@c under the terms of the GNU Free Documentation License, Version 1.2 or
+@c any later version published by the Free Software Foundation.
+""")
+
+
+def collect_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-v', '--verbose', help='generate progress messages',
+ action='store_true')
+ parser.add_argument('-b', '--builddir', help='set the build directory',
+ default='.', action='store')
+ parser.add_argument('-f', '--inputfile', help='set the input file',
+ default=None, action='store')
+ parser.add_argument('-o', '--outputfile', help='set the output file',
+ default=None, action='store')
+ parser.add_argument('-s', '--sourcedir', help='set the source directory',
+ default='.', action='store')
+ parser.add_argument('-t', '--texinfo',
+ help='generate texinfo documentation',
+ default=False, action='store_true')
+ parser.add_argument('-u', '--up', help='set the up node',
+ default='', action='store')
+ parser.add_argument('-x', '--sphinx', help='generate sphinx documentation',
+ default=False, action='store_true')
+ args = parser.parse_args()
+ return args
+
+
+def handle_file():
+ if args.inputfile is None:
+ display_copyright()
+ display_menu()
+ display_library_class()
+ else:
+ parse_definition('.', args.sourcedir, args.builddir,
+ args.inputfile, False)
+
+
+def main():
+ global args, output
+ args = collect_args()
+ if args.outputfile is None:
+ output = sys.stdout
+ handle_file()
+ else:
+ with open(args.outputfile, 'w') as output:
+ handle_file()
+
+
+main()