file CBGB/cbgb.py

[No description available]

Namespaces

Name
cbgb

Source code

#!/usr/bin/env python
#
#   =========================================================
#   |                                                       |
#   |   CBGB - Common Block harvester for GAMBIT Backends   |
#   |                                                       |
#   =========================================================
#
#   Author: Anders Kvellestad (anders.kvellestad@nordita.org)
#

import sys
import os
from collections import OrderedDict, Counter
import importlib


print
print
print '  ~~~~  CBGB  -  Common Block harvester for GAMBIT Backends  ~~~~'
print
print


# Check that arguments list is not empty
if (len(sys.argv) < 2):

    print 'Missing input arguments.'
    print 'Usage:'
    print
    print '  python cbgb.py configs/<config_file>'
    print

    sys.exit()



#
# Get the config file name from command line and import it as the 'cfg' module.
#

# Get the config file name from command line input
input_cfg_path = sys.argv[1]

# Import the given config file as a module named 'cfg'.
input_cfg_dir, input_cfg_filename = os.path.split(input_cfg_path)
input_cfg_modulename, ext = os.path.splitext(input_cfg_filename)

import modules.active_cfg
modules.active_cfg.module_name = input_cfg_modulename

try:
    cfg = importlib.import_module("configs." + modules.active_cfg.module_name)
except ImportError as e:
    print "Failed to import config module '%s'. Are you sure the file %s exists?" % (input_cfg_modulename, os.path.join('configs', input_cfg_modulename + '.py'))
    print
    sys.exit()

import modules.gb as gb
import modules.utils as utils




#
# Check and output some info from the config file.
#

print '  Input Fortran files:'
print '  --------------------'
print
for i, src_file_name in enumerate(cfg.input_files,1):
    print '    ' + src_file_name
print
print

print '  Requested functions/subroutines:'
print '  --------------------------------'
print

f_listing = '    '
for i, f_name in enumerate(cfg.load_functions,1):
    f_listing += f_name + ', '

    if i%4==0:
        f_listing += '\n    '
print f_listing.rstrip().rstrip(',')
print

# Check for duplicates in cfg.load_functions
f_duplicates = [item for item,count in Counter(cfg.load_functions).items() if count > 1]
if len(f_duplicates) > 0:
    print "    ERROR: The following functions/subroutines appear multiple times in the list load_functions:"
    print
    for fn in f_duplicates:
        print "    - '%s'" % (fn)
    print
    print "    To list identically named functions from different modules, specify both the module name"
    print "    and the function name, separated by '::', e.g., 'some_module::some_function'."
    print

    sys.exit()
print


print '  Requested common blocks:'
print '  ------------------------'
print

cb_listing = '    '
for i, cb_name in enumerate(cfg.load_common_blocks,1):
    cb_listing += cb_name + ', '

    if i%4==0:
        cb_listing += '\n    '
print cb_listing.rstrip().rstrip(',')
print

# Check for duplicates in cfg.load_common_blocks
cb_duplicates = [item for item,count in Counter(cfg.load_common_blocks).items() if count > 1]
if len(cb_duplicates) > 0:
    print "    ERROR: The following common blocks appear multiple times in the list load_common_blocks:"
    print
    for cb in cb_duplicates:
        print "    - '%s'" % (cb)
    print

    sys.exit()
print




#
# Read input source files
#

src_content = ''
for fn in cfg.input_files:
    with open(fn,'r') as src_file:
        src_content += src_file.read()



#
# Do some reformatting of the source code:
#
# - Convert all code to lower-case. (Fortran is not case-sensitive.)
# - Replace tabs with spaces.
# - Split source code into a list of lines.
# - Remove all comments and blank lines.
# - Remove all statement labels.
# - Combine continued source lines into single lines.
# - Remove leading and trailing blanks,
#

# # Convert all source code to lower-case. (Fortran is not case-sensitive.)
# src_content = src_content.lower()

# Convert tabs to spaces.
src_content = src_content.replace('\t', ' '*cfg.tabs_to_n_spaces)

# Split source code into a list of code lines.
src_lines = src_content.splitlines()

# Remove comments.
src_lines = utils.removeComments(src_lines)

