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 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 HasImplicitStmt(object): a = AttributeHolder(implicit_rules={}) def get_type_by_name(self, name): implicit_rules = self.a.implicit_rules if implicit_rules is None: raise AnalyzeError, 'Implicit rules mapping is null while getting %r type' % ( name) l = name[0].lower() if l in implicit_rules: return implicit_rules[l] # default rules: if l in 'ijklmn': l = 'default_integer' else: l = 'default_real' t = implicit_rules.get(l, None) if t is None: if l[8:] == 'real': implicit_rules[l] = t = Real(self, self.item.copy('real')) else: implicit_rules[l] = t = Integer(self, self.item.copy('integer')) return t def topyf(self, tab=' '): implicit_rules = self.a.implicit_rules if implicit_rules is None: return tab + 'IMPLICIT NONE\n' items = {} for c, t in implicit_rules.items(): if c.startswith('default'): continue st = t.tostr() if st in items: items[st].append(c) else: items[st] = [c] if not items: return tab + '! default IMPLICIT rules apply\n' s = 'IMPLICIT' ls = [] for st, l in items.items(): l.sort() ls.append(st + ' (%s)' % (', '.join(l))) s += ' ' + ', '.join(ls) return tab + s + '\n'
class HasTypeDecls(object): a = AttributeHolder(type_decls={}) def topyf(self, tab=''): s = '' for name, stmt in 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 l = [] if '' in private_list: l.append(tab + 'PRIVATE\n') if '' in public_list: l.append(tab + 'PUBLIC\n') for a in private_list: if not a: continue l.append(tab + 'PRIVATE :: %s\n' % (a)) for a in public_list: if not a: continue l.append(tab + 'PUBLIC :: %s\n' % (a)) return ''.join(l)
class HasVariables: a = AttributeHolder(variables = {}, variable_names = [] # defines the order of declarations ) def get_variable_by_name(self, name): variables = self.a.variables if variables.has_key(name): 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 = self.a.variables.keys() for name in only_variables: var = self.a.variables[name] s += tab + str(var) + '\n' return s
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, ` 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, ` 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, ` self.bind ` self.bind, suffix = parse_result(suffix, item) if self.result is None: self.result = self.name assert not suffix, ` 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), ` 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: raise AnalyzeError('argument must be a name or * but got %r' % (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), ` stmt ` elif isinstance(stmt, self.end_stmt_cls): continue else: stmt.analyze() 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: self.warning( 'module subprogram name conflict with %s, overriding.' % (self.name)) if self.is_public(): parent_provides[self.name] = self if self.is_recursive() and self.is_elemental(): self.warning( 'C1241 violation: prefix cannot specify both ELEMENTAL and RECURSIVE' ) 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'] match = re.compile( r'(interface\s*(\w+\s*\(.*\)|\w*)|abstract\s*interface)\Z', re.I).match end_stmt_cls = EndInterface blocktype = 'interface' a = AttributeHolder(interface_provides={}) def get_classes(self): l = intrinsic_type_spec + interface_specification if self.reader.mode == 'pyf': return [Subroutine, Function] + l return l 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 get_provides(self): # return self.a.interface_provides def analyze(self): content = self.content[:] while content: stmt = content.pop(0) if isinstance(stmt, self.end_stmt_cls): break stmt.analyze() #assert isinstance(stmt, SubProgramStatement),`stmt.__class__.__name__` 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), ` 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(' ', '')[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 self.a.variables.items(): if var.is_public(): if name in module_provides: self.warning( 'module data object name conflict with %s, overriding.' % (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 self.a.module_interface.items(): s += stmt.topyf(tab=tab + ' ') s += tab + ' CONTAINS\n' for name, stmt in 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 tab + BeginStatement.tofortran(self, isfix=isfix) def tostr(self): return self.blocktype.upper() + ' ' + self.name 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.ispyf: 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.isf77: 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 self.a.module.items(): s += stmt.topyf(tab=tab) for name, stmt in self.a.external_subprogram.items(): s += stmt.topyf(tab=tab) for name, stmt in 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 [ [ , <type-attr-spec-list>] :: ] <type-name> [ ( <type-param-name-list> ) ] <type-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=[], # specifies component order for sequence types components={}) known_attributes = re.compile( r'\A(PUBLIC|PRIVATE|SEQUENCE|ABSTRACT|BIND\s*\(.*\))\Z', 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] == ')', ` 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(')'), ` 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, ` 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: logging.info('Not analyzed content: %s' % content) # self.show_message('Not analyzed content: %s' % content) 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: self.warning( 'type declaration name conflict with %s, overriding.' % (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 get_bit_size(self, _cache={}): try: return _cache[id(self)] except KeyError: s = 0 for name, var in self.a.components.items(): s += var.get_bit_size() _cache[id(self)] = s return s 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)