diff options
Diffstat (limited to 'gcc/m2/tools-src/def2doc.py')
-rw-r--r-- | gcc/m2/tools-src/def2doc.py | 539 |
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() |