# Remove statement labels.
src_lines = utils.removeStatementLabels(src_lines)

# Remove blank lines.
src_lines = utils.removeBlankLines(src_lines)

# Join continued lines.
src_lines = utils.joinContinuedLines(src_lines)

# Remove leading and trailing blanks
src_lines = utils.removeLeadingTrailingBlanks(src_lines)

# Replace multiple spaces with single spaces.
src_lines = utils.allSingleSpace(src_lines)

# Remove keywords we don't care about.
src_lines = utils.removeKeywords(src_lines)



# Identify the various parts of the code: program, functions and subroutines.
# Return a dict with the following structure:
#  {
#   'some_subroutine_name' : { 'category'  : 'subroutine',
#                              'code_lines': [line1, line2, ...] },
#   'some_function_name'   : { ... },
#    ...
#  }
print '  Searching code...'
print '  -----------------'
print
code_parts_dict = utils.getCodeParts(src_lines, prepend_module_name=True)
print


#
# Start generation of code for GAMBIT headers
#

# Variables to store the generated code
be_types_file_content = ''
frontend_file_content = ''


# ---- Functions/subroutines ----

# Create a copy of functions list
functions_left = list(cfg.load_functions)

print '  Functions/subroutines:'
print '  ----------------------'
print

#
# Loop over code parts to extract info on functions/subroutines.
#
for code_part_name, code_dict in code_parts_dict.items():

    code_lines    = code_dict['code_lines']
    code_category = code_dict['category']
    module_name   = code_dict['module']

    # Check that this code part is a function or subroutine
    if not code_category in ['function','subroutine']:
        continue

    f_name = code_part_name
    f_name_short = f_name.split('::')[-1]


    # Check if functions with the name f_name_short exist in other modules
    other_f_list = []
    found_other_f = False
    for other_f_name in code_parts_dict.keys():

        if not code_category in ['function','subroutine']:
            continue

        # Skip identical item
        if other_f_name == f_name:
            continue

        other_f_name_short = other_f_name.split('::')[-1]

        if module_name == '':
            if other_f_name_short == f_name:
                found_other_f = True
                other_f_list.append(other_f_name)

        elif module_name != '':
            if other_f_name == f_name_short:
                found_other_f = True
                other_f_list.append(other_f_name)

            if other_f_name != other_f_name_short:
                if other_f_name_short == f_name_short:
                    found_other_f = True
                    other_f_list.append(other_f_name)


    # Should the current function be identified with one of the entries in cfg.load_functions?
    if module_name == '':

        if (f_name in cfg.load_functions) and (not found_other_f):
            is_requested = True
            use_f_name = f_name

        elif (f_name in cfg.load_functions) and (found_other_f):
            is_requested = True
            use_f_name = f_name
            # print "    INFO: Assuming the requested function '%s' does not refer to any of the following similarly named functions:" % (f_name_short)
            # for other_f in other_f_list:
            #     print "          - '%s' in module '%s'" % (f_name_short, other_f.split('::')[0])

        else:
            is_requested = False

    elif module_name != '':

        if (f_name in cfg.load_functions):
            is_requested = True
            use_f_name = f_name

        elif (f_name_short in cfg.load_functions) and (not found_other_f):
            is_requested = True
            use_f_name = f_name_short
            # print "    INFO: Assuming the requested function '%s' refers to the one found in module '%s'." % (f_name_short, module_name)

        elif (f_name_short in cfg.load_functions) and (found_other_f):
            if f_name_short in other_f_list:
                print "    INFO: Will skip %s '%s' in module '%s'. The requested '%s' is identified with a different %s." % (code_category, f_name_short, module_name, f_name_short, code_category)
                is_requested = False
            else:
                print
                print "    ERROR: The %s name '%s' listed in load_functions is ambiguous. The following candidates have been identified:" % (code_category, f_name_short)
                print
                print "    - '%s' in module '%s'" % (f_name_short, module_name)
                for other_f in other_f_list:
                    print "    - '%s' in module '%s'" % (f_name_short, other_f.split('::')[0])
                print
                print "    Please specify both the module name and the %s name, separated by '::', e.g., 'some_module::some_%s'." % (code_category, code_category)
                print
                sys.exit()
        else:
            is_requested = False


    if is_requested:

        if module_name != '':
            print "    Found %s: '%s' in module '%s'" % (code_category, f_name_short, module_name)
        else:
            print "    Found %s: '%s'" % (code_category, f_name_short)

        # If function, get info on return type
        if code_category == 'function':
            return_type_info = utils.getFunctionReturnType(code_lines)


        # Get dictionary with parameter definitions
        parameter_defs = utils.getParameterDefs(code_lines)

        # Get list of argument names
        arg_names = utils.getFunctionArgumentNames(code_lines[0])

        # Get dict of dicts with info on the arguments
        arg_info_dict = utils.getVariablesDict(code_lines[1:], arg_names)

        # Construct a dict with all the info required to generate code for the GAMBIT frontend header
        f_dict = OrderedDict()
        f_dict['name'] = f_name_short
        if code_category == 'function':
            f_dict['return_type_info'] = return_type_info
        f_dict['category'] = code_category
        f_dict['arg_info'] = OrderedDict(arg_info_dict)
        f_dict['module'] = module_name

        # Generate code for the frontend header
        frontend_file_content += utils.generateFrontendFunction(f_dict, parameter_defs)

        # Remove function from list of functions remaining.
        functions_left.remove(use_f_name)

