def parse_len_select(self, lenselect, context, len_optional=True): """Parse a character type length_selector""" largs = [x.strip() for x in lenselect.split('=')] if len(largs) > 2: raise ParseSyntaxError("length_selector", token=lenselect, context=context) # end if if (not len_optional) and ((len(largs) != 2) or (largs[0].lower() != 'len')): raise ParseSyntaxError("length_selector when len= is required", token=lenselect, context=context) # end if if len(largs) == 2: if largs[0].lower() != 'len': raise ParseSyntaxError("length_selector", token=lenselect, context=context) # end if return self.parse_len_token(largs[1], context) elif len_optional: return self.parse_len_token(largs[0], context) else: raise ParseSyntaxError("length_selector when len= is required", token=lenselect, context=context)
def __init__(self, typestr_in=None, kind_in=None, line_in=None, context=None): if context is None: self._context = ParseContext() else: self._context = ParseContext(context=context) # We have to distinguish which type of initialization we have if typestr_in is not None: if line_in is not None: raise ParseInternalError( "typestr_in and line_in cannot both be used in a single call", self._context) # End if self._typestr = typestr_in self.default_kind = kind_in is None if kind_in is None: self._kind = None elif kind_in[0] == '(': # Parse an explicit kind declaration self._kind = self.parse_kind_selector(kind_in) else: # The kind has already been parsed for us (e.g., by character) self._kind = kind_in elif kind_in is not None: raise ParseInternalError( "kind_in cannot be passed without typestr_in", self._context) elif line_in is not None: match = Ftype.type_match(line_in) self._match_len = len(match.group(0)) if match is None: raise ParseSyntaxError("type declaration", token=line_in, context=self._context) elif check_fortran_intrinsic(match.group(1)): self._typestr = match.group(1) if match.group(2) is not None: # Parse kind section self._kind = self.parse_kind_selector( match.group(2).strip()) else: self._kind = None # End if self.default_kind = self._kind is None else: raise ParseSyntaxError("type declaration", token=line_in, context=self._context) else: raise ParseInternalError( "At least one of typestr_in or line must be passed", self._context)
def parse_program(pobj, statements, logger=None): # The first statement should be a program statement, grab the name pmatch = program_re.match(statements[0]) if pmatch is None: raise ParseSyntaxError('PROGRAM statement', statements[0]) # End if prog_name = pmatch.group(1) pobj.enter_region('PROGRAM', region_name=prog_name, nested_ok=False) # After the program name is the specification part statements, mheaders = parse_specification(pobj, statements[1:], prog_name=prog_name, logger=logger) # We really cannot have tables inside a program's executable section # Just read until end statements = read_statements(pobj, statements) inprogram = True while inprogram and (statements is not None): while len(statements) > 0: statement = statements.pop(0) # End program pmatch = endprogram_re.match(statements) if pmatch is not None: prog_name = pmatch.group(1) pobj.leave_region('PROGRAM', region_name=prog_name) inprogram = False # End if # End while if inprogram and (len(statements) == 0): statements = read_statements(pobj) # End if # End while return statements, mheaders
def reassemble_parens(cls, propstr, errstr, context, splitstr=','): """Return list of <propstr> split by top-level instances of <splitstr>. Occurrences of <splitstr> in character contexts or in parentheses are ignored. >>> Ftype.reassemble_parens("a(b, c),d,e()", 'spec', ParseContext()) ['a(b, c)', 'd', 'e()'] >>> Ftype.reassemble_parens("dimension(size(Grid%xlon,1),NSPC1), intent(in)", 'spec', ParseContext()) ['dimension(size(Grid%xlon,1),NSPC1)', 'intent(in)'] """ var_list = list() proplist = propstr.split(splitstr) while len(proplist) > 0: var = proplist.pop(0) while var.count('(') != var.count(')'): if len(proplist) == 0: raise ParseSyntaxError(errstr, token=propstr, context=context) # end if var = var + ',' + proplist.pop(0) # end while var = var.strip() if len(var) > 0: var_list.append(var) # end if # end while return var_list
def parse_attr_specs(cls, propstring, context): """Return a list of variable properties""" properties = list() # Remove leading comma propstring = propstring.strip() if propstring and (propstring[0] == ','): propstring = propstring[1:].lstrip() # end if proplist = cls.reassemble_parens(propstring, 'attr_spec', context) for prop in proplist: prop = prop.strip().lower() if '(' in prop: # Strip out value from dimensions, bind, or intent pval = prop[0:prop.index('(')].strip() else: pval = prop # end if if pval not in cls.__attr_spec: raise ParseSyntaxError('attr_spec', token=prop, context=context) # end if properties.append(prop) # end for return properties
def parse_len_token(self, token, context): """Check to make sure token is a valid length identifier""" match = Ftype_character.len_token_re.match(token) if match is not None: return match.group(1) else: raise ParseSyntaxError("length type-param-value", token=token, context=context)
def __init__(self, line, context): """Initialize an extended type from a declaration line""" match = Ftype_type_decl.type_match(line) if match is None: raise ParseSyntaxError("type declaration", token=line, context=context) else: self._match_len = len(match.group(0)) self._class = match.group(1) self._typestr = match.group(2) self._kind = self.typestr
def parse_kind_selector(self, kind_selector, context=None): """Find and return the 'kind' value from <kind_selector> '(foo)' and '(kind=foo)' both return 'foo'""" if context is None: if hasattr(self, 'context'): context = self.__context else: context = ParseContext() # end if kind = None if (kind_selector[0] == '(') and (kind_selector[-1] == ')'): args = kind_selector[1:-1].split('=') else: args = kind_selector.split('=') # end if if (len(args) > 2) or (len(args) < 1): raise ParseSyntaxError("kind_selector", token=kind_selector, context=context) # end if if len(args) == 1: kind = args[0].strip() elif args[0].strip().lower() != 'kind': # We have two args, the first better be kind raise ParseSyntaxError("kind_selector", token=kind_selector, context=context) # end if if kind is None: # We have two args and the second is our kind string kind = args[1].strip() # end if # One last check for missing right paren match = Ftype.__kind_re.search(kind) if match is not None: if match.group(2) is not None: if match.group(2) != match.group(4): raise ParseSyntaxError("kind_selector", token=kind_selector, context=context) # end if if (match.group(1) is None) and (match.group(5) is not None): raise ParseSyntaxError("kind_selector", token=kind_selector, context=context) # end if if (match.group(1) is not None) and (match.group(5) is None): raise ParseSyntaxError("kind_selector", token=kind_selector, context=context) # end if else: pass elif kind[0:4].lower() == "kind": # Got 'something' == 'kind'?? raise ParseSyntaxError("kind_selector", token=kind_selector, context=context) # end if return kind
def __init__(self, line, context): """Initialize an extended type from a declaration line""" match = FtypeTypeDecl.type_match(line) if match is None: raise ParseSyntaxError("type declaration", token=line, context=context) # end if super(FtypeTypeDecl, self).__init__(typestr_in=match.group(2), kind_in=match.group(2), match_len_in=len(match.group(0)), context=context) self.__class = match.group(1)
def add_variable(self, newvar, exists_ok=False): """Add a variable if it does not conflict with existing entries""" standard_name = newvar.get_prop_value('standard_name') if (standard_name in self) and (not exists_ok): # We already have a matching variable, error! if self._logger is not None: self._logger.error("Attempt to add duplicate variable, {} from {}".format(standard_name, newvar.source.name)) # End if raise ParseSyntaxError("Duplicate standard name in {}".format(self.name), token=standard_name, context=newvar._context) # End if cvar = self.find_variable(standard_name) if (cvar is not None) and (not cvar.compatible(newvar, self._logger)): if self._logger is not None: self._logger.error("Attempt to add incompatible variable, {} from {}".format(standard_name, newvar.source.name)) # End if errstr = "standard name, incompatible with {}" raise ParseSyntaxError(errstr.format(cvar.context), token=standard_name, context=newvar.source.context) # End if # If we make it to here without an exception, add the variable self[standard_name] = newvar
def parse_module(pobj, statements, logger=None): # The first statement should be a module statement, grab the name pmatch = module_re.match(statements[0]) if pmatch is None: raise ParseSyntaxError('MODULE statement', statements[0]) # End if mod_name = pmatch.group(1) pobj.enter_region('MODULE', region_name=mod_name, nested_ok=False) # After the module name is the specification part statements, mheaders = parse_specification(pobj, statements[1:], mod_name=mod_name, logger=logger) # Look for metadata tables statements = read_statements(pobj, statements) inmodule = pobj.in_region('MODULE', region_name=mod_name) active_table = None while inmodule and (statements is not None): while len(statements) > 0: statement = statements.pop(0) # End module pmatch = endmodule_re.match(statement) asmatch = arg_table_start_re.match(statement) if asmatch is not None: active_table = asmatch.group(1) elif pmatch is not None: mod_name = pmatch.group(1) pobj.leave_region('MODULE', region_name=mod_name) inmodule = False break elif active_table is not None: statements, mheader = parse_scheme_metadata( statements, pobj, mod_name, active_table, logger) if mheader is not None: mheaders.append(mheader) # End if active_table = None inmodule = pobj.in_region('MODULE', region_name=mod_name) break # End if # End while if inmodule and (statements is not None) and (len(statements) == 0): statements = read_statements(pobj) # End if # End while return statements, mheaders
def __init__(self, line, context): """Initialize a character type from a declaration line""" clen = None kind = None # This will be interpreted as default kind match = Ftype_character.type_match(line) if match is None: raise ParseSyntaxError("character declaration", token=line, context=context) elif len(match.groups()) == 3: self._match_len = len(match.group(0)) # We have an old style character declaration if match.group(2) != '*': raise ParseSyntaxError("character declaration", token=line, context=context) elif Ftype_character.oldchartrail_re.match(line.strip()[len(match.group(0)):]) is None: raise ParseSyntaxError("character declaration", token=line, context=context) else: clen = match.group(3) # End if elif match.group(2) is not None: self._match_len = len(match.group(0)) # Parse attributes (strip off parentheses) attrs = [ x.strip() for x in match.group(2)[1:-1].split(',') ] if len(attrs) == 0: # Empty parentheses is not allowed raise ParseSyntaxError("char_selector", token=match.group(2), context=context) if len(attrs) > 2: # Too many attributes! raise ParseSyntaxError("char_selector", token=match.group(2), context=context) elif attrs[0][0:4].lower() == "kind": # The first arg is kind, try to parse it kind = self.parse_kind_selector(attrs[0], context=context) # If there is a second arg, it must be of form len=<length_selector> if len(attrs) == 2: clen = self.parse_len_select(attrs[1], context, len_optional=False) elif len(attrs) == 2: # We have both a len and a kind, len first clen = self.parse_len_select(attrs[0], context, len_optional=True) kind = self.parse_kind_selector(attrs[1], context) else: # We just a len argument clen = self.parse_len_select(attrs[0], context, len_optional=True) # End if else: self._match_len = len(match.group(0)) # We had better check the training characters if Ftype_character.chartrail_re.match(line.strip()[len(match.group(0)):]) is None: raise ParseSyntaxError("character declaration", token=line, context=context) # End if if clen is None: clen = 1 # End if self.lenstr = "{}".format(clen) super(Ftype_character, self).__init__(typestr_in=match.group(1), kind_in=kind, context=context)
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 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_program(pobj, statements, logger=None): """Parse a Fortran PROGRAM and return any leftover statements and metadata tables encountered in the PROGRAM.""" # The first statement should be a program statement, grab the name pmatch = _PROGRAM_RE.match(statements[0]) if pmatch is None: raise ParseSyntaxError('PROGRAM statement', statements[0]) # End if prog_name = pmatch.group(1) pobj.enter_region('PROGRAM', region_name=prog_name, nested_ok=False) if logger is not None: ctx = context_string(pobj, nodir=True) msg = "Parsing Fortran program, {}{}" logger.debug(msg.format(prog_name, ctx)) # End if # After the program name is the specification part statements, mtables = parse_specification(pobj, statements[1:], prog_name=prog_name, logger=logger) # We really cannot have tables inside a program's executable section # Just read until end statements = read_statements(pobj, statements) inprogram = True while inprogram and (statements is not None): while len(statements) > 0: statement = statements.pop(0) # End program pmatch = _ENDPROGRAM_RE.match(statement) if pmatch is not None: prog_name = pmatch.group(1) pobj.leave_region('PROGRAM', region_name=prog_name) inprogram = False # End if # End while if inprogram and (len(statements) == 0): statements = read_statements(pobj) # End if # End while return statements, mtables
def __init__(self, typestr_in=None, kind_in=None, match_len_in=None, line_in=None, context=None): """Initialize this FType object, either using <typestr_in> and <kind_in>, OR using line_in.""" if context is None: self.__context = ParseContext() else: self.__context = ParseContext(context=context) # end if # We have to distinguish which type of initialization we have self.__typestr = typestr_in if typestr_in is not None: if line_in is not None: emsg = "Cannot pass both typestr_in and line_in as arguments" raise ParseInternalError(emsg, self.__context) # end if self.__default_kind = kind_in is None if kind_in is None: self.__kind = None elif kind_in[0] == '(': # Parse an explicit kind declaration self.__kind = self.parse_kind_selector(kind_in) else: # The kind has already been parsed for us (e.g., by character) self.__kind = kind_in # end if if match_len_in is not None: self.__match_len = match_len_in else: self.__match_len = len(self.typestr) if kind_in is not None: self.__match_len += len(self.__kind) + 2 # end if # end if elif kind_in is not None: emsg = "kind_in cannot be passed without typestr_in" raise ParseInternalError(emsg, self.__context) elif line_in is not None: match = Ftype.type_match(line_in) if match is None: emsg = "type declaration" raise ParseSyntaxError(emsg, token=line_in, context=self.__context) # end if if match_len_in is not None: self.__match_len = match_len_in else: self.__match_len = len(match.group(0)) # end if if check_fortran_intrinsic(match.group(1)): self.__typestr = match.group(1) if match.group(2) is not None: # Parse kind section kmatch = match.group(2).strip() self.__kind = self.parse_kind_selector(kmatch) else: self.__kind = None # end if self.__default_kind = self.__kind is None else: raise ParseSyntaxError("type declaration", token=line_in, context=self.__context) # end if else: emsg = "At least one of typestr_in or line_in must be passed" raise ParseInternalError(emsg, self.__context)
def parse_fortran_var_decl(line, source, run_env): ######################################################################## """Parse a Fortran variable declaration line and return a list of Var objects representing the variables declared on <line>. >>> _VAR_ID_RE.match('foo') #doctest: +ELLIPSIS <re.Match object; span=(0, 3), match='foo'> >>> _VAR_ID_RE.match("foo()") >>> _VAR_ID_RE.match('foo').group(1) 'foo' >>> _VAR_ID_RE.match('foo').group(2) >>> _VAR_ID_RE.match("foo(bar)").group(1) 'foo' >>> _VAR_ID_RE.match("foo(bar)").group(2) '(bar)' >>> _VAR_ID_RE.match("foo(bar)").group(2) '(bar)' >>> _VAR_ID_RE.match("foo(bar, baz)").group(2) '(bar, baz)' >>> _VAR_ID_RE.match("foo(bar : baz)").group(2) '(bar : baz)' >>> _VAR_ID_RE.match("foo(bar:)").group(2) '(bar:)' >>> _VAR_ID_RE.match("foo(: baz)").group(2) '(: baz)' >>> _VAR_ID_RE.match("foo(:, :,:)").group(2) '(:, :,:)' >>> _VAR_ID_RE.match("foo(8)").group(2) '(8)' >>> _VAR_ID_RE.match("foo(::,a:b,a:,:b)").group(2) '(::,a:b,a:,:b)' >>> parse_fortran_var_decl("integer :: foo", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('local_name') 'foo' >>> parse_fortran_var_decl("integer :: foo = 0", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('local_name') 'foo' >>> parse_fortran_var_decl("integer :: foo", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('optional') >>> parse_fortran_var_decl("integer, optional :: foo", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('optional') 'True' >>> parse_fortran_var_decl("integer, dimension(:) :: foo", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('dimensions') '(:)' >>> parse_fortran_var_decl("integer, dimension(:) :: foo(bar)", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('dimensions') '(bar)' >>> parse_fortran_var_decl("integer, dimension(:) :: foo(:,:), baz", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('dimensions') '(:,:)' >>> parse_fortran_var_decl("integer, dimension(:) :: foo(:,:), baz", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[1].get_prop_value('dimensions') '(:)' >>> parse_fortran_var_decl("real (kind=kind_phys), pointer :: phii (:,:) => null() !< interface geopotential height", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('dimensions') '(:,:)' >>> parse_fortran_var_decl("real(kind=kind_phys), dimension(im, levs, ntrac), intent(in) :: qgrs", ParseSource('foo.F90', 'scheme', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('dimensions') '(im, levs, ntrac)' >>> parse_fortran_var_decl("character(len=*), intent(out) :: errmsg", ParseSource('foo.F90', 'scheme', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('local_name') 'errmsg' >>> parse_fortran_var_decl("character(len=512), intent(out) :: errmsg", ParseSource('foo.F90', 'scheme', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('kind') 'len=512' >>> parse_fortran_var_decl("real(kind_phys), intent(out) :: foo(8)", ParseSource('foo.F90', 'scheme', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('dimensions') '(8)' >>> parse_fortran_var_decl("real(kind_phys), intent(out) :: foo(8)", ParseSource('foo.F90', 'scheme', ParseContext()), _DUMMY_RUN_ENV)[0].get_dimensions() ['8'] >>> parse_fortran_var_decl("character(len=*), intent(out) :: errmsg", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('local_name') #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ParseSyntaxError: Invalid variable declaration, character(len=*), intent(out) :: errmsg, intent not allowed in module variable, in <standard input> ## NB: Expressions (including function calls) not currently supported here #>>> parse_fortran_var_decl("real(kind_phys), intent(out) :: foo(size(bar))", ParseSource('foo.F90', 'scheme', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('dimensions') #'(size(bar))' """ context = source.context sline = line.strip() # Strip comments first if '!' in sline: sline = sline[0:sline.index('!')].rstrip() # end if tobject = ftype_factory(sline, context) newvars = list() if tobject is not None: varprops = sline[tobject.type_len:].strip() def_dims = None # Default dimensions intent = None dimensions = None if '::' in varprops: elements = varprops.split('::') varlist = elements[1].strip() varprops = Ftype.parse_attr_specs(elements[0].strip(), context) for prop in varprops: if prop[0:6] == 'intent': if source.type != 'scheme': typ = source.type errmsg = 'Invalid variable declaration, {}, intent' errmsg = errmsg + ' not allowed in {} variable' if run_env.logger is not None: ctx = context_string(context) errmsg = "WARNING: " + errmsg + "{}" run_env.logger.warning( errmsg.format(sline, typ, ctx)) else: raise ParseSyntaxError(errmsg.format(sline, typ), context=context) # end if else: intent = prop[6:].strip()[1:-1].strip() # end if elif prop[0:9:] == 'dimension': dimensions = prop[9:].strip() # end if # end for else: # No attr_specs varlist = varprops varprops = list() # end if # Create Vars from these pieces # We may need to reassemble multi-dimensional specs var_list = Ftype.reassemble_parens(varlist, 'variable_list', context) for var in var_list: prop_dict = {} if '=' in var: # We do not care about initializers var = var[0:var.rindex('=')].rstrip() # end if # Scan <var> and gather variable pieces inchar = None # Character context var_len = len(var) ploc = var.find('(') if ploc < 0: varname = var.strip() dimspec = None else: varname = var[0:ploc].strip() begin, end = check_balanced_paren(var) if (begin < 0) or (end < 0): if run_env.logger is not None: ctx = context_string(context) errmsg = "WARNING: Invalid variable declaration, {}{}" run_env.logger.warning(errmsg.format(var, ctx)) else: raise ParseSyntaxError('variable declaration', token=var, context=context) # end if else: dimspec = var[begin:end + 1] # end if # end if prop_dict['local_name'] = varname prop_dict['standard_name'] = unique_standard_name() prop_dict['units'] = '' if isinstance(tobject, FtypeTypeDecl): prop_dict['ddt_type'] = tobject.typestr else: prop_dict['type'] = tobject.typestr # end if if tobject.kind() is not None: prop_dict['kind'] = tobject.kind() # end if if 'optional' in varprops: prop_dict['optional'] = 'True' # end if if 'allocatable' in varprops: prop_dict['allocatable'] = 'True' # end if if intent is not None: prop_dict['intent'] = intent # end if if dimspec is not None: prop_dict['dimensions'] = dimspec elif dimensions is not None: prop_dict['dimensions'] = dimensions else: prop_dict['dimensions'] = '()' # end if # XXgoldyXX: I am nervous about allowing invalid Var objects here # Also, this tends to cause an exception that ends up back here # which is not a good idea. var = FortranVar(prop_dict, source, run_env) newvars.append(var) # end for # No else (not a variable declaration) # end if return newvars
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_module(pobj, statements, logger=None): """Parse a Fortran MODULE and return any leftover statements and metadata tables encountered in the MODULE.""" # The first statement should be a module statement, grab the name pmatch = _MODULE_RE.match(statements[0]) if pmatch is None: raise ParseSyntaxError('MODULE statement', statements[0]) # End if mod_name = pmatch.group(1) pobj.enter_region('MODULE', region_name=mod_name, nested_ok=False) if logger is not None: ctx = context_string(pobj, nodir=True) msg = "Parsing Fortran module, {}{}" logger.debug(msg.format(mod_name, ctx)) # End if # After the module name is the specification part statements, mtables = parse_specification(pobj, statements[1:], mod_name=mod_name, logger=logger) # Look for metadata tables statements = read_statements(pobj, statements) inmodule = pobj.in_region('MODULE', region_name=mod_name) active_table = None while inmodule and (statements is not None): while statements: statement = statements.pop(0) # End module pmatch = _ENDMODULE_RE.match(statement) asmatch = _ARG_TABLE_START_RE.match(statement) if asmatch is not None: active_table = asmatch.group(1) elif pmatch is not None: mod_name = pmatch.group(1) pobj.leave_region('MODULE', region_name=mod_name) inmodule = False break elif active_table is not None: statements, mheader = parse_scheme_metadata(statements, pobj, mod_name, active_table, logger) if mheader is not None: title = mheader.table_name if title in mtables: errmsg = duplicate_header(mtables[title], mheader) raise CCPPError(errmsg) # end if if logger is not None: mtype = mheader.table_type ctx = mheader.start_context() msg = "Adding metadata from {}, {}{}" logger.debug(msg.format(mtype, title, ctx)) # End if mtables.append(mheader) # End if active_table = None inmodule = pobj.in_region('MODULE', region_name=mod_name) break # End if # End while if inmodule and (statements is not None) and (len(statements) == 0): statements = read_statements(pobj) # End if # End while return statements, mtables
def parse_fortran_var_decl(line, source, logger=None): ######################################################################## """Parse a Fortran variable declaration line and return a list of Var objects representing the variables declared on <line>. >>> _var_id_re_.match('foo') #doctest: +ELLIPSIS <_sre.SRE_Match object at 0x...> >>> _var_id_re_.match("foo()") >>> _var_id_re_.match('foo').group(1) 'foo' >>> _var_id_re_.match('foo').group(2) >>> _var_id_re_.match("foo(bar)").group(1) 'foo' >>> _var_id_re_.match("foo(bar)").group(2) '(bar)' >>> _var_id_re_.match("foo(bar)").group(2) '(bar)' >>> _var_id_re_.match("foo(bar, baz)").group(2) '(bar, baz)' >>> _var_id_re_.match("foo(bar : baz)").group(2) '(bar : baz)' >>> _var_id_re_.match("foo(bar:)").group(2) '(bar:)' >>> _var_id_re_.match("foo(: baz)").group(2) '(: baz)' >>> _var_id_re_.match("foo(:, :,:)").group(2) '(:, :,:)' >>> _var_id_re_.match("foo(8)").group(2) '(8)' >>> _var_id_re_.match("foo(::,a:b,a:,:b)").group(2) '(::,a:b,a:,:b)' >>> parse_fortran_var_decl("integer :: foo", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('local_name') 'foo' >>> parse_fortran_var_decl("integer :: foo = 0", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('local_name') 'foo' >>> parse_fortran_var_decl("integer :: foo", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('optional') False >>> parse_fortran_var_decl("integer, optional :: foo", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('optional') 'True' >>> parse_fortran_var_decl("integer, dimension(:) :: foo", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('dimensions') '(:)' >>> parse_fortran_var_decl("integer, dimension(:) :: foo(bar)", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('dimensions') '(bar)' >>> parse_fortran_var_decl("integer, dimension(:) :: foo(:,:), baz", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('dimensions') '(:,:)' >>> parse_fortran_var_decl("integer, dimension(:) :: foo(:,:), baz", ParseSource('foo.F90', 'MODULE', ParseContext()))[1].get_prop_value('dimensions') '(:)' >>> parse_fortran_var_decl("real (kind=kind_phys), pointer :: phii (:,:) => null() !< interface geopotential height", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('dimensions') '(:,:)' >>> parse_fortran_var_decl("real(kind=kind_phys), dimension(im, levs, ntrac), intent(in) :: qgrs", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('dimensions') '(im, levs, ntrac)' >>> parse_fortran_var_decl("character(len=*), intent(out) :: errmsg", ParseSource('foo.F90', 'MODULE', ParseContext()))[0].get_prop_value('local_name') 'errmsg' """ context = source.context sline = line.strip() # Strip comments first if '!' in sline: sline = sline[0:sline.index('!')].rstrip() # End if tobject = Ftype_factory(sline, context) newvars = list() if tobject is not None: varprops = sline[tobject.type_len:].strip() def_dims = None # Default dimensions intent = None dimensions = None if '::' in varprops: elements = varprops.split('::') varlist = elements[1].strip() varprops = Ftype.parse_attr_specs(elements[0].strip(), context) for prop in varprops: if prop[0:6] == 'intent': intent = prop[6:].strip()[1:-1].strip() elif prop[0:9:] == 'dimension': dimensions = prop[9:].strip() # End if # End for else: # No attr_specs varlist = varprops varprops = list() # End if # Create Vars from these pieces # We may need to reassemble multi-dimensional specs vars = Ftype.reassemble_parens(varlist, 'variable_list', context) for var in vars: prop_dict = {} if '=' in var: # We do not care about initializers var = var[0:var.rindex('=')].rstrip() # End if # Scan <var> and gather variable pieces inchar = None # Character context var_len = len(var) ploc = var.find('(') if ploc < 0: varname = var.strip() dimspec = None else: varname = var[0:ploc].strip() begin, end = check_balanced_paren(var) if (begin < 0) or (end < 0): if logger is not None: ctx = context_string(context) logger.warning( "WARNING: Invalid variable declaration, {}{}". format(var, ctx)) else: raise ParseSyntaxError('variable declaration', token=var, context=context) # End if else: dimspec = var[begin:end + 1] # End if # End if prop_dict['local_name'] = varname prop_dict['standard_name'] = Ftype.unique_standard_name() prop_dict['units'] = '' prop_dict['type'] = tobject.typestr if tobject.kind is not None: prop_dict['kind'] = tobject.kind # End if if 'optional' in varprops: prop_dict['optional'] = 'True' # End if if 'allocatable' in varprops: prop_dict['allocatable'] = 'True' # End if if intent is not None: prop_dict['intent'] = intent # End if if dimspec is not None: prop_dict['dimensions'] = dimspec elif dimensions is not None: prop_dict['dimensions'] = dimensions else: prop_dict['dimensions'] = '()' # End if # XXgoldyXX: I am nervous about allowing invalid Var objects here newvars.append( Var(prop_dict, source, invalid_ok=(logger is not None), logger=logger)) # End for # No else (not a variable declaration) # End if return newvars
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)
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
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 scan_free_line(line, in_continue, in_single_char, in_double_char, context): """Scan a Fortran line for continue indicators, continued quotes, and comments Return continue_in_col, continue_out_col, in_single_char, in_double_char, comment_col >>> scan_free_line("! Comment line", False, False, False, ParseContext()) (-1, -1, False, False, 0) >>> scan_free_line("!! ", False, False, False, ParseContext()) (-1, -1, False, False, 0) >>> scan_free_line("int :: index", False, False, False, ParseContext()) (-1, -1, False, False, -1) >>> scan_free_line("int :: inde& ! oops", False, False, False, ParseContext()) (-1, 11, False, False, 13) >>> scan_free_line("int :: inde&", False, False, False, ParseContext()) (-1, 11, False, False, -1) >>> scan_free_line("character(len=*), parameter :: foo = 'This line & not continued'", False, False, False, ParseContext()) (-1, -1, False, False, -1) >>> scan_free_line("character(len=*), parameter :: foo = 'This is continue line& ", False, False, False, ParseContext()) (-1, 59, True, False, -1) >>> scan_free_line('character(len=*), parameter :: foo = "This line & not continued"', False, False, False, ParseContext()) (-1, -1, False, False, -1) >>> scan_free_line('character(len=*), parameter :: foo = "This is continue line& ', False, False, False, ParseContext()) (-1, 59, False, True, -1) >>> scan_free_line(' & line continued"', True, False, True, ParseContext()) (2, -1, False, False, -1) >>> scan_free_line(' & line continued"', True, True, False, ParseContext()) #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ParseSyntaxError: Cannot end non-continued line in a character context, in <standard input> >>> scan_free_line(" & line continued'", True, False, True, ParseContext()) #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ParseSyntaxError: Cannot end non-continued line in a character context, in <standard input> >>> scan_free_line("int :: inde&", False, True, False, ParseContext()) Traceback (most recent call last): ParseSyntaxError: Cannot start line in character context if not a continued line, in <standard input> >>> scan_free_line("int :: inde&", True, True, True, ParseContext()) Traceback (most recent call last): ParseSyntaxError: Cannot be both in an apostrophe character context and a quote character context, in <standard input> """ # A few sanity checks if (in_single_char or in_double_char) and (not in_continue): raise ParseSyntaxError( "Cannot start line in character context if not a continued line", context=context) # Endif if in_single_char and in_double_char: raise ParseSyntaxError( "Cannot be both in an apostrophe character context and a quote character context", context=context) continue_in_col = -1 continue_out_col = -1 comment_col = -1 index = 0 last_ind = len(line.rstrip()) - 1 # Is first non-blank character a continue character? if line.lstrip()[0] == '&': if not in_continue: raise ParseSyntaxError( "Cannot begin line with continue character (&), not on continued line", context=context) else: continue_in_col = line.find('&') index = continue_in_col + 1 # End if # Process rest of line while index <= last_ind: blank = blank_re.match(line[index:]) if blank is not None: index = index + len(blank.group(0)) - 1 # +1 at end of loop elif in_single_char: if line[index:min(index + 1, last_ind)] == "''": # Embedded single quote index = index + 1 # +1 and end of loop elif line[index] == "'": in_single_char = False elif (line[index] == '&'): if index == last_ind: continue_out_col = index # End if # End if (just ignore any other character) elif in_double_char: if line[index:min(index + 1, last_ind)] == '""': # Embedded double quote index = index + 1 # +1 and end of loop elif line[index] == '"': in_double_char = False elif line[index] == '&': if index == last_ind: continue_out_col = index # End if # End if (just ignore any other character) elif line[index] == "'": # If we got here, we are not in a character context, start single in_single_char = True elif line[index] == '"': # If we got here, we are not in a character context, start double in_double_char = True elif line[index] == '!': # If we got here, we are not in a character context, done with line comment_col = index index = last_ind elif line[index] == '&': # If we got here, we are not in a character context, note continue # First make sure this is a valid continue match = continue_re.match(line[index:]) if match is not None: continue_out_col = index else: raise ParseSyntaxError( "Invalid continue, ampersand not followed by comment character", context=context) # End if # End if index = index + 1 # End while # A final check if (in_single_char or in_double_char) and (continue_out_col < 0): raise ParseSyntaxError( "Cannot end non-continued line in a character context", context=context) return continue_in_col, continue_out_col, in_single_char, in_double_char, comment_col
def __init__(self, prop_dict, source, invalid_ok=False, logger=None): """NB: invalid_ok=True is dangerous because it allows creation of a Var object with invalid properties. In order to prevent silent failures, invalid_ok requires a logger in order to take effect.""" if source.type is 'SCHEME': required_props = Var.__required_var_props master_propdict = Var.__var_propdict else: required_props = Var.__required_spec_props master_propdict = Var.__spec_propdict # End if self._source = source # Grab a frozen copy of the context self._context = ParseContext(context=source.context) # First, check the input if 'ddt_type' in prop_dict: # Special case to bypass normal type rules if 'type' not in prop_dict: prop_dict['type'] = prop_dict['ddt_type'] # End if if 'units' not in prop_dict: prop_dict['units'] = "" # End if prop_dict['kind'] = prop_dict['ddt_type'] del prop_dict['ddt_type'] # End if for key in prop_dict: if Var.get_prop(key) is None: raise ParseSyntaxError("Invalid metadata variable property, '{}'".format(key), context=self.context) # End if # End for # Make sure required properties are present for propname in required_props: if propname not in prop_dict: if invalid_ok and (logger is not None): ctx = context_string(self.context) logger.warning("Required property, '{}', missing{}".format(propname, ctx)) else: raise ParseSyntaxError("Required property, '{}', missing".format(propname), context=self.context) # End if # End if # End for # Check for any mismatch if ('constant' in prop_dict) and ('intent' in prop_dict): if prop_dict['intent'].lower() != 'in': if invalid_ok and (logger is not None): ctx = context_string(self.context) logger.warning("{} is marked constant but is intent {}{}".format(prop_dict['local_name'], prop_dict['intent'], ctx)) else: raise ParseSyntaxError("{} is marked constant but is intent {}".format(prop_dict['local_name'], prop_dict['intent']), context=self.context) # End if # End if # End if # Steal dict from caller self._prop_dict = prop_dict # Fill in default values for missing properties for propname in master_propdict: if (propname not in prop_dict) and master_propdict[propname].optional: self._prop_dict[propname] = master_propdict[propname].get_default_val(self._prop_dict, context=self.context) # End if # End for # Make sure all the variable values are valid try: for prop in self._prop_dict.keys(): check = Var.get_prop(prop).valid_value(self._prop_dict[prop], error=True) # End for except CCPPError as cp: if invalid_ok and (logger is not None): ctx = context_string(self.context) logger.warning("{}: {}{}".format(self._prop_dict['local_name'], cp, ctx)) else: raise ParseSyntaxError("{}: {}".format(self._prop_dict['local_name'], cp), context=self.context)
def scan_fixed_line(line, in_single_char, in_double_char, context): """Scan a fixed-format FORTRAN line for continue indicators, continued quotes, and comments Return continue_in_col, in_single_char, in_double_char, comment_col >>> scan_fixed_line(' & line continued', False, False, ParseContext()) (5, False, False, -1) >>> scan_fixed_line(' & line continued"', False, True, ParseContext()) (5, False, False, -1) >>> scan_fixed_line(' * line continued', False, False, ParseContext()) (5, False, False, -1) >>> scan_fixed_line(' 1 line continued', False, False, ParseContext()) (5, False, False, -1) >>> scan_fixed_line('C comment line', False, False, ParseContext()) (-1, False, False, 0) >>> scan_fixed_line('* comment line', False, False, ParseContext()) (-1, False, False, 0) >>> scan_fixed_line('! comment line', False, False, ParseContext()) (-1, False, False, 0) >>> scan_fixed_line(' ! comment line', False, False, ParseContext()) (-1, False, False, 1) >>> scan_fixed_line(' ! comment line', False, False, ParseContext()) (-1, False, False, 4) >>> scan_fixed_line(' ! not comment line', False, False, ParseContext()) (5, False, False, -1) >>> scan_fixed_line('!...................................', False, False, ParseContext()) (-1, False, False, 0) >>> scan_fixed_line('123 x = x + 1', False, False, ParseContext()) (-1, False, False, -1) """ # Check if comment or continue statement cmatch = fixed_comment_re.match(line) is_comment = cmatch is not None is_continue = fixed_continue_re.match(line) is not None # A few sanity checks if (in_single_char or in_double_char) and (not is_continue): raise ParseSyntaxError( "Cannot start line in character context if not a continued line", context=context) # Endif if in_single_char and in_double_char: raise ParseSyntaxError( "Cannot be both in an apostrophe character context and a quote character context", context=context) if is_continue: continue_in_col = 5 comment_col = -1 index = 6 elif is_comment: comment_col = len(cmatch.group(1)) - 1 continue_in_col = -1 index = len(line.rstrip()) else: continue_in_col = -1 comment_col = -1 index = 0 # End if last_ind = len(line.rstrip()) - 1 # Process the line while index <= last_ind: blank = blank_re.match(line[index:]) if blank is not None: index = index + len(blank.group(0)) - 1 # +1 at end of loop elif in_single_char: if line[index:min(index + 1, last_ind)] == "''": # Embedded single quote index = index + 1 # +1 and end of loop elif line[index] == "'": in_single_char = False # End if # End if (just ignore any other character) elif in_double_char: if line[index:min(index + 1, last_ind)] == '""': # Embedded double quote index = index + 1 # +1 and end of loop elif line[index] == '"': in_double_char = False # End if # End if (just ignore any other character) elif line[index] == "'": # If we got here, we are not in a character context, start single in_single_char = True elif line[index] == '"': # If we got here, we are not in a character context, start double in_double_char = True elif line[index] == '!': # If we got here, we are not in a character context, done with line comment_col = index index = last_ind # End if index = index + 1 # End while return continue_in_col, in_single_char, in_double_char, comment_col
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)