class HasAttributes(object): known_attributes = [] a = AttributeHolder(attributes=[]) def topyf(self, tab=''): s = '' for attr in self.a.attributes: s += tab + attr + '\n' return s def update_attributes(self, *attrs): attributes = self.a.attributes known_attributes = self.known_attributes if len(attrs) == 1 and isinstance(attrs[0], (tuple, list)): attrs = attrs[0] for attr in attrs: uattr = attr.upper() if uattr not in attributes: if isinstance(known_attributes, (list, tuple)): if uattr not in known_attributes: self.warning('unknown attribute %r' % (attr)) elif not known_attributes(uattr): self.warning('unknown attribute %r' % (attr)) attributes.append(uattr) else: self.warning('multiple specification of attribute %r' % (attr)) return
class HasUseStmt(object): a = AttributeHolder(use={}, use_provides={}) def get_entity(self, name): for modname, modblock in list(self.top.a.module.items()): for stmt in modblock.content: if getattr(stmt, 'name', '') == name: return stmt return def topyf(self, tab=' '): sys.stderr.write('HasUseStmt.topyf not implemented\n') return ''
class HasTypeDecls(object): a = AttributeHolder(type_decls={}) def topyf(self, tab=''): s = '' for name, stmt in list(self.a.type_decls.items()): s += stmt.topyf(tab=' ' + tab) return s def get_type_decl_by_kind(self, kind): type_decls = self.a.type_decls type_decl = type_decls.get(kind, None) if type_decl is None: return self.get_entity(kind) return type_decl
class AccessSpecs(object): a = AttributeHolder(private_id_list=[], public_id_list=[]) def topyf(self, tab=' '): private_list = self.a.private_id_list public_list = self.a.public_id_list lines = [] if '' in private_list: lines.append(tab + 'PRIVATE\n') if '' in public_list: lines.append(tab + 'PUBLIC\n') for a in private_list: if not a: continue lines.append(tab + 'PRIVATE :: %s\n' % (a)) for a in public_list: if not a: continue lines.append(tab + 'PUBLIC :: %s\n' % (a)) return ''.join(lines)
class HasVariables(object): a = AttributeHolder( variables={}, variable_names=[] # defines the order of declarations ) def get_variable_by_name(self, name): variables = self.a.variables if name in variables: var = variables[name] else: var = variables[name] = Variable(self, name) self.a.variable_names.append(name) return var def topyf(self, tab='', only_variables=None): s = '' if only_variables is None: only_variables = list(self.a.variables.keys()) for name in only_variables: var = self.a.variables[name] s += tab + str(var) + '\n' return s
class HasImplicitStmt(object): ''' Class encapsulating information about any Implicit statements contained within a scoping block. ''' a = AttributeHolder(implicit_rules={}) def get_type_by_name(self, name): ''' Returns an object of the correct type (Integer or Real) using Fortran's implicit typing rules for the supplied variable name. :param str name: The variable name. :returns: Object describing the variable. :rtype: Either :py:class:`fparser.one.typedecl_statements.Real` \ or :py:class:`fparser.one.typedecl_statements.Integer`. ''' # The implicit_rules dict is populated by the analyze() method # of one.typedecl_statements.Implicit implicit_rules = self.a.implicit_rules if implicit_rules is None: raise AnalyzeError('Implicit rules mapping is null ' 'while getting %r type' % (name)) line = name[0].lower() if line in implicit_rules: return implicit_rules[line] # default rules: if line in 'ijklmn': line = 'default_integer' else: line = 'default_real' var = implicit_rules.get(line, None) if var is None: if line[8:] == 'real': implicit_rules[line] = var = Real(self, self.item.copy('real')) else: implicit_rules[line] = var = Integer(self, self.item.copy('integer')) return var def topyf(self, tab=' '): ''' Constructs a pyf representation of this class. :param str tab: White space to prepend to output. :returns: pyf code for this implicit statement. :rtype: str ''' implicit_rules = self.a.implicit_rules if implicit_rules is None: return tab + 'IMPLICIT NONE\n' # Construct a dict where the keys are types and the items are # the list of initial letters mapped to that type items = {} for char, itype in list(implicit_rules.items()): if char.startswith('default'): continue type_str = itype.tostr() if type_str in items: items[type_str].append(char) else: items[type_str] = [char] if not items: return tab + '! default IMPLICIT rules apply\n' stmt = 'IMPLICIT' impl_list = [] for itype, letter_list in list(items.items()): letter_list.sort() impl_list.append(itype + ' (%s)' % (', '.join(letter_list))) stmt += ' ' + ', '.join(impl_list) return tab + stmt + '\n'
class SubProgramStatement(BeginStatement, ProgramBlock, HasImplicitStmt, HasAttributes, HasUseStmt, HasVariables, HasTypeDecls, AccessSpecs): """ [<prefix>] <FUNCTION|SUBROUTINE> <name> [( <args> )] [<suffix>] """ a = AttributeHolder(internal_subprogram={}) known_attributes = ['RECURSIVE', 'PURE', 'ELEMENTAL'] def process_item(self): clsname = self.__class__.__name__.lower() item = self.item line = item.get_line() m = self.match(line) i = line.lower().find(clsname) assert i != -1, repr((clsname, line)) self.prefix = line[:i].rstrip() self.name = line[i:m.end()].lstrip()[len(clsname):].strip() line = line[m.end():].lstrip() args = [] if line.startswith('('): i = line.find(')') assert i != -1, repr(line) line2 = item.apply_map(line[:i + 1]) for a in line2[1:-1].split(','): a = a.strip() if not a: continue args.append(a) line = line[i + 1:].lstrip() suffix = item.apply_map(line) self.bind, suffix = parse_bind(suffix, item) self.result = None if isinstance(self, Function): self.result, suffix = parse_result(suffix, item) if suffix: assert self.bind is None, repr(self.bind) self.bind, suffix = parse_result(suffix, item) if self.result is None: self.result = self.name assert not suffix, repr(suffix) self.args = args self.typedecl = None return BeginStatement.process_item(self) def tostr(self): clsname = self.__class__.__name__.upper() s = '' if self.prefix: s += self.prefix + ' ' if self.typedecl is not None: assert isinstance(self, Function), repr(self.__class__.__name__) s += self.typedecl.tostr() + ' ' s += clsname suf = '' if self.result and self.result != self.name: suf += ' RESULT ( %s )' % (self.result) if self.bind: suf += ' BIND ( %s )' % (', '.join(self.bind)) return '%s %s (%s)%s' % (s, self.name, ', '.join(self.args), suf) def get_classes(self): return f2py_stmt + specification_part + execution_part \ + internal_subprogram_part def analyze(self): content = self.content[:] if self.prefix: self.update_attributes(self.prefix.upper().split()) variables = self.a.variables for a in self.args: assert a not in variables if is_name(a): variables[a] = Variable(self, a) elif a == '*': variables[a] = Variable(self, a) # XXX: fix me appropriately else: message = 'argument must be a name or * but got %r' raise AnalyzeError(message % (a)) if isinstance(self, Function): var = variables[self.result] = Variable(self, self.result) if self.typedecl is not None: var.set_type(self.typedecl) while content: stmt = content.pop(0) if isinstance(stmt, Contains): for stmt in filter_stmts(content, SubProgramStatement): stmt.analyze() self.a.internal_subprogram[stmt.name] = stmt stmt = content.pop(0) while isinstance(stmt, Comment): stmt = content.pop(0) assert isinstance(stmt, self.end_stmt_cls), repr(stmt) elif isinstance(stmt, self.end_stmt_cls): continue else: if hasattr(stmt, "analyze"): stmt.analyze() else: message = 'Failed to parse: {0}' raise AnalyzeError(message.format(str(stmt))) if content: logger.info('Not analyzed content: %s' % content) # self.show_message('Not analyzed content: %s' % content) parent_provides = self.parent.get_provides() if parent_provides is not None: if self.name in parent_provides: message = 'module subprogram name conflict with %s, ' \ + 'overriding.' self.warning(message % (self.name)) if self.is_public(): parent_provides[self.name] = self if self.is_recursive() and self.is_elemental(): message = 'C1241 violation: prefix cannot specify both ' \ + 'ELEMENTAL and RECURSIVE' self.warning(message) return def topyf(self, tab=''): s = tab + self.__class__.__name__.upper() s += ' ' + self.name + ' (%s)' % (', '.join(self.args)) if isinstance(self, Function) and self.result != self.name: s += ' RESULT (%s)' % (self.result) s += '\n' s += HasImplicitStmt.topyf(self, tab=tab + ' ') s += AccessSpecs.topyf(self, tab=tab + ' ') s += HasTypeDecls.topyf(self, tab=tab + ' ') s += HasVariables.topyf(self, tab=tab + ' ', only_variables=self.args) s += tab + 'END ' + self.__class__.__name__.upper() \ + ' ' + self.name + '\n' return s def is_public(self): return not self.is_private() def is_private(self): return self.parent.check_private(self.name) def is_recursive(self): return 'RECURSIVE' in self.a.attributes def is_pure(self): return 'PURE' in self.a.attributes def is_elemental(self): return 'ELEMENTAL' in self.a.attributes
class Interface(BeginStatement, HasAttributes, HasImplicitStmt, HasUseStmt, HasModuleProcedures, AccessSpecs): """ INTERFACE [<generic-spec>] | ABSTRACT INTERFACE END INTERFACE [<generic-spec>] <generic-spec> = <generic-name> | OPERATOR ( <defined-operator> ) | ASSIGNMENT ( = ) | <dtio-generic-spec> <dtio-generic-spec> = READ ( FORMATTED ) | READ ( UNFORMATTED ) | WRITE ( FORMATTED ) | WRITE ( UNFORMATTED ) """ modes = ['free', 'fix', 'pyf'] pattern = r'(interface\s*(\w+\s*\(.*\)|\w*)|abstract\s*interface)\Z' match = re.compile(pattern, re.I).match end_stmt_cls = EndInterface blocktype = 'interface' a = AttributeHolder(interface_provides={}) def get_classes(self): line = intrinsic_type_spec + interface_specification if self.reader.format.mode == 'pyf': return [Subroutine, Function] + line return line def process_item(self): line = self.item.get_line() line = self.item.apply_map(line) self.isabstract = line.startswith('abstract') if self.isabstract: self.generic_spec = '' else: self.generic_spec = line[len(self.blocktype):].strip() self.name = self.generic_spec # XXX return BeginStatement.process_item(self) def tostr(self): if self.isabstract: return 'ABSTRACT INTERFACE' return 'INTERFACE ' + str(self.generic_spec) def analyze(self): content = self.content[:] while content: stmt = content.pop(0) if isinstance(stmt, self.end_stmt_cls): break stmt.analyze() if content: logger.info('Not analyzed content: %s' % content) # self.show_message('Not analyzed content: %s' % content) if self.name in self.parent.a.variables: var = self.parent.a.variables.pop(self.name) self.update_attributes(var.attributes) if isinstance(self.parent, Module): # XXX parent_interface = self.parent.get_interface() if self.name in parent_interface: p = parent_interface[self.name] last = p.content.pop() assert isinstance(last, EndInterface), repr(last.__class__) p.content += self.content p.update_attributes(self.a.attributes) else: parent_interface[self.name] = self return def topyf(self, tab=''): s = tab + self.tostr() + '\n' s += HasImplicitStmt.topyf(self, tab=tab + ' ') s += HasAttributes.topyf(self, tab=tab + ' ') s += HasUseStmt.topyf(self, tab=tab + ' ') s += tab + 'END' + self.tostr() + '\n' return s
class Module(BeginStatement, HasAttributes, HasImplicitStmt, HasUseStmt, HasVariables, HasTypeDecls, AccessSpecs): """ MODULE <name> .. END [MODULE [name]] """ match = re.compile(r'module\s*\w+\Z', re.I).match end_stmt_cls = EndModule a = AttributeHolder( module_subprogram={}, module_provides={}, # all symbols that are public and # so can be imported via USE # statement by other blocks module_interface={}) known_attributes = ['PUBLIC', 'PRIVATE'] def get_classes(self): return access_spec + specification_part + module_subprogram_part def process_item(self): name = self.item.get_line().replace(' ', '') name = name[len(self.blocktype):].strip() self.name = name return BeginStatement.process_item(self) def get_provides(self): return self.a.module_provides def get_interface(self): return self.a.module_interface def analyze(self): content = self.content[:] while content: stmt = content.pop(0) if isinstance(stmt, Contains): for stmt in filter_stmts(content, SubProgramStatement): stmt.analyze() self.a.module_subprogram[stmt.name] = stmt stmt = content.pop(0) while isinstance(stmt, Comment): stmt = content.pop(0) if not isinstance(stmt, EndModule): stmt.error('Expected END MODULE statement (analyzer).') continue stmt.analyze() if content: logger.info('Not analyzed content: %s' % content) # self.show_message('Not analyzed content: %s' % content) module_provides = self.a.module_provides for name, var in list(self.a.variables.items()): if var.is_public(): if name in module_provides: message = 'module data object name conflict with %s, ' \ + 'overriding.' self.warning(message % (name)) module_provides[name] = var return def topyf(self, tab=''): s = tab + 'MODULE ' + self.name + '\n' s += HasImplicitStmt.topyf(self, tab=tab + ' ') s += AccessSpecs.topyf(self, tab=tab + ' ') s += HasAttributes.topyf(self, tab=tab + ' ') s += HasTypeDecls.topyf(self, tab=tab + ' ') s += HasVariables.topyf(self, tab=tab + ' ') for name, stmt in list(self.a.module_interface.items()): s += stmt.topyf(tab=tab + ' ') s += tab + ' CONTAINS\n' for name, stmt in list(self.a.module_subprogram.items()): s += stmt.topyf(tab=tab + ' ') s += tab + 'END MODULE ' + self.name + '\n' return s def check_private(self, name): if name in self.a.public_id_list: return False if name in self.a.private_id_list: return True if '' in self.a.public_id_list: return False if '' in self.a.private_id_list: return True # todo: handle generic-spec-s in id-lists. return
class BeginSource(BeginStatement): """ Fortran source content. """ match = staticmethod(lambda s: True) end_stmt_cls = EndSource a = AttributeHolder( module={}, external_subprogram={}, blockdata={}, ) # def tofortran(self, isfix=None): # if isfix: # tab = 'C' # else: # tab = self.get_indent_tab(isfix=isfix) + '!' # return BeginStatement.tofortran(self, isfix=isfix) def tostr(self): return None def process_item(self): self.name = self.reader.name self.top = self self.fill(end_flag=True) return def analyze(self): for stmt in self.content: if isinstance(stmt, Module): stmt.analyze() self.a.module[stmt.name] = stmt elif isinstance(stmt, SubProgramStatement): stmt.analyze() self.a.external_subprogram[stmt.name] = stmt elif isinstance(stmt, BlockData): stmt.analyze() self.a.blockdata[stmt.name] = stmt else: stmt.analyze() return def get_classes(self): if self.reader.format.is_pyf: return [PythonModule] + program_unit return program_unit def process_subitem(self, item): # MAIN block does not define start/end line conditions, # so it should never end until all lines are read. # However, sometimes F77 programs lack the PROGRAM statement, # and here we fix that: if self.reader.format.is_f77: line = item.get_line() if line == 'end': message = item.reader.format_message( 'WARNING', 'assuming the end of undefined PROGRAM statement', item.span[0], item.span[1]) logger.warning(message) # print >> sys.stderr, message p = Program(self) p.content.extend(self.content) p.content.append(EndProgram(p, item)) self.content[:] = [p] return return BeginStatement.process_subitem(self, item) def topyf(self, tab=''): # XXXX s = '' for name, stmt in list(self.a.module.items()): s += stmt.topyf(tab=tab) for name, stmt in list(self.a.external_subprogram.items()): s += stmt.topyf(tab=tab) for name, stmt in list(self.a.blockdata.items()): s += stmt.topyf(tab=tab) return s
class HasModuleProcedures(object): a = AttributeHolder(module_procedures=[])
class Type(BeginStatement, HasVariables, HasAttributes, AccessSpecs): """ TYPE [[, <typ-attr-spec-list>] ::] <type-name> [( <type-param-name-list> )] <typ-attr-spec> = <access-spec> | EXTENDS ( <parent-type-name> ) | ABSTRACT | BIND(C) """ match = re.compile(r'type\b\s*').match end_stmt_cls = EndType a = AttributeHolder( extends=None, parameters={}, component_names=[], # Order for sequence types components={}) pattern = r'\A(PUBLIC|PRIVATE|SEQUENCE|ABSTRACT|BIND\s*\(.*\))\Z' known_attributes = re.compile(pattern, re.I).match def process_item(self): line = self.item.get_line()[4:].lstrip() if line.startswith('('): self.isvalid = False return specs = [] i = line.find('::') if i != -1: for s in line[:i].split(','): s = s.strip() if s: specs.append(s) line = line[i + 2:].lstrip() self.specs = specs i = line.find('(') if i != -1: self.name = line[:i].rstrip() assert line[-1] == ')', repr(line) self.params = split_comma(line[i + 1:-1].lstrip()) else: self.name = line self.params = [] if not is_name(self.name): self.isvalid = False return return BeginStatement.process_item(self) def tostr(self): s = 'TYPE' if self.specs: s += ', '.join([''] + self.specs) + ' ::' s += ' ' + self.name if self.params: s += ' (' + ', '.join(self.params) + ')' return s def get_classes(self): return [Integer] + private_or_sequence + component_part +\ type_bound_procedure_part def analyze(self): for spec in self.specs: i = spec.find('(') if i != -1: assert spec.endswith(')'), repr(spec) s = spec[:i].rstrip().upper() n = spec[i + 1:-1].strip() if s == 'EXTENDS': self.a.extends = n continue elif s == 'BIND': args, rest = parse_bind(spec) assert not rest, repr(rest) spec = 'BIND(%s)' % (', '.join(args)) else: spec = '%s(%s)' % (s, n) else: spec = spec.upper() self.update_attributes(spec) component_names = self.a.component_names content = self.content[:] while content: stmt = content.pop(0) if isinstance(stmt, self.end_stmt_cls): break stmt.analyze() if content: message = 'Not analyzed content: %s' % content logging.getLogger(__class__).info(message) parameters = self.a.parameters components = self.a.components component_names = self.a.component_names for name in self.a.variable_names: var = self.a.variables[name] if name in self.params: parameters[name] = var else: component_names.append(name) components[name] = var self.parent.a.type_decls[self.name] = self parent_provides = self.parent.get_provides() if parent_provides is not None: if self.is_public(): if self.name in parent_provides: message = 'type declaration name conflict with {}, ' \ + 'overriding.' self.warning(message.format(self.name)) parent_provides[self.name] = self return def topyf(self, tab=''): s = tab + 'TYPE' if self.a.extends is not None: s += ', EXTENDS(%s) ::' % (self.a.extends) s += ' ' + self.name if self.a.parameters: s += ' (%s)' % (', '.join(self.a.parameters)) s += '\n' s += AccessSpecs.topyf(self, tab=tab + ' ') s += HasAttributes.topyf(self, tab=tab + ' ') s += HasVariables.topyf(self, tab=tab + ' ') s += tab + 'END TYPE ' + self.name + '\n' return s # Wrapper methods: def is_public(self): return not self.is_private() def is_private(self): if 'PUBLIC' in self.a.attributes: return False if 'PRIVATE' in self.a.attributes: return True return self.parent.check_private(self.name)