print
print

# ---- Common blocks ----

# Create a copy of common block list
common_blocks_left = list(cfg.load_common_blocks)

#
# Loop over code parts to extract info on common blocks.
#

print '  Common blocks:'
print '  --------------'
print
for code_part_name, code_dict in code_parts_dict.items():

    code_lines    = code_dict['code_lines']
    code_category = code_dict['category']

    # Get list of dicts with info on all common blocks in this code part.
    cb_dicts = utils.getCommonBlockDicts(code_lines)

    # Get dictionary with parameter definitions
    parameter_defs = utils.getParameterDefs(code_lines)


    # Loop over the common blocks found.
    for cb_dict in cb_dicts:

        if cb_dict['name'] in common_blocks_left:

            print "    In code part '%s': Found common block: '%s'" % (code_part_name, cb_dict['name'])

            # Get dict of dicts with info on the member variables for this common block.
            var_info_dict = utils.getVariablesDict(code_lines, cb_dict['member_names'])

            # Generate code for the backend types header.
            be_types_file_content += utils.generateTypeDeclCommonBlock(cb_dict, var_info_dict, parameter_defs)

            # Generate code for the frontend header.
            frontend_file_content += utils.generateFrontendCommonBlock(cb_dict)

            # Remove common block from list of blocks remaining.
            common_blocks_left.remove(cb_dict['name'])

    #
    # END: loop over common blocks in this code part
    #

    # Break out if all reqested blocks are done.
    if len(common_blocks_left) == 0:
        break

#
# END: loop over code parts
#

# Encapsulate backend types code in a Gambit namespace
be_types_file_content = utils.addNamespace(be_types_file_content, 'Gambit', indent=4)

# Write output files
with open(gb.output_file_path_be_types, 'w') as out_file_be_types:
    out_file_be_types.write('\n')
    out_file_be_types.write(be_types_file_content)

with open(gb.output_file_path_frontend, 'w') as out_file_frontend:
    out_file_frontend.write('\n')
    out_file_frontend.write(frontend_file_content)

# Print summary
print
print
print '  Summary:'
print '  --------'

# Check that all requested functions were found.
if len(functions_left) > 0:
    print
    for f_name in functions_left:
        print "    WARNING: Reqested function/subroutine '%s' was not found." % f_name

# Check that all requested common blocks were found.
if len(common_blocks_left) > 0:
    print
    for cb_name in common_blocks_left:
        print "    WARNING: Reqested common block '%s' was not found." % cb_name

print
print '    Generated code for GAMBIT written to files:'
print
print '    - ' + gb.output_file_path_frontend
print '    - ' + gb.output_file_path_be_types
print

print
print '  ~~~~  Done!  ~~~~'
print
print

Updated on 2022-08-03 at 12:58:06 +0000