def parse_preamble_data(statements, pobj, spec_name, endmatch, logger): """Parse module variables or DDT definitions from a module preamble or parse program variables from the beginning of a program. """ inspec = True mheaders = list() var_dict = VarDictionary(spec_name) psrc = ParseSource(spec_name, 'MODULE', pobj) active_table = None while inspec and (statements is not None): while len(statements) > 0: statement = statements.pop(0) # End program or module pmatch = endmatch.match(statement) asmatch = arg_table_start_re.match(statement) type_def = fortran_type_definition(statement) if asmatch is not None: active_table = asmatch.group(1).lower() elif (pmatch is not None) or is_contains_statement( statement, inspec): # We are done with the specification inspec = False if len(var_dict.variable_list()) > 0: mheader = MetadataHeader(title=spec_name, type_in='MODULE', module=spec_name, var_dict=var_dict, logger=logger) mheaders.append(mheader) # End if break elif (type_def is not None) and (type_def[0].lower() == active_table): statements, ddt = parse_type_def(statements, type_def, spec_name, pobj, logger) mheaders.append(ddt) active_table = None else: # We should have a variable definition to add if ((not is_comment_statement(statement, logger)) and (not parse_use_statement({}, statement, pobj, logger)) and (active_table == spec_name)): vars = parse_fortran_var_decl(statement, psrc, logger=logger) for var in vars: var_dict.add_variable(var) # End for # End if # End if # End while if inspec and (len(statements) == 0): statements = read_statements(pobj) # End if # End while return statements, mheaders
def __init__(self, parse_object=None, title=None, type_in=None, module=None, var_dict=None, property_table=False, logger=None): self._pobj = parse_object """If <parse_object> is not None, initialize from the current file and location in <parse_object>. If <parse_object> is None, initialize from <title>, <type>, <module>, and <var_dict>. Note that if <parse_object> is not None, <title>, <type>, <module>, and <var_dict> are ignored. """ if parse_object is None: if title is None: raise ParseInternalError('MetadataHeader requires a title') else: self._table_title = title # End if if type_in is None: raise ParseInternalError( 'MetadataHeader requires a header type') else: self._header_type = type # End if if module is None: raise ParseInternalError( 'MetadataHeader requires a module name') else: self._module_name = module # End if # Initialize our ParseSource parent super(MetadataHeader, self).__init__(self.title, self.header_type, self._pobj) self._variables = VarDictionary(self.title, logger=logger) for var in var_dict.variable_list(): # Let this crash if no dict self._variables.add_variable(var) # End for else: self.__init_from_file__(parse_object, property_table, logger) # End if # Categorize the variables self._var_intents = {'in': list(), 'out': list(), 'inout': list()} for var in self.variable_list(): intent = var.get_prop_value('intent') if intent is not None: self._var_intents[intent].append(var)
def parse_type_def(statements, type_def, mod_name, pobj, logger): """Parse a type definition from <statements> and return the remaining statements along with a MetadataTable object representing the type's variables.""" psrc = ParseSource(mod_name, 'ddt', pobj) seen_contains = False mheader = None var_dict = VarDictionary(type_def[0]) inspec = True while inspec and (statements is not None): while len(statements) > 0: statement = statements.pop(0) # End program or module pmatch = _END_TYPE_RE.match(statement) if pmatch is not None: # We hit the end of the type, make a header mheader = MetadataTable(table_name_in=type_def[0], table_type_in='ddt', module=mod_name, var_dict=var_dict, logger=logger) inspec = False elif is_contains_statement(statement, inspec): seen_contains = True elif not seen_contains: # Comment of variable if ((not is_comment_statement(statement)) and (not parse_use_statement(statement, logger))): dvars = parse_fortran_var_decl(statement, psrc, logger=logger) for var in dvars: var_dict.add_variable(var) # End for # End if else: # We are just skipping lines until the end type pass # End if # End while if inspec and (len(statements) == 0): statements = read_statements(pobj) # End if # End while return statements, mheader
def parse_type_def(statements, type_def, mod_name, pobj, logger): psrc = ParseSource(mod_name, 'DDT', pobj) seen_contains = False mheader = None var_dict = VarDictionary(type_def[0]) inspec = True while inspec and (statements is not None): while len(statements) > 0: statement = statements.pop(0) # End program or module pmatch = end_type_re.match(statement) if pmatch is not None: # We hit the end of the type, make a header mheader = MetadataHeader(title=type_def[0], type_in='DDT', module=mod_name, var_dict=var_dict, logger=logger) inspec = False elif is_contains_statement(statement, inspec): seen_contains = True elif not seen_contains: # Comment of variable if ((not is_comment_statement(statement, logger)) and (not parse_use_statement({}, statement, pobj, logger))): vars = parse_fortran_var_decl(statement, psrc, logger=logger) for var in vars: var_dict.add_variable(var) # End for # End if else: # We are just skipping lines until the end type pass # End if # End while if inspec and (len(statements) == 0): statements = read_statements(pobj) # End if # End while return statements, mheader
def write_host_cap(host_model, api, output_dir, logger): ############################################################################### """Write an API to allow <host_model> to call any configured CCPP suite""" module_name = "{}_ccpp_cap".format(host_model.name) cap_filename = os.path.join(output_dir, '{}.F90'.format(module_name)) if logger is not None: msg = 'Writing CCPP Host Model Cap for {} to {}' logger.info(msg.format(host_model.name, cap_filename)) # End if header = _HEADER.format(host_model=host_model.name) with FortranWriter(cap_filename, 'w', header, module_name) as cap: # Write module use statements maxmod = len(KINDS_MODULE) cap.write(' use {kinds}'.format(kinds=KINDS_MODULE), 1) modules = host_model.variable_locations() if modules: mlen = max([len(x[0]) for x in modules]) maxmod = max(maxmod, mlen) # End if mlen = max([len(x.module) for x in api.suites]) maxmod = max(maxmod, mlen) maxmod = max(maxmod, len(CONST_DDT_MOD)) for mod in modules: mspc = (maxmod - len(mod[0]))*' ' cap.write("use {}, {}only: {}".format(mod[0], mspc, mod[1]), 1) # End for mspc = ' '*(maxmod - len(CONST_DDT_MOD)) cap.write("use {}, {}only: {}".format(CONST_DDT_MOD, mspc, CONST_DDT_NAME), 1) cap.write_preamble() max_suite_len = 0 for suite in api.suites: max_suite_len = max(max_suite_len, len(suite.module)) # End for cap.write("! Public Interfaces", 1) # CCPP_STATE_MACH.transitions represents the host CCPP interface for stage in CCPP_STATE_MACH.transitions(): stmt = "public :: {host_model}_ccpp_physics_{stage}" cap.write(stmt.format(host_model=host_model.name, stage=stage), 1) # End for API.declare_inspection_interfaces(cap) # Write the host-model interfaces for constituents reg_name = constituent_register_subname(host_model) cap.write("public :: {}".format(reg_name), 1) numconsts_name = constituent_num_consts_funcname(host_model) cap.write("public :: {}".format(numconsts_name), 1) copyin_name = constituent_copyin_subname(host_model) cap.write("public :: {}".format(copyin_name), 1) copyout_name = constituent_copyout_subname(host_model) cap.write("public :: {}".format(copyout_name), 1) cap.write("", 0) cap.write("! Private module variables", 1) const_dict = add_constituent_vars(cap, host_model, api.suites, logger) cap.end_module_header() for stage in CCPP_STATE_MACH.transitions(): # Create a dict of local variables for stage host_local_vars = VarDictionary("{}_{}".format(host_model.name, stage)) # Create part call lists # Look for any loop-variable mismatch for suite in api.suites: spart_list = suite_part_list(suite, stage) for spart in spart_list: spart_args = spart.call_list.variable_list() for sp_var in spart_args: stdname = sp_var.get_prop_value('standard_name') hvar = const_dict.find_variable(standard_name=stdname, any_scope=True) if hvar is None: errmsg = 'No host model variable for {} in {}' raise CCPPError(errmsg.format(stdname, spart.name)) # End if # End for (loop over part variables) # End for (loop of suite parts) # End for (loop over suites) run_stage = stage == 'run' # All interfaces need the suite name apivars = [_SUITE_NAME_VAR] if run_stage: # Only the run phase needs a suite part name apivars.append(_SUITE_PART_VAR) # End if # Create a list of dummy arguments with correct intent settings callvars = host_model.call_list(stage) # Host interface dummy args hdvars = list() subst_dict = {} for hvar in callvars: protected = hvar.get_prop_value('protected') stdname = hvar.get_prop_value('standard_name') if stdname in CCPP_LOOP_VAR_STDNAMES: protected = True # Cannot modify a loop variable # End if if protected: subst_dict['intent'] = 'in' else: subst_dict['intent'] = 'inout' # End if hdvars.append(hvar.clone(subst_dict, source_name=_API_SRC_NAME)) # End for lnames = [x.get_prop_value('local_name') for x in apivars + hdvars] api_vlist = ", ".join(lnames) cap.write(_SUBHEAD.format(api_vars=api_vlist, host_model=host_model.name, stage=stage), 1) # Write out any suite part use statements for suite in api.suites: mspc = (max_suite_len - len(suite.module))*' ' spart_list = suite_part_list(suite, stage) for spart in spart_list: stmt = "use {}, {}only: {}" cap.write(stmt.format(suite.module, mspc, spart.name), 2) # End for # End for # Write out any host model DDT input var use statements host_model.ddt_lib.write_ddt_use_statements(hdvars, cap, 2, pad=max_suite_len) cap.write("", 1) # Write out dummy arguments for var in apivars: var.write_def(cap, 2, host_model) # End for for var in hdvars: var.write_def(cap, 2, host_model) # End for for var in host_local_vars.variable_list(): var.write_def(cap, 2, host_model) # End for cap.write('', 0) # Write out the body clauses errmsg_name, errflg_name = api.get_errinfo_names() # Initialize err variables cap.write('{errflg} = 0'.format(errflg=errflg_name), 2) cap.write('{errmsg} = ""'.format(errmsg=errmsg_name), 2) else_str = '' for suite in api.suites: stmt = "{}if (trim(suite_name) == '{}') then" cap.write(stmt.format(else_str, suite.name), 2) if stage == 'run': el2_str = '' spart_list = suite_part_list(suite, stage) for spart in spart_list: pname = spart.name[len(suite.name)+1:] stmt = "{}if (trim(suite_part) == '{}') then" cap.write(stmt.format(el2_str, pname), 3) call_str = suite_part_call_list(host_model, const_dict, spart, True) cap.write("call {}({})".format(spart.name, call_str), 4) el2_str = 'else ' # End for cap.write("else", 3) emsg = "write({errmsg}, '(3a)')".format(errmsg=errmsg_name) emsg += '"No suite part named ", ' emsg += 'trim(suite_part), ' emsg += '" found in suite {sname}"'.format(sname=suite.name) cap.write(emsg, 4) cap.write("{errflg} = 1".format(errflg=errflg_name), 4) cap.write("end if", 3) else: spart = suite.phase_group(stage) call_str = suite_part_call_list(host_model, const_dict, spart, False) stmt = "call {}_{}({})" cap.write(stmt.format(suite.name, stage, call_str), 3) # End if else_str = 'else ' # End for cap.write("else", 2) emsg = "write({errmsg}, '(3a)')".format(errmsg=errmsg_name) emsg += '"No suite named ", ' emsg += 'trim(suite_name), "found"' cap.write(emsg, 3) cap.write("{errflg} = 1".format(errflg=errflg_name), 3) cap.write("end if", 2) cap.write(_SUBFOOT.format(host_model=host_model.name, stage=stage), 1) # End for # Write the API inspection routines (e.g., list of suites) api.write_inspection_routines(cap) # Write the constituent initialization interfaces err_vars = host_model.find_error_variables() const_obj_name = constituent_model_object_name(host_model) cap.write("", 0) const_names_name = constituent_model_const_stdnames(host_model) const_indices_name = constituent_model_const_indices(host_model) ConstituentVarDict.write_host_routines(cap, host_model, reg_name, numconsts_name, copyin_name, copyout_name, const_obj_name, const_names_name, const_indices_name, api.suites, err_vars) # End with return cap_filename
def add_constituent_vars(cap, host_model, suite_list, logger): ############################################################################### """Create a DDT library containing array reference variables for each constituent field for all suites in <suite_list>. Create and return a dictionary containing an index variable for each of the constituents as well as the variables from the DDT object. Also, write declarations for these variables to <cap>. Since the constituents are in a DDT (ccpp_constituent_properties_t), create a metadata table with the required information, then parse it to create the dictionary. """ # First create a MetadataTable for the constituents DDT stdname_layer = "ccpp_constituents_num_layer_consts" stdname_interface = "ccpp_constituents_num_interface_consts" stdname_2d = "ccpp_constituents_num_2d_consts" horiz_dim = "horizontal_dimension" vert_layer_dim = "vertical_layer_dimension" vert_interface_dim = "vertical_interface_dimension" array_layer = "vars_layer" array_interface = "vars_interface" array_2d = "vars_2d" # Table preamble (leave off ccpp-table-properties header) ddt_mdata = [ #"[ccpp-table-properties]", " name = {}".format(CONST_DDT_NAME), " type = ddt", "[ccpp-arg-table]", " name = {}".format(CONST_DDT_NAME), " type = ddt", "[ num_layer_vars ]", " standard_name = {}".format(stdname_layer), " units = count", " dimensions = ()", " type = integer", "[ num_interface_vars ]", " standard_name = {}".format(stdname_interface), " units = count", " dimensions = ()", " type = integer", "[ num_2d_vars ]", " standard_name = {}".format(stdname_2d), " units = count", " dimensions = ()", " type = integer", "[ {} ]".format(array_layer), " standard_name = ccpp_constituents_array_of_layer_consts", " units = none", " dimensions = ({}, {}, {})".format(horiz_dim, vert_layer_dim, stdname_layer), " type = real", " kind = kind_phys", "[ {} ]".format(array_interface), " standard_name = ccpp_constituents_array_of_interface_consts", " units = none", " dimensions = ({}, {}, {})".format(horiz_dim, vert_interface_dim, stdname_interface), " type = real", " kind = kind_phys", "[ {} ]".format(array_2d), " standard_name = ccpp_constituents_array_of_2d_consts", " units = none", " dimensions = ({}, {})".format(horiz_dim, stdname_2d), " type = real", " kind = kind_phys"] # Add entries for each constituent (once per standard name) const_stdnames = set() for suite in suite_list: if logger is not None: lmsg = "Adding constituents from {} to {}" logger.debug(lmsg.format(suite.name, host_model.name)) # end if scdict = suite.constituent_dictionary() for cvar in scdict.variable_list(): std_name = cvar.get_prop_value('standard_name') if std_name not in const_stdnames: # Add a metadata entry for this constituent # Check dimensions and figure vertical dimension # Currently, we only support variables with first dimension, # horizontal_dimension, and second (optional) dimension, # vertical_layer_dimension or vertical_interface_dimension dims = cvar.get_dimensions() if (len(dims) < 1) or (len(dims) > 2): emsg = "Unsupported constituent dimensions, '{}'" dimstr = "({})".format(", ".join(dims)) raise CCPPError(emsg.format(dimstr)) # end if hdim = dims[0].split(':')[-1] if hdim != 'horizontal_dimension': emsg = "Unsupported first constituent dimension, '{}', " emsg += "must be 'horizontal_dimension'" raise CCPPError(emsg.format(hdim)) # end if if len(dims) > 1: vdim = dims[1].split(':')[-1] if vdim == vert_layer_dim: cvar_array_name = array_layer elif vdim == vert_interface_dim: cvar_array_name = array_interface else: emsg = "Unsupported vertical constituent dimension, " emsg += "'{}', must be '{}' or '{}'" raise CCPPError(emsg.format(vdim, vert_layer_dim, vert_interface_dim)) # end if else: cvar_array_name = array_2d # end if # First, create an index variable for <cvar> ind_std_name = "index_of_{}".format(std_name) loc_name = "{}(:,:,{})".format(cvar_array_name, ind_std_name) ddt_mdata.append("[ {} ]".format(loc_name)) ddt_mdata.append(" standard_name = {}".format(std_name)) units = cvar.get_prop_value('units') ddt_mdata.append(" units = {}".format(units)) dimstr = "({})".format(", ".join(dims)) ddt_mdata.append(" dimensions = {}".format(dimstr)) vtype = cvar.get_prop_value('type') vkind = cvar.get_prop_value('kind') ddt_mdata.append(" type = {} | kind = {}".format(vtype, vkind)) const_stdnames.add(std_name) # end if # end for # end for # Parse this table using a fake filename parse_obj = ParseObject("{}_constituent_mod.meta".format(host_model.name), ddt_mdata) ddt_table = MetadataTable(parse_object=parse_obj, logger=logger) ddt_name = ddt_table.sections()[0].title ddt_lib = DDTLibrary('{}_constituent_ddtlib'.format(host_model.name), ddts=ddt_table.sections(), logger=logger) # A bit of cleanup del parse_obj del ddt_mdata # Now, create the "host constituent module" dictionary const_dict = VarDictionary("{}_constituents".format(host_model.name), parent_dict=host_model) # Add in the constituents object prop_dict = {'standard_name' : "ccpp_model_constituents_object", 'local_name' : constituent_model_object_name(host_model), 'dimensions' : '()', 'units' : "None", 'ddt_type' : ddt_name} const_var = Var(prop_dict, _API_SOURCE) const_var.write_def(cap, 1, const_dict) ddt_lib.collect_ddt_fields(const_dict, const_var) # Declare variable for the constituent standard names array max_csname = max([len(x) for x in const_stdnames]) if const_stdnames else 0 num_const_fields = len(const_stdnames) cs_stdname = constituent_model_const_stdnames(host_model) const_list = sorted(const_stdnames) if const_list: const_strs = ['"{}{}"'.format(x, ' '*(max_csname - len(x))) for x in const_list] cs_stdame_initstr = " = (/ " + ", ".join(const_strs) + " /)" else: cs_stdame_initstr = "" # end if cap.write("character(len={}) :: {}({}){}".format(max_csname, cs_stdname, num_const_fields, cs_stdame_initstr), 1) # Declare variable for the constituent standard names array array_name = constituent_model_const_indices(host_model) cap.write("integer :: {}({}) = -1".format(array_name, num_const_fields), 1) # Add individual variables for each index var to the const_dict for index, std_name in enumerate(const_list): ind_std_name = "index_of_{}".format(std_name) ind_loc_name = "{}({})".format(array_name, index + 1) prop_dict = {'standard_name' : ind_std_name, 'local_name' : ind_loc_name, 'dimensions' : '()', 'units' : 'index', 'protected' : "True", 'type' : 'integer', 'kind' : ''} ind_var = Var(prop_dict, _API_SOURCE) const_dict.add_variable(ind_var) # end for # Add vertical dimensions for DDT call strings pver = host_model.find_variable(standard_name=vert_layer_dim, any_scope=False) if pver is not None: prop_dict = {'standard_name' : vert_layer_dim, 'local_name' : pver.get_prop_value('local_name'), 'units' : 'count', 'type' : 'integer', 'protected' : 'True', 'dimensions' : '()'} if const_dict.find_variable(standard_name=vert_layer_dim, any_scope=False) is None: ind_var = Var(prop_dict, _API_SOURCE) const_dict.add_variable(ind_var) # end if # end if pver = host_model.find_variable(standard_name=vert_interface_dim, any_scope=False) if pver is not None: prop_dict = {'standard_name' : vert_interface_dim, 'local_name' : pver.get_prop_value('local_name'), 'units' : 'count', 'type' : 'integer', 'protected' : 'True', 'dimensions' : '()'} if const_dict.find_variable(standard_name=vert_interface_dim, any_scope=False) is None: ind_var = Var(prop_dict, _API_SOURCE) const_dict.add_variable(ind_var) # end if # end if return const_dict
ParseContext(filename="host_cap.F90")) _SUITE_NAME_VAR = Var({'local_name':'suite_name', 'standard_name':'suite_name', 'intent':'in', 'type':'character', 'kind':'len=*', 'units':'', 'protected':'True', 'dimensions':'()'}, _API_SOURCE) _SUITE_PART_VAR = Var({'local_name':'suite_part', 'standard_name':'suite_part', 'intent':'in', 'type':'character', 'kind':'len=*', 'units':'', 'protected':'True', 'dimensions':'()'}, _API_SOURCE) # Used to prevent loop substitution lookups _BLANK_DICT = VarDictionary(_API_SRC_NAME) ############################################################################### def suite_part_list(suite, stage): ############################################################################### """Return a list of all the suite parts for this stage""" run_stage = stage == 'run' if run_stage: spart_list = list() for spart in suite.groups: if suite.is_run_group(spart): spart_list.append(spart) # End if # End for else: spart_list = [suite.phase_group(stage)]
def __init__(self, meta_tables, name_in, logger): self.__name = name_in self.__var_locations = {} # Local name to module map self.__loop_vars = None # Loop control vars in interface calls self.__used_variables = None # Local names which have been requested self.__deferred_finds = None # Used variables that were missed at first # First, process DDT headers meta_headers = list() for sect in [x.sections() for x in meta_tables.values()]: meta_headers.extend(sect) # end for # Initialize our dictionaries # Initialize variable dictionary super(HostModel, self).__init__(self.name, logger=logger) self.__ddt_lib = DDTLibrary( '{}_ddts'.format(self.name), ddts=[d for d in meta_headers if d.header_type == 'ddt'], logger=logger) self.__ddt_dict = VarDictionary("{}_ddt_vars".format(self.name), parent_dict=self, logger=logger) # Now, process the code headers by type self.__metadata_tables = meta_tables for header in [h for h in meta_headers if h.header_type != 'ddt']: title = header.title if logger is not None: msg = 'Adding {} {} to host model' logger.debug(msg.format(header.header_type, title)) # End if if header.header_type == 'module': # Set the variable modules modname = header.title for var in header.variable_list(): self.add_variable(var) lname = var.get_prop_value('local_name') self.__var_locations[lname] = modname self.ddt_lib.check_ddt_type(var, header, lname=lname) if var.is_ddt(): self.ddt_lib.collect_ddt_fields(self.__ddt_dict, var) # End if # End for elif header.header_type == 'host': if self.__name is None: # Grab the first host name we see self.__name = header.name # End if for var in header.variable_list(): self.add_variable(var) self.ddt_lib.check_ddt_type(var, header) if var.is_ddt(): self.ddt_lib.collect_ddt_fields(self.__ddt_dict, var) # End if # End for loop_vars = header.variable_list(std_vars=False, loop_vars=True, consts=False) if loop_vars: # loop_vars are part of the host-model interface call # at run time. As such, they override the host-model # array dimensions. self.__loop_vars = VarDictionary(self.name) # End if for hvar in loop_vars: std_name = hvar.get_prop_value('standard_name') if std_name not in self.__loop_vars: self.__loop_vars.add_variable(hvar) else: ovar = self.__loop_vars[std_name] ctx1 = context_string(ovar.context) ctx2 = context_string(hvar.context) lname1 = ovar.get_prop_value('local_name') lname2 = hvar.get_prop_value('local_name') errmsg = ("Duplicate host loop var for {n}:\n" " Dup: {l1}{c1}\n Orig: {l2}{c2}") raise CCPPError( errmsg.format(n=self.name, l1=lname1, c1=ctx1, l2=lname2, c2=ctx2)) # End if # End for else: errmsg = "Invalid host model metadata header type, {} ({}){}" errmsg += "\nType must be 'module' or 'host'" ctx = context_string(header.context) raise CCPPError( errmsg.format(header.title, header.header_type, ctx)) # End if # End while if self.name is None: errmsg = 'No name found for host model, add a host metadata entry' raise CCPPError(errmsg) # End if # Finally, turn on the use meter so we know which module variables # to 'use' in a host cap. self.__used_variables = set() # Local names which have been requested self.__deferred_finds = set( ) # Used variables that were missed at first
class HostModel(VarDictionary): """Class to hold the data from a host model""" def __init__(self, meta_tables, name_in, logger): self.__name = name_in self.__var_locations = {} # Local name to module map self.__loop_vars = None # Loop control vars in interface calls self.__used_variables = None # Local names which have been requested self.__deferred_finds = None # Used variables that were missed at first # First, process DDT headers meta_headers = list() for sect in [x.sections() for x in meta_tables.values()]: meta_headers.extend(sect) # end for # Initialize our dictionaries # Initialize variable dictionary super(HostModel, self).__init__(self.name, logger=logger) self.__ddt_lib = DDTLibrary( '{}_ddts'.format(self.name), ddts=[d for d in meta_headers if d.header_type == 'ddt'], logger=logger) self.__ddt_dict = VarDictionary("{}_ddt_vars".format(self.name), parent_dict=self, logger=logger) # Now, process the code headers by type self.__metadata_tables = meta_tables for header in [h for h in meta_headers if h.header_type != 'ddt']: title = header.title if logger is not None: msg = 'Adding {} {} to host model' logger.debug(msg.format(header.header_type, title)) # End if if header.header_type == 'module': # Set the variable modules modname = header.title for var in header.variable_list(): self.add_variable(var) lname = var.get_prop_value('local_name') self.__var_locations[lname] = modname self.ddt_lib.check_ddt_type(var, header, lname=lname) if var.is_ddt(): self.ddt_lib.collect_ddt_fields(self.__ddt_dict, var) # End if # End for elif header.header_type == 'host': if self.__name is None: # Grab the first host name we see self.__name = header.name # End if for var in header.variable_list(): self.add_variable(var) self.ddt_lib.check_ddt_type(var, header) if var.is_ddt(): self.ddt_lib.collect_ddt_fields(self.__ddt_dict, var) # End if # End for loop_vars = header.variable_list(std_vars=False, loop_vars=True, consts=False) if loop_vars: # loop_vars are part of the host-model interface call # at run time. As such, they override the host-model # array dimensions. self.__loop_vars = VarDictionary(self.name) # End if for hvar in loop_vars: std_name = hvar.get_prop_value('standard_name') if std_name not in self.__loop_vars: self.__loop_vars.add_variable(hvar) else: ovar = self.__loop_vars[std_name] ctx1 = context_string(ovar.context) ctx2 = context_string(hvar.context) lname1 = ovar.get_prop_value('local_name') lname2 = hvar.get_prop_value('local_name') errmsg = ("Duplicate host loop var for {n}:\n" " Dup: {l1}{c1}\n Orig: {l2}{c2}") raise CCPPError( errmsg.format(n=self.name, l1=lname1, c1=ctx1, l2=lname2, c2=ctx2)) # End if # End for else: errmsg = "Invalid host model metadata header type, {} ({}){}" errmsg += "\nType must be 'module' or 'host'" ctx = context_string(header.context) raise CCPPError( errmsg.format(header.title, header.header_type, ctx)) # End if # End while if self.name is None: errmsg = 'No name found for host model, add a host metadata entry' raise CCPPError(errmsg) # End if # Finally, turn on the use meter so we know which module variables # to 'use' in a host cap. self.__used_variables = set() # Local names which have been requested self.__deferred_finds = set( ) # Used variables that were missed at first @property def name(self): """Return the host model name""" return self.__name @property def loop_vars(self): """Return this host model's loop variables""" return self.__loop_vars @property def ddt_lib(self): """Return this host model's DDT library""" return self.__ddt_lib # XXgoldyXX: v needed? @property def constituent_module(self): """Return the name of host model constituent module""" return "{}_ccpp_constituents".format(self.name) # XXgoldyXX: ^ needed? def argument_list(self, loop_vars=True): """Return a string representing the host model variable arg list""" args = [ v.call_string(self) for v in self.variable_list(loop_vars=loop_vars, consts=False) ] return ', '.join(args) def metadata_tables(self): """Return a copy of this host models metadata tables""" return dict(self.__metadata_tables) def host_variable_module(self, local_name): """Return the module name for a host variable""" if local_name in self.__var_locations: return self.__var_locations[local_name] # End if return None def variable_locations(self): """Return a set of module-variable and module-type pairs. These represent the locations of all host model data with a listed source location (variables with no <module> source are omitted).""" varset = set() lnames = self.prop_list('local_name') # Attempt to realize deferred lookups if self.__deferred_finds is not None: for std_name in list(self.__deferred_finds): var = self.find_variable(standard_name=std_name) if var is not None: self.__deferred_finds.remove(std_name) # End if # End for # End if # Now, find all the used module variables for name in lnames: module = self.host_variable_module(name) used = self.__used_variables and (name in self.__used_variables) if module and used: varset.add((module, name)) # No else, either no module or a zero-length module name # End if # End for return varset def find_variable(self, standard_name=None, source_var=None, any_scope=False, clone=None, search_call_list=False, loop_subst=False): """Return the host model variable matching <standard_name> or None If <loop_subst> is True, substitute a begin:end range for an extent. """ my_var = super(HostModel, self).find_variable(standard_name=standard_name, source_var=source_var, any_scope=any_scope, clone=clone, search_call_list=search_call_list, loop_subst=loop_subst) if my_var is None: # Check our DDT library if standard_name is None: if source_var is None: emsg = ("One of <standard_name> or <source_var> " + "must be passed.") raise ParseInternalError(emsg) # end if standard_name = source_var.get_prop_value('standard_name') # end if # Since we are the parent of the DDT library, only check that dict my_var = self.__ddt_dict.find_variable(standard_name=standard_name, any_scope=False) # End if if loop_subst: if my_var is None: my_var = self.find_loop_subst(standard_name) # End if if my_var is not None: # If we get here, the host does not have the requested # variable but does have a replacement set. Create a new # variable to use to send to suites. ##XXgoldyXX: This cannot be working since find_loop_subst ## returns a tuple new_name = self.new_internal_variable_name(prefix=self.name) ctx = ParseContext(filename='host_model.py') new_var = my_var.clone(new_name, source_name=self.name, source_type="HOST", context=ctx) self.add_variable(new_var) my_var = new_var # End if # End if if my_var is None: if self.__deferred_finds is not None: self.__deferred_finds.add(standard_name) # End if elif self.__used_variables is not None: lname = my_var.get_prop_value('local_name') # Try to add any index references (should be method?) imatch = FORTRAN_SCALAR_REF_RE.match(lname) if imatch is not None: vdims = [ x.strip() for x in imatch.group(2).split(',') if ':' not in x ] for vname in vdims: _ = self.find_variable(standard_name=vname) # End for # End if if isinstance(my_var, VarDDT): lname = my_var.get_parent_prop('local_name') # End if self.__used_variables.add(lname) # End if return my_var def add_variable(self, newvar, exists_ok=False, gen_unique=False, adjust_intent=False): """Add <newvar> if it does not conflict with existing entries. For the host model, this includes entries in used DDT variables. If <exists_ok> is True, attempting to add an identical copy is okay. If <gen_unique> is True, a new local_name will be created if a local_name collision is detected. if <adjust_intent> is True, adjust conflicting intents to inout.""" standard_name = newvar.get_prop_value('standard_name') cvar = self.find_variable(standard_name=standard_name, any_scope=False) if cvar is None: # Check the DDT dictionary cvar = self.__ddt_dict.find_variable(standard_name=standard_name, any_scope=False) # end if if cvar and (not exists_ok): emsg = "Attempt to add duplicate host model variable, {}{}." emsg += "\nVariable originally defined{}" ntx = context_string(newvar.context) ctx = context_string(cvar.context) raise CCPPError(emsg.format(standard_name, ntx, ctx)) # end if # No collision, proceed normally super(HostModel, self).add_variable(newvar=newvar, exists_ok=exists_ok, gen_unique=gen_unique, adjust_intent=False) def add_host_variable_module(self, local_name, module, logger=None): """Add a module name location for a host variable""" if local_name not in self.__var_locations: if logger is not None: emsg = 'Adding variable, {}, from module, {}' logger.debug(emsg.format(local_name, module)) # End if self.__var_locations[local_name] = module else: emsg = "Host variable, {}, already located in module" raise CCPPError(emsg.format(self.__var_locations[local_name])) # End if def call_list(self, phase): "Return the list of variables passed by the host model to the host cap" hdvars = list() loop_vars = phase == 'run' for hvar in self.variable_list(loop_vars=loop_vars, consts=False): lname = hvar.get_prop_value('local_name') if self.host_variable_module(lname) is None: hdvars.append(hvar) # End if # End for return hdvars
def parse_scheme_metadata(statements, pobj, spec_name, table_name, logger): "Parse dummy argument information from a subroutine" psrc = None mheader = None var_dict = None scheme_name = None # Find the subroutine line, should be first executable statement inpreamble = False insub = True while insub and (statements is not None): while len(statements) > 0: statement = statements.pop(0) smatch = subroutine_re.match(statement) esmatch = end_subroutine_re.match(statement) pmatch = endmodule_re.match(statement) asmatch = arg_table_start_re.match(statement) if asmatch is not None: # We have run off the end of something, hope that is okay # Put this statement back for the caller to deal with statements.insert(0, statement) insub = False break elif (pmatch is not None): # We have run off the end of the module, hope that is okay pobj.leave_region('MODULE', region_name=spec_name) insub = False break elif smatch is not None: scheme_name = smatch.group(1) inpreamble = scheme_name == table_name if inpreamble: if smatch.group(2) is not None: scheme_args = [ x.strip().lower() for x in smatch.group(2).split(',') ] else: scheme_args = list() # End if scheme_set = set(scheme_args) var_dict = VarDictionary(scheme_name) psrc = ParseSource(scheme_name, 'SCHEME', pobj) # End if elif inpreamble: # Process a preamble statement (use or argument declaration) if esmatch is not None: inpreamble = False insub = False elif ((not is_comment_statement(statement, logger)) and (not parse_use_statement({}, statement, pobj, logger)) and ('intent' in statement.lower())): vars = parse_fortran_var_decl(statement, psrc, logger=logger) for var in vars: lname = var.get_prop_value('local_name').lower() if lname in scheme_set: scheme_set.remove(lname) else: raise ParseSyntaxError('dummy argument', token=lname, context=pobj) # End if var_dict.add_variable(var) # End for # End if # End if # End while if insub and (len(statements) == 0): statements = read_statements(pobj) # End if # End while if (scheme_name is not None) and (var_dict is not None): mheader = MetadataHeader(title=scheme_name, type_in='SCHEME', module=spec_name, var_dict=var_dict, logger=logger) # End if return statements, mheader
def __init_from_file__(self, parse_object, logger): # Read the table preamble, assume the caller already figured out # the first line of the header using the table_start method. curr_line, curr_line_num = self._pobj.next_line() self._table_title = None self._header_type = None self._module_name = None while (curr_line is not None) and (not self.variable_start(curr_line)) and ( not MetadataHeader.table_start(curr_line)): for property in self.parse_config_line(curr_line): # Manually parse name, type, and module properties key = property[0].strip().lower() value = property[1].strip() if key == 'name': self._table_title = value elif key == 'type': if value not in ['module', 'scheme', 'ddt']: raise ParseSyntaxError("metadata table type", token=value, context=self._pobj) # End if self._header_type = value elif key == 'module': if value == "None": raise ParseSyntaxError("metadata table, no module", context=self._pobj) else: self._module_name = value # End if else: raise ParseSyntaxError("metadata table start property", token=value, context=self._pobj) # End if # End for curr_line, curr_line_num = self._pobj.next_line() # End while if self.title is None: raise ParseSyntaxError("metadata header start, no table name", token=curr_line, context=self._pobj) elif self.header_type is None: raise ParseSyntaxError("metadata header start, no table type", token=curr_line, context=self._pobj) elif self.header_type == "ddt": register_fortran_ddt_name(self.title) # End if # Initialize our ParseSource parent super(MetadataHeader, self).__init__(self.title, self.header_type, self._pobj) # Read the variables valid_lines = True self._variables = VarDictionary(self.title, logger=logger) while valid_lines: newvar, curr_line = self.parse_variable(curr_line) valid_lines = newvar is not None if valid_lines: self._variables.add_variable(newvar) # Check to see if we hit the end of the table valid_lines = not MetadataHeader.table_start(curr_line)
class MetadataHeader(ParseSource): """Class to hold all information from a metadata header >>> MetadataHeader(ParseObject("foobar.txt", \ ["name = foobar", "type = scheme", "module = foo", \ "[ im ]", "standard_name = horizontal_loop_extent", \ "long_name = horizontal loop extent, start at 1", \ "units = index | type = integer", \ "dimensions = () | intent = in"])) #doctest: +ELLIPSIS <__main__.MetadataHeader foo / foobar at 0x...> >>> MetadataHeader(ParseObject("foobar.txt", \ ["name = foobar", "type = scheme", "module = foobar", \ "[ im ]", "standard_name = horizontal_loop_extent", \ "long_name = horizontal loop extent, start at 1", \ "units = index | type = integer", \ "dimensions = () | intent = in"])).get_var(standard_name='horizontal_loop_extent') #doctest: +ELLIPSIS <metavar.Var horizontal_loop_extent: im at 0x...> >>> MetadataHeader(ParseObject("foobar.txt", \ ["name = foobar", "module = foo", \ "[ im ]", "standard_name = horizontal_loop_extent", \ "long_name = horizontal loop extent, start at 1", \ "units = index | type = integer", \ "dimensions = () | intent = in", \ " subroutine foo()"])).get_var(standard_name='horizontal_loop_extent') #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ParseSyntaxError: Missing metadata header type, at foobar.txt:7 >>> MetadataHeader(ParseObject("foobar.txt", \ ["name = foobar", "type = scheme", "module=foobar", \ "[ im ]", "standard_name = horizontal_loop_extent", \ "long_name = horizontal loop extent, start at 1", \ "units = index | type = integer", \ "dimensions = () | intent = in", \ ""], line_start=0)).get_var(standard_name='horizontal_loop_extent').get_prop_value('local_name') 'im' >>> MetadataHeader(ParseObject("foobar.txt", \ ["name = foobar", "type = scheme" \ "[ im ]", "standard_name = horizontal loop extent", \ "long_name = horizontal loop extent, start at 1", \ "units = index | type = integer", \ "dimensions = () | intent = in", \ ""], line_start=0)).get_var(standard_name='horizontal_loop_extent') #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ParseSyntaxError: Invalid variable property value, 'horizontal loop extent', at foobar.txt:2 >>> MetadataHeader(ParseObject("foobar.txt", \ ["[ccpp-arg-table]", "name = foobar", "type = scheme" \ "[ im ]", "standard_name = horizontal loop extent", \ "long_name = horizontal loop extent, start at 1", \ "units = index | type = integer", \ "dimensions = () | intent = in", \ ""], line_start=0)).get_var(standard_name='horizontal_loop_extent') #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ParseSyntaxError: Invalid property syntax, '[ccpp-arg-table]', at foobar.txt:1 >>> MetadataHeader(ParseObject("foobar.txt", \ ["name = foobar", "module = foo" \ "[ im ]", "standard_name = horizontal loop extent", \ "long_name = horizontal loop extent, start at 1", \ "units = index | type = integer", \ "dimensions = () | intent = in", \ ""], line_start=0)).get_var(standard_name='horizontal_loop_extent') #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ParseSyntaxError: Invalid metadata header start, no table type, at foobar.txt:2 >>> MetadataHeader.__var_start__.match('[ qval ]') #doctest: +ELLIPSIS <_sre.SRE_Match object at 0x...> >>> MetadataHeader.__var_start__.match('[ qval(hi_mom) ]') #doctest: +ELLIPSIS <_sre.SRE_Match object at 0x...> """ __header_start__ = re.compile(r"(?i)\s*\[\s*ccpp-arg-table\s*\]") __var_start__ = re.compile(r"^\[\s*(" + FORTRAN_ID + r"|" + LITERAL_INT + r"|" + FORTRAN_SCALAR_REF + r")\s*\]$") __blank_line__ = re.compile(r"\s*[#;]") __html_template__ = """ <html> <head> <title>{title}</title> <meta charset="UTF-8"> </head> <body> <table border="1"> {header}{contents}</table> </body> </html> """ def __init__(self, parse_object=None, title=None, type_in=None, module=None, var_dict=None, logger=None): self._pobj = parse_object """If <parse_object> is not None, initialize from the current file and location in <parse_object>. If <parse_object> is None, initialize from <title>, <type>, <module>, and <var_dict>. Note that if <parse_object> is not None, <title>, <type>, <module>, and <var_dict> are ignored. """ if parse_object is None: if title is None: raise ParseInternalError('MetadataHeader requires a title') else: self._table_title = title # End if if type_in is None: raise ParseInternalError( 'MetadataHeader requires a header type') else: self._header_type = type # End if if module is None: raise ParseInternalError( 'MetadataHeader requires a module name') else: self._module_name = module # End if # Initialize our ParseSource parent super(MetadataHeader, self).__init__(self.title, self.header_type, self._pobj) self._variables = VarDictionary(self.title, logger=logger) for var in var_dict.variable_list(): # Let this crash if no dict self._variables.add_variable(var) # End for else: self.__init_from_file__(parse_object, logger) # End if # Categorize the variables self._var_intents = {'in': list(), 'out': list(), 'inout': list()} for var in self.variable_list(): intent = var.get_prop_value('intent') if intent is not None: self._var_intents[intent].append(var) # End if # End for def __init_from_file__(self, parse_object, logger): # Read the table preamble, assume the caller already figured out # the first line of the header using the table_start method. curr_line, curr_line_num = self._pobj.next_line() self._table_title = None self._header_type = None self._module_name = None while (curr_line is not None) and (not self.variable_start(curr_line)) and ( not MetadataHeader.table_start(curr_line)): for property in self.parse_config_line(curr_line): # Manually parse name, type, and module properties key = property[0].strip().lower() value = property[1].strip() if key == 'name': self._table_title = value elif key == 'type': if value not in ['module', 'scheme', 'ddt']: raise ParseSyntaxError("metadata table type", token=value, context=self._pobj) # End if self._header_type = value elif key == 'module': if value == "None": raise ParseSyntaxError("metadata table, no module", context=self._pobj) else: self._module_name = value # End if else: raise ParseSyntaxError("metadata table start property", token=value, context=self._pobj) # End if # End for curr_line, curr_line_num = self._pobj.next_line() # End while if self.title is None: raise ParseSyntaxError("metadata header start, no table name", token=curr_line, context=self._pobj) elif self.header_type is None: raise ParseSyntaxError("metadata header start, no table type", token=curr_line, context=self._pobj) elif self.header_type == "ddt": register_fortran_ddt_name(self.title) # End if # Initialize our ParseSource parent super(MetadataHeader, self).__init__(self.title, self.header_type, self._pobj) # Read the variables valid_lines = True self._variables = VarDictionary(self.title, logger=logger) while valid_lines: newvar, curr_line = self.parse_variable(curr_line) valid_lines = newvar is not None if valid_lines: self._variables.add_variable(newvar) # Check to see if we hit the end of the table valid_lines = not MetadataHeader.table_start(curr_line) # No else, we just run off the end of the table # End if # End while def parse_config_line(self, line): "Parse a config line and return a list of keyword value pairs." parse_items = list() if line is None: pass # No properties on this line elif MetadataHeader.is_blank(line): pass # No properties on this line else: properties = line.strip().split('|') for property in properties: pitems = property.split('=', 1) if len(pitems) < 2: raise ParseSyntaxError("variable property syntax", token=property, context=self._pobj) else: parse_items.append(pitems) # End if # End for # End if return parse_items def parse_variable(self, curr_line): # The header line has the format [ <valid_fortran_symbol> ] # Parse header valid_line = (curr_line is not None) and ( not MetadataHeader.table_start(curr_line)) if valid_line: local_name = self.variable_start( curr_line) # caller handles exception else: local_name = None # End if if local_name is None: # This is not a valid variable line, punt (should be end of table) valid_line = False # End if # Parse lines until invalid line is found # NB: Header variables cannot have embedded blank lines if valid_line: var_props = {} var_props['local_name'] = local_name else: var_props = None # End if while valid_line: curr_line, curr_line_num = self._pobj.next_line() valid_line = ((curr_line is not None) and (not MetadataHeader.is_blank(curr_line)) and (not MetadataHeader.table_start(curr_line)) and (self.variable_start(curr_line) is None)) # A valid line may have multiple properties (separated by '|') if valid_line: properties = self.parse_config_line(curr_line) for property in properties: try: pname = property[0].strip() pval_str = property[1].strip() # Make sure this is a match hp = Var.get_prop(pname) if hp is not None: pval = hp.valid_value(pval_str) else: raise ParseSyntaxError("variable property name", token=pname, context=self._pobj) # End if if pval is None: raise ParseSyntaxError( "'{}' property value".format(pname), token=pval_str, context=self._pobj) # End if except ParseSyntaxError as p: raise p # If we get this far, we have a valid property. var_props[pname] = pval # End for # End if # End while if var_props is None: return None, curr_line else: try: newvar = Var(var_props, source=self) except CCPPError as ve: raise ParseSyntaxError(ve, context=self._pobj) return newvar, curr_line # End if def variable_list(self): "Return an ordered list of the header's variables" return self._variables.variable_list() def to_html(self, outdir, props): """Write html file for metadata table and return filename. Skip metadata headers without variables""" if not self._variables.variable_list(): return None # Write table header header = "<tr>" for prop in props: header += "<th>{}</th>".format(prop) header += "</tr>\n" # Write table contents, one row per variable contents = "" for var in self._variables.variable_list(): row = "<tr>" for prop in props: value = var.get_prop_value(prop) # Pretty-print for dimensions if prop == 'dimensions': value = '(' + ', '.join(value) + ')' elif value is None: value = "n/a" row += "<td>{}</td>".format(value) row += "</tr>\n" contents += row filename = os.path.join(outdir, self.title + '.html') with open(filename, "w") as f: f.writelines( self.__html_template__.format(title=self.title + ' argument table', header=header, contents=contents)) return filename def get_var(self, standard_name=None, intent=None): if standard_name is not None: var = self._variables.find_variable(standard_name) return var elif intent is not None: if intent not in self._var_intents: raise ParseInternalError( "Illegal intent type, '{}', in {}".format( intent, self.title), context=self._pobj) # End if return self._var_intents[intent] else: return None def prop_list(self, prop_name): "Return list of <prop_name> values for this scheme's arguments" return self._variables.prop_list(prop_name) def variable_start(self, line): """Return variable name if <line> is an interface metadata table header """ if line is None: match = None else: match = MetadataHeader.__var_start__.match(line) # End if if match is not None: name = match.group(1) if not MetadataHeader.is_scalar_reference(name): raise ParseSyntaxError("local variable name", token=name, context=self._pobj) # End if else: name = None # End if return name def __repr__(self): base = super(MetadataHeader, self).__repr__() pind = base.find(' object ') if pind >= 0: pre = base[0:pind] else: pre = '<MetadataHeader' # End if bind = base.find('at 0x') if bind >= 0: post = base[bind:] else: post = '>' # End if return '{} {} / {} {}'.format(pre, self.module, self.title, post) def __del__(self): try: del self._variables super(MetadataHeader, self).__del__() except Exception as e: pass # Python does not guarantee much about __del__ conditions # End try @property def title(self): 'Return the name of the metadata arg_table' return self._table_title @property def module(self): 'Return the module name for this header (if it exists)' return self._module_name @property def header_type(self): 'Return the type of structure this header documents' return self._header_type @classmethod def is_blank(cls, line): "Return True iff <line> is a valid config format blank or comment line" return (len(line) == 0) or (cls.__blank_line__.match(line) is not None) @classmethod def table_start(cls, line): """Return variable name if <line> is an interface metadata table header """ if (line is None) or cls.is_blank(line): match = None else: match = MetadataHeader.__header_start__.match(line) # End if return match is not None @classmethod def is_scalar_reference(cls, test_val): return check_fortran_ref(test_val) is not None @classmethod def parse_metadata_file(cls, filename): "Parse <filename> and return list of parsed metadata headers" # Read all lines of the file at once mheaders = list() with open(filename, 'r') as file: fin_lines = file.readlines() for index in range(len(fin_lines)): fin_lines[index] = fin_lines[index].rstrip('\n') # End for # End with # Look for a header start parse_obj = ParseObject(filename, fin_lines) curr_line, curr_line_num = parse_obj.curr_line() while curr_line is not None: if MetadataHeader.table_start(curr_line): mheaders.append(MetadataHeader(parse_obj)) curr_line, curr_line_num = parse_obj.curr_line() else: curr_line, curr_line_num = parse_obj.next_line() # End if # End while return mheaders
def parse_scheme_metadata(statements, pobj, spec_name, table_name, logger): "Parse dummy argument information from a subroutine" psrc = None mheader = None var_dict = None scheme_name = None # Find the subroutine line, should be first executable statement inpreamble = False insub = True if logger is not None: ctx = context_string(pobj, nodir=True) msg = "Parsing specification of {}{}" logger.debug(msg.format(table_name, ctx)) # End if ctx = context_string(pobj) # Save initial context with directory vdict = None # Initialized when we parse the subroutine arguments while insub and (statements is not None): while statements: statement = statements.pop(0) smatch = _SUBROUTINE_RE.match(statement) esmatch = _END_SUBROUTINE_RE.match(statement) pmatch = _ENDMODULE_RE.match(statement) asmatch = _ARG_TABLE_START_RE.match(statement) if asmatch is not None: # We have run off the end of something, hope that is okay # Put this statement back for the caller to deal with statements.insert(0, statement) insub = False break # End if if pmatch is not None: # We have run off the end of the module, hope that is okay pobj.leave_region('MODULE', region_name=spec_name) insub = False break # End if if smatch is not None: scheme_name = smatch.group(1) inpreamble = scheme_name.lower() == table_name.lower() if inpreamble: if smatch.group(2) is not None: smstr = smatch.group(2).strip() if len(smstr) > 0: smlist = smstr.strip().split(',') else: smlist = list() # End if scheme_args = [x.strip().lower() for x in smlist] else: scheme_args = list() # End if # Create a dict template with all the scheme's arguments # in the correct order vdict = OrderedDict() for arg in scheme_args: if len(arg) == 0: errmsg = 'Empty argument{}' raise ParseInternalError(errmsg.format(pobj)) # End if if arg in vdict: errmsg = 'Duplicate dummy argument, {}' raise ParseSyntaxError(errmsg.format(arg), context=pobj) # End if vdict[arg] = None # End for psrc = ParseSource(scheme_name, 'scheme', pobj) # End if elif inpreamble: # Process a preamble statement (use or argument declaration) if esmatch is not None: inpreamble = False insub = False elif ((not is_comment_statement(statement)) and (not parse_use_statement(statement, logger)) and is_dummy_argument_statement(statement)): dvars = parse_fortran_var_decl(statement, psrc, logger=logger) for var in dvars: lname = var.get_prop_value('local_name').lower() if lname in vdict: if vdict[lname] is not None: emsg = "Error: duplicate dummy argument, {}" raise ParseSyntaxError(emsg.format(lname), context=pobj) # End if vdict[lname] = var else: raise ParseSyntaxError('dummy argument', token=lname, context=pobj) # End if # End for # End if # End if # End while if insub and (len(statements) == 0): statements = read_statements(pobj) # End if # End while # Check for missing declarations missing = list() if vdict is None: errmsg = 'Subroutine, {}, not found{}' raise CCPPError(errmsg.format(scheme_name, ctx)) # End if for lname in vdict.keys(): if vdict[lname] is None: missing.append(lname) # End if # End for if len(missing) > 0: errmsg = 'Missing local_variables, {} in {}' raise CCPPError(errmsg.format(missing, scheme_name)) # End if var_dict = VarDictionary(scheme_name, variables=vdict) if (scheme_name is not None) and (var_dict is not None): mheader = MetadataTable(table_name_in=scheme_name, table_type_in='scheme', module=spec_name, var_dict=var_dict, logger=logger) # End if return statements, mheader
def parse_preamble_data(statements, pobj, spec_name, endmatch, logger): """Parse module variables or DDT definitions from a module preamble or parse program variables from the beginning of a program. """ inspec = True mheaders = list() var_dict = VarDictionary(spec_name) psrc = ParseSource(spec_name, 'MODULE', pobj) active_table = None if logger is not None: ctx = context_string(pobj, nodir=True) msg = "Parsing preamble variables of {}{}" logger.debug(msg.format(spec_name, ctx)) # End if while inspec and (statements is not None): while len(statements) > 0: statement = statements.pop(0) # End program or module pmatch = endmatch.match(statement) asmatch = _ARG_TABLE_START_RE.match(statement) type_def = fortran_type_definition(statement) if asmatch is not None: active_table = asmatch.group(1) elif (pmatch is not None) or is_contains_statement(statement, inspec): # We are done with the specification inspec = False # Put statement back so caller knows where we are statements.insert(0, statement) # Add the header (even if we found no variables) mheader = MetadataTable(table_name_in=spec_name, table_type_in='module', module=spec_name, var_dict=var_dict, logger=logger) mheaders.append(mheader) if logger is not None: ctx = context_string(pobj, nodir=True) msg = 'Adding header {}{}' logger.debug(msg.format(mheader.table_name, ctx)) break elif ((type_def is not None) and (type_def[0].lower() == active_table.lower())): # Put statement back so caller knows where we are statements.insert(0, statement) statements, ddt = parse_type_def(statements, type_def, spec_name, pobj, logger) if ddt is None: ctx = context_string(pobj, nodir=True) msg = "No DDT found at '{}'{}" raise CCPPError(msg.format(statement, ctx)) # End if mheaders.append(ddt) if logger is not None: ctx = context_string(pobj, nodir=True) msg = 'Adding DDT {}{}' logger.debug(msg.format(ddt.table_name, ctx)) # End if active_table = None elif active_table is not None: # We should have a variable definition to add if ((not is_comment_statement(statement)) and (not parse_use_statement(statement, logger)) and (active_table.lower() == spec_name.lower())): dvars = parse_fortran_var_decl(statement, psrc, logger=logger) for var in dvars: var_dict.add_variable(var) # End for # End if # End if (else we are not in an active table so just skip) # End while if inspec and (len(statements) == 0): statements = read_statements(pobj) # End if # End while return statements, mheaders
'dimensions':'()'}, _API_SOURCE, _API_DUMMY_RUN_ENV) _SUITE_PART_VAR = Var({'local_name':'suite_part', 'standard_name':'suite_part', 'intent':'in', 'type':'character', 'kind':'len=*', 'units':'', 'protected':'True', 'dimensions':'()'}, _API_SOURCE, _API_DUMMY_RUN_ENV) ############################################################################### # Used for creating blank dictionary _MVAR_DUMMY_RUN_ENV = CCPPFrameworkEnv(None, ndict={'host_files':'', 'scheme_files':'', 'suites':''}) # Used to prevent loop substitution lookups _BLANK_DICT = VarDictionary(_API_SRC_NAME, _MVAR_DUMMY_RUN_ENV) ############################################################################### def suite_part_list(suite, stage): ############################################################################### """Return a list of all the suite parts for this stage""" run_stage = stage == 'run' if run_stage: spart_list = list() for spart in suite.groups: if suite.is_run_group(spart): spart_list.append(spart) # End if # End for else: spart_list = [suite.phase_group(stage)]
def __init_from_file__(self, parse_object, property_table, logger): # Read the table preamble, assume the caller already figured out # the first line of the header using the table_start method. curr_line, curr_line_num = self._pobj.next_line() self._table_title = None self._header_type = None self._module_name = None self._dependencies = [] relative_path_local = '' self._property_table = property_table while (curr_line is not None) and (not self.variable_start(curr_line)) and ( not MetadataHeader.table_start(curr_line)): for property in self.parse_config_line(curr_line): # Manually parse name, type, and module properties key = property[0].strip().lower() value = property[1].strip() if key == 'name': self._table_title = value elif key == 'type': if value not in ['module', 'scheme', 'ddt']: raise ParseSyntaxError("metadata table type", token=value, context=self._pobj) # End if self._header_type = value elif key == 'module': if value == "None": raise ParseSyntaxError("metadata table, no module", context=self._pobj) else: self._module_name = value # End if elif key == 'dependencies': if not (value == "None" or value == ""): # Remove trailing comma, remove white spaces from each list element self._dependencies += [ v.strip() for v in value.rstrip(",").split(",") ] elif key == 'relative_path': relative_path_local = value.strip() else: raise ParseSyntaxError("metadata table start property", token=value, context=self._pobj) # End if # End for curr_line, curr_line_num = self._pobj.next_line() # End while if self.title is None: raise ParseSyntaxError("metadata header start, no table name", token=curr_line, context=self._pobj) elif self.header_type is None: raise ParseSyntaxError("metadata header start, no table type", token=curr_line, context=self._pobj) elif self.header_type == "ddt": register_fortran_ddt_name(self.title) # End if # Add relative path to dependencies if self.dependencies and relative_path_local: self._dependencies = [ os.path.join(relative_path_local, v) for v in self.dependencies ] # Initialize our ParseSource parent super(MetadataHeader, self).__init__(self.title, self.header_type, self._pobj) # Read the variables valid_lines = True self._variables = VarDictionary(self.title, logger=logger) while valid_lines: newvar, curr_line = self.parse_variable(curr_line) valid_lines = newvar is not None if valid_lines: self._variables.add_variable(newvar) # Check to see if we hit the end of the table valid_lines = not MetadataHeader.table_start(curr_line)