diff options
author | Pearu Peterson <pearu.peterson@gmail.com> | 2006-07-04 12:12:47 +0000 |
---|---|---|
committer | Pearu Peterson <pearu.peterson@gmail.com> | 2006-07-04 12:12:47 +0000 |
commit | a8672c2c5e1fe4ae0c51805aa1eed5d736a5eedf (patch) | |
tree | b081bf1ae86d4b0f734157e2e7810c8fb43568b0 /numpy | |
parent | 17d8921d6d37745e01601fc19497ae2b4029b10c (diff) | |
download | numpy-a8672c2c5e1fe4ae0c51805aa1eed5d736a5eedf.tar.gz |
Working on Fortran analyzer.
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/f2py/lib/base_classes.py | 177 | ||||
-rw-r--r-- | numpy/f2py/lib/block_statements.py | 215 | ||||
-rw-r--r-- | numpy/f2py/lib/parsefortran.py | 18 | ||||
-rw-r--r-- | numpy/f2py/lib/research/rat/rational.f90 | 6 | ||||
-rw-r--r-- | numpy/f2py/lib/research/rat/wrap.f90 | 5 | ||||
-rw-r--r-- | numpy/f2py/lib/splitline.py | 2 | ||||
-rw-r--r-- | numpy/f2py/lib/statements.py | 163 | ||||
-rw-r--r-- | numpy/f2py/lib/test_parser.py | 63 | ||||
-rw-r--r-- | numpy/f2py/lib/typedecl_statements.py | 204 | ||||
-rw-r--r-- | numpy/f2py/lib/utils.py | 121 |
10 files changed, 846 insertions, 128 deletions
diff --git a/numpy/f2py/lib/base_classes.py b/numpy/f2py/lib/base_classes.py index cdae17f03..21aa63b8e 100644 --- a/numpy/f2py/lib/base_classes.py +++ b/numpy/f2py/lib/base_classes.py @@ -3,8 +3,155 @@ __all__ = ['Statement','BeginStatement','EndStatement'] import re import sys +import copy from readfortran import Line +from utils import split_comma, specs_split_comma +class AttributeHolder: + # copied from symbolic.base module + """ + Defines a object with predefined attributes. Only those attributes + are allowed that are specified as keyword arguments of a constructor. + When an argument is callable then the corresponding attribute will + be read-only and set by the value the callable object returns. + """ + def __init__(self, **kws): + self._attributes = {} + self._readonly = [] + for k,v in kws.items(): + self._attributes[k] = v + if callable(v): + self._readonly.append(k) + return + + def __getattr__(self, name): + if name not in self._attributes: + raise AttributeError,'%s instance has no attribute %r, '\ + 'expected attributes: %s' \ + % (self.__class__.__name__,name, + ','.join(self._attributes.keys())) + value = self._attributes[name] + if callable(value): + value = value() + self._attributes[name] = value + return value + + def __setattr__(self, name, value): + if name in ['_attributes','_readonly']: + self.__dict__[name] = value + return + if name in self._readonly: + raise AttributeError,'%s instance attribute %r is readonly' \ + % (self.__class__.__name__, name) + if name not in self._attributes: + raise AttributeError,'%s instance has no attribute %r, '\ + 'expected attributes: %s' \ + % (self.__class__.__name__,name,','.join(self._attributes.keys())) + self._attributes[name] = value + + def __repr__(self): + l = [] + for k in self._attributes.keys(): + v = getattr(self,k) + l.append('%s=%r' % (k,v)) + return '%s(%s)' % (self.__class__.__name__,', '.join(l)) + + def todict(self): + d = {} + for k in self._attributes.keys(): + v = getattr(self, k) + d[k] = v + return d + +def get_base_classes(cls): + bases = () + for c in cls.__bases__: + bases += get_base_classes(c) + return bases + cls.__bases__ + (cls,) + +class Variable: + """ + Variable instance has attributes: + name + typedecl + dimension + attributes + intent + parent - Statement instances defining the variable + """ + def __init__(self, parent, name): + self.parent = parent + self.name = name + self.typedecl = None + self.dimension = None + self.attributes = [] + self.intent = None + self.bind = [] + self.check = [] + return + + def set_type(self, typedecl): + if self.typedecl is not None: + if not self.typedecl==typedecl: + message = 'Warning: variable %r already has type %s' \ + % (self.name, self.typedecl.tostr()) + message += '.. resetting to %s' % (typedecl.tostr()) + self.parent.show_message(message) + self.typedecl = typedecl + return + + def update(self, attrs): + attributes = self.attributes + for attr in attrs: + lattr = attr.lower() + uattr = attr.upper() + if lattr.startswith('dimension'): + assert self.dimension is None, `self.dimension,attr` + l = attr[9:].lstrip() + assert l[0]+l[-1]=='()',`l` + self.dimension = split_comma(l[1:-1].strip(), self.parent.item) + continue + if lattr.startswith('intent'): + l = attr[6:].lstrip() + assert l[0]+l[-1]=='()',`l` + self.intent = intent = [] + for i in split_comma(l[1:-1].strip(), self.parent.item): + if i not in intent: + intent.append(i) + continue + if lattr.startswith('bind'): + l = attr[4:].lstrip() + assert l[0]+l[-1]=='()',`l` + self.bind = specs_split_comma(l[1:-1].strip(), self.parent.item) + continue + if lattr.startswith('check'): + l = attr[5:].lstrip() + assert l[0]+l[-1]=='()',`l` + self.check.extend(split_comma(l[1:-1].strip()), self.parent.item) + continue + if uattr not in attributes: + attributes.append(uattr) + return + + def __str__(self): + s = '' + if self.typedecl is not None: + s += self.typedecl.tostr() + ' ' + a = self.attributes[:] + if self.dimension is not None: + a.append('DIMENSION(%s)' % (', '.join(self.dimension))) + if self.intent is not None: + a.append('INTENT(%s)' % (', '.join(self.intent))) + if self.bind: + a.append('BIND(%s)' % (', '.join(self.bind))) + if self.check: + a.append('CHECK(%s)' % (', '.join(self.check))) + if a: + s += ', '.join(a) + ' :: ' + return s + self.name + +class ProgramBlock: + pass class Statement: """ @@ -18,6 +165,14 @@ class Statement: def __init__(self, parent, item): self.parent = parent self.reader = parent.reader + self.top = getattr(parent,'top',None) + if isinstance(parent, ProgramBlock): + self.programblock = parent + elif isinstance(self, ProgramBlock): + self.programblock = self + elif hasattr(parent,'programblock'): + self.programblock = parent.programblock + self.item = item # when a statement instance is constructed by error, set isvalid to False @@ -25,8 +180,19 @@ class Statement: # when a statement should be ignored, set ignore to True self.ignore = False + # attribute a will hold analyze information. + a_dict = {} + for cls in get_base_classes(self.__class__): + if hasattr(cls,'a'): + a_dict.update(copy.deepcopy(cls.a.todict())) + self.a = AttributeHolder(**a_dict) + if hasattr(self.__class__,'a'): + assert self.a is not self.__class__.a + self.process_item() + return + def get_indent_tab(self,colon=None,deindent=False): if self.reader.isfix: tab = ' '*6 @@ -60,6 +226,10 @@ class Statement: sys.stderr.flush() return + def analyze(self): + self.show_message('nothing analyzed in %s' % (self.__class__.__name__)) + return + class BeginStatement(Statement): """ <blocktype> <name> @@ -237,6 +407,10 @@ class BeginStatement(Statement): #sys.exit() return + def analyze(self): + for stmt in self.content: + stmt.analyze() + return class EndStatement(Statement): """ @@ -274,6 +448,9 @@ class EndStatement(Statement): self.isvalid = False self.name = self.parent.name + def analyze(self): + return + def __str__(self): return self.get_indent_tab()[:-2] + 'END %s %s'\ % (self.blocktype.upper(),self.name or '') diff --git a/numpy/f2py/lib/block_statements.py b/numpy/f2py/lib/block_statements.py index 804a5549e..5c72fb808 100644 --- a/numpy/f2py/lib/block_statements.py +++ b/numpy/f2py/lib/block_statements.py @@ -5,31 +5,115 @@ import re import sys -from base_classes import BeginStatement, EndStatement, Statement +from base_classes import BeginStatement, EndStatement, Statement,\ + AttributeHolder, ProgramBlock from readfortran import Line +from utils import filter_stmts, parse_bind, parse_result + +class HasImplicitStmt: + + a = AttributeHolder(implicit_rules = None) + + def get_type(self, name): + implicit_rules = self.a.implicit_rules + if implicit_rules=={}: + raise ValueError,'Implicit rules mapping is null' + l = name[0].lower() + if implicit_rules.has_key(l): + return implicit_rules[l] + # default rules + if l in 'ijklmn': + return implicit_rules['default_integer'] + return implicit_rules['default_real'] + + def initialize(self): + implicit_rules = self.a.implicit_rules + if implicit_rules is not None: + return + self.a.implicit_rules = implicit_rules = {} + real = Real(self, self.item.copy('real')) + assert real.isvalid + integer = Integer(self, self.item.copy('integer')) + assert integer.isvalid + implicit_rules['default_real'] = real + implicit_rules['default_integer'] = integer + return + +class HasUseStmt: + + a = AttributeHolder(use = {}) + + 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 initialize(self): + + return + +class HasVariables: + + a = AttributeHolder(variables = {}) + +class HasTypeDecls: + + a = AttributeHolder(type_decls = {}) + +class HasExternal: + + a = AttributeHolder(external = []) + +class HasAttributes: + + a = AttributeHolder(attributes = []) # File block class EndSource(EndStatement): """ + Dummy End statement for BeginSource. """ match = staticmethod(lambda s: False) class BeginSource(BeginStatement): """ + Fortran source content. """ match = staticmethod(lambda s: True) - end_stmt_cls = EndSource + a = AttributeHolder(module = {}, + external_subprogram = {}, + blockdata = {}, + ) 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: + message = 'Unexpected statement %r in %r block. Ignoring.' \ + % (stmt.__class__, self.__class__) + return + def get_classes(self): return program_unit @@ -58,7 +142,9 @@ class BeginSource(BeginStatement): class EndModule(EndStatement): match = re.compile(r'end(\s*module\s*\w*|)\Z', re.I).match -class Module(BeginStatement): +class Module(BeginStatement, HasAttributes, + HasImplicitStmt, HasUseStmt, HasVariables, + HasTypeDecls): """ MODULE <name> .. @@ -66,6 +152,9 @@ class Module(BeginStatement): """ match = re.compile(r'module\s*\w+\Z', re.I).match end_stmt_cls = EndModule + a = AttributeHolder(module_subprogram = {}, + module_data = {}, + ) def get_classes(self): return access_spec + specification_part + module_subprogram_part @@ -75,17 +164,37 @@ class Module(BeginStatement): self.name = name return BeginStatement.process_item(self) - #def __str__(self): - # s = self.get_indent_tab(deindent=True) - # s += 'MODULE '+ self.name - # return s + def analyze(self): + content = self.content[:] + + [stmt.analyze() for stmt in filter_stmts(content, Use)] + HasUseStmt.initialize(self) + + [stmt.analyze() for stmt in filter_stmts(content, Implicit)] + HasImplicitStmt.initialize(self) + + 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) + assert isinstance(stmt, EndModule),`stmt` + continue + stmt.analyze() + + if content: + print 'Not analyzed content:',content + + return # Python Module class EndPythonModule(EndStatement): match = re.compile(r'end(\s*python\s*module\s*\w*|)\Z', re.I).match -class PythonModule(BeginStatement): +class PythonModule(BeginStatement, HasImplicitStmt, HasUseStmt): """ PYTHON MODULE <name> .. @@ -111,7 +220,8 @@ class EndProgram(EndStatement): """ match = re.compile(r'end(\s*program\s*\w*|)\Z', re.I).match -class Program(BeginStatement): +class Program(BeginStatement, ProgramBlock, HasAttributes, + HasImplicitStmt, HasUseStmt): """ PROGRAM [name] """ match = re.compile(r'program\s*\w*\Z', re.I).match @@ -137,7 +247,8 @@ class EndBlockData(EndStatement): match = re.compile(r'end(\s*block\s*data\s*\w*|)\Z', re.I).match blocktype = 'blockdata' -class BlockData(BeginStatement): +class BlockData(BeginStatement, HasImplicitStmt, HasUseStmt, + HasVariables): """ BLOCK DATA [ <block-data-name> ] """ @@ -157,7 +268,7 @@ class EndInterface(EndStatement): match = re.compile(r'end\s*interface\s*\w*\Z', re.I).match blocktype = 'interface' -class Interface(BeginStatement): +class Interface(BeginStatement, HasImplicitStmt, HasUseStmt): """ INTERFACE [<generic-spec>] | ABSTRACT INTERFACE END INTERFACE [<generic-spec>] @@ -198,10 +309,14 @@ class Interface(BeginStatement): # Subroutine -class SubProgramStatement(BeginStatement): +class SubProgramStatement(BeginStatement, ProgramBlock, + HasImplicitStmt, HasAttributes, + HasUseStmt, HasVariables, HasTypeDecls, + HasExternal): """ [ <prefix> ] <FUNCTION|SUBROUTINE> <name> [ ( <args> ) ] [ <suffix> ] """ + a = AttributeHolder(internal_subprogram = {}) def process_item(self): clsname = self.__class__.__name__.lower() @@ -223,7 +338,17 @@ class SubProgramStatement(BeginStatement): if not a: continue args.append(a) line = line[i+1:].lstrip() - self.suffix = item.apply_map(line) + 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) @@ -237,12 +362,60 @@ class SubProgramStatement(BeginStatement): assert isinstance(self, Function),`self.__class__.__name__` s += self.typedecl.tostr() + ' ' s += clsname - return '%s %s(%s) %s' % (s, self.name,', '.join(self.args),self.suffix) + 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[:] + + variables = self.a.variables + for a in self.args: + assert not variables.has_key(a) + assert is_name(a) + variables[a] = Variable(self, a) + + if isinstance(self, Function): + variables[self.result] = Variable(self, self.result) + + [stmt.analyze() for stmt in filter_stmts(content, Use)] + HasUseStmt.initialize(self) + + [stmt.analyze() for stmt in filter_stmts(content, Implicit)] + HasImplicitStmt.initialize(self) + + 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) + assert isinstance(stmt, self.end_stmt_cls),`stmt` + elif isinstance(stmt, TypeDecl): + stmt.analyze() + self.a.type_declaration[stmt.name] = stmt + elif isinstance(stmt, self.end_stmt_cls): + continue + elif isinstance(stmt, External): + self.a.external.extend(stmt.items) + continue + elif isinstance(stmt, tuple(declaration_type_spec)): + stmt.analyze() + else: + stmt.analyze() + if content: + print 'Not analyzed content:',content + + return + class EndSubroutine(EndStatement): """ END [SUBROUTINE [name]] @@ -252,7 +425,7 @@ class EndSubroutine(EndStatement): class Subroutine(SubProgramStatement): """ - [prefix] SUBROUTINE <name> [ ( [<dummy-arg-list>] ) [<proc-language-binding-spec>]] + [ <prefix> ] SUBROUTINE <name> [ ( [ <dummy-arg-list> ] ) [ <proc-language-binding-spec> ]] """ end_stmt_cls = EndSubroutine match = re.compile(r'(recursive|pure|elemental|\s)*subroutine\s*\w+', re.I).match @@ -271,6 +444,8 @@ class Function(SubProgramStatement): <prefix> = <prefix-spec> [ <prefix-spec> ]... <prefix-spec> = <declaration-type-spec> | RECURSIVE | PURE | ELEMENTAL + <suffix> = <proc-language-binding-spec> [ RESULT ( <result-name> ) ] + | RESULT ( <result-name> ) [ <proc-language-binding-spec> ] """ end_stmt_cls = EndFunction match = re.compile(r'(recursive|pure|elemental|\s)*function\s*\w+', re.I).match @@ -292,7 +467,7 @@ class SubprogramPrefix(Statement): self.item.clone(rest) self.isvalid = False return - if self.parent.__class__ not in [Function,Subroutine]: + if self.parent.__class__ not in [Function, Subroutine]: self.isvalid = False return prefix = prefix + ' ' + self.parent.prefix @@ -558,7 +733,7 @@ class EndType(EndStatement): match = re.compile(r'end\s*type\s*\w*\Z', re.I).match blocktype = 'type' -class Type(BeginStatement): +class Type(BeginStatement, HasVariables, HasAttributes): """ TYPE [ [, <type-attr-spec-list>] ::] <type-name> [ ( <type-param-name-list> ) ] <type-attr-spec> = <access-spec> | EXTENDS ( <parent-type-name> ) @@ -607,6 +782,12 @@ class Type(BeginStatement): return [Integer] + private_or_sequence + component_part +\ type_bound_procedure_part + def analyze(self): + BeginStatement.analyze(self) + assert isinstance(self.parent,HasTypeDecls) + self.parent.a.type_decls[self.name] = self + return + TypeDecl = Type # Enum diff --git a/numpy/f2py/lib/parsefortran.py b/numpy/f2py/lib/parsefortran.py index 33a6eb112..314bbfa24 100644 --- a/numpy/f2py/lib/parsefortran.py +++ b/numpy/f2py/lib/parsefortran.py @@ -17,6 +17,7 @@ from numpy.distutils.misc_util import yellow_text, red_text from readfortran import FortranFileReader, FortranStringReader from block_statements import BeginSource +from utils import AnalyzeError class FortranParser: @@ -37,7 +38,7 @@ class FortranParser: def parse(self): try: - return BeginSource(self) + block = self.block = BeginSource(self) except KeyboardInterrupt: raise except: @@ -50,6 +51,16 @@ class FortranParser: reader = reader.reader traceback.print_exc(file=sys.stdout) self.reader.show_message(red_text('STOPPED PARSING'), sys.stdout) + return + return + + def analyze(self): + try: + self.block.analyze() + except AnalyzeError: + pass + except: + raise return def test_pyf(): @@ -123,8 +134,9 @@ def simple_main(): reader = FortranFileReader(filename) print yellow_text('Processing '+filename+' (mode=%r)' % (reader.mode)) parser = FortranParser(reader) - block = parser.parse() - #print block + parser.parse() + parser.analyze() + #print parser.block def profile_main(): import hotshot, hotshot.stats diff --git a/numpy/f2py/lib/research/rat/rational.f90 b/numpy/f2py/lib/research/rat/rational.f90 index 7ae6d340a..9cbc57e15 100644 --- a/numpy/f2py/lib/research/rat/rational.f90 +++ b/numpy/f2py/lib/research/rat/rational.f90 @@ -41,8 +41,4 @@ module rational end module rational -subroutine init_f90_funcs(set_f90_funcs) - use rational - external set_f90_funcs - call set_f90_funcs(4, rat_create, rat_show, rat_set, rat_add) -end subroutine init_f90_funcs + diff --git a/numpy/f2py/lib/research/rat/wrap.f90 b/numpy/f2py/lib/research/rat/wrap.f90 new file mode 100644 index 000000000..6c9da8491 --- /dev/null +++ b/numpy/f2py/lib/research/rat/wrap.f90 @@ -0,0 +1,5 @@ +subroutine init_f90_funcs(set_f90_funcs) + use rational + external set_f90_funcs + call set_f90_funcs(4, rat_create, rat_show, rat_set, rat_add) +end subroutine init_f90_funcs diff --git a/numpy/f2py/lib/splitline.py b/numpy/f2py/lib/splitline.py index a1148a502..1bbd77b6e 100644 --- a/numpy/f2py/lib/splitline.py +++ b/numpy/f2py/lib/splitline.py @@ -65,7 +65,7 @@ def string_replace_map(line, lower=False, _cache['pindex'] += 1 index = _cache['pindex'] key = 'F2PY_EXPR_TUPLE_%s' % (index) - it = item[1:-1] + it = item[1:-1].strip() string_map[key] = it rev_string_map[it] = key expr_keys.append(key) diff --git a/numpy/f2py/lib/statements.py b/numpy/f2py/lib/statements.py index 741b0a11f..5a24f2d84 100644 --- a/numpy/f2py/lib/statements.py +++ b/numpy/f2py/lib/statements.py @@ -2,40 +2,15 @@ import re import sys -from base_classes import Statement +from base_classes import Statement, Variable +from expression import Expression # Auxiliary tools -is_name = re.compile(r'\w+\Z').match +from utils import split_comma, specs_split_comma, AnalyzeError, ParseError,\ + get_module_file, parse_bind, parse_result -def split_comma(line, item = None, comma=','): - items = [] - if item is None: - for s in line.split(comma): - s = s.strip() - if not s: continue - items.append(s) - return items - newitem = item.copy(line, True) - apply_map = newitem.apply_map - for s in newitem.get_line().split(comma): - s = apply_map(s).strip() - if not s: continue - items.append(s) - return items - -def specs_split_comma(line, item): - specs0 = split_comma(line, item) - specs = [] - for spec in specs0: - i = spec.find('=') - if i!=-1: - kw = spec[:i].strip().upper() - v = spec[i+1:].strip() - specs.append('%s = %s' % (kw, v)) - else: - specs.append(spec) - return specs +is_name = re.compile(r'\w+\Z').match class StatementWithNamelist(Statement): """ @@ -113,7 +88,7 @@ class Assign(Statement): match = re.compile(r'assign\s*\d+\s*to\s*\w+\s*\Z',re.I).match def process_item(self): line = self.item.get_line()[6:].lstrip() - i = line.find('to') + i = line.lower().find('to') assert not self.item.has_map() self.items = [line[:i].rstrip(),line[i+2:].lstrip()] return @@ -121,7 +96,6 @@ class Assign(Statement): return self.get_indent_tab() + 'ASSIGN %s TO %s' \ % (self.items[0], self.items[1]) - class Call(Statement): """Call statement class CALL <procedure-designator> [ ( [ <actual-arg-spec-list> ] ) ] @@ -172,6 +146,14 @@ class Call(Statement): s += '('+', '.join(map(str,self.items))+ ')' return s + def analyze(self): + a = self.programblock.a + if hasattr(a, 'external'): + external = a.external + if self.designator in external: + print 'Need to analyze:',self + return + class Goto(Statement): """ GO TO <label> @@ -244,6 +226,7 @@ class Continue(Statement): def __str__(self): return self.get_indent_tab(deindent=True) + 'CONTINUE' + def analyze(self): return class Return(Statement): """ @@ -261,6 +244,8 @@ class Return(Statement): return tab + 'RETURN %s' % (self.expr) return tab + 'RETURN' + def analyze(self): return + class Stop(Statement): """ STOP [ <stop-code> ] @@ -278,6 +263,8 @@ class Stop(Statement): return tab + 'STOP %s' % (self.code) return tab + 'STOP' + def analyze(self): return + class Print(Statement): """ PRINT <format> [, <output-item-list>] @@ -526,7 +513,7 @@ class Access(Statement): def process_item(self): clsname = self.__class__.__name__.lower() line = self.item.get_line() - if not line.startswith(clsname): + if not line.lower().startswith(clsname): self.isvalid = False return line = line[len(clsname):].lstrip() @@ -542,8 +529,23 @@ class Access(Statement): return tab + clsname + ' ' + ', '.join(self.items) return tab + clsname -class Public(Access): pass -class Private(Access): pass + def analyze(self): + clsname = self.__class__.__name__.upper() + if self.items: + variables = self.parent.a.variables + for n in self.items: + if not variables.has_key(n): + variables[n] = Variable(self, n) + var = variables[n] + var.update([clsname]) + else: + self.parent.a.attributes.append(clsname) + return + +class Public(Access): + is_public = True +class Private(Access): + is_public = False class Close(Statement): """ @@ -591,7 +593,7 @@ class FilePositioningStatement(Statement): def process_item(self): clsname = self.__class__.__name__.lower() line = self.item.get_line() - if not line.startswith(clsname): + if not line.lower().startswith(clsname): self.isvalid = False return line = line[len(clsname):].lstrip() @@ -799,7 +801,7 @@ class Use(Statement): else: self.name = line[:i].rstrip() line = line[i+1:].lstrip() - if line.startswith('only') and line[4:].lstrip().startswith(':'): + if line.lower().startswith('only') and line[4:].lstrip().startswith(':'): self.isonly = True line = line[4:].lstrip()[1:].lstrip() self.items = split_comma(line, self.item) @@ -819,6 +821,39 @@ class Use(Statement): s += ' ' + ', '.join(self.items) return tab + s + def analyze(self): + modules = self.top.a.module + + if not modules.has_key(self.name): + fn = None + for d in self.reader.include_dirs: + fn = get_module_file(self.name, d) + if fn is not None: + break + + if fn is not None: + from readfortran import FortranFileReader + from parsefortran import FortranParser + self.show_message('Processing %r (parent file=%r)' % (fn, self.reader.name)) + reader = FortranFileReader(fn) + parser = FortranParser(reader) + parser.parse() + parser.block.a.module.update(modules) + parser.analyze() + modules.update(parser.block.a.module) + + if not modules.has_key(self.name): + message = self.reader.format_message(\ + 'ERROR', + 'No information about the use module %r.' \ + % (self.name), + self.item.span[0],self.item.span[1]) + self.show_message(message) + raise AnalyzeError + return + + return + class Exit(Statement): """ EXIT [ <do-construct-name> ] @@ -869,7 +904,7 @@ class Dimension(Statement): DIMENSION [ :: ] <array-name> ( <array-spec> ) [ , <array-name> ( <array-spec> ) ]... """ - match = re.compile(r'dimension\b').match + match = re.compile(r'dimension\b', re.I).match def process_item(self): line = self.item.get_line()[9:].lstrip() if line.startswith('::'): @@ -884,7 +919,7 @@ class Target(Statement): TARGET [ :: ] <object-name> ( <array-spec> ) [ , <object-name> ( <array-spec> ) ]... """ - match = re.compile(r'target\b').match + match = re.compile(r'target\b', re.I).match def process_item(self): line = self.item.get_line()[6:].lstrip() if line.startswith('::'): @@ -992,7 +1027,7 @@ class External(StatementWithNamelist): """ EXTERNAL [ :: ] <external-name-list> """ - match = re.compile(r'external\b').match + match = re.compile(r'external\b', re.I).match class Namelist(Statement): """ @@ -1129,33 +1164,14 @@ class Entry(Statement): line = line[i+1:].lstrip() else: items = [] - binds = [] - result = '' - if line.startswith('bind'): - line = line[4:].lstrip() - i = line.find(')') - assert line.startswith('(') and i != -1,`line` - binds = split_comma(line[1:i], self.item) - line = line[i+1:].lstrip() - if line.startswith('result'): - line = line[6:].lstrip() - i = line.find(')') - assert line.startswith('(') and i != -1,`line` - result = line[1:i].strip() - assert is_name(result),`result,line` - line = line[i+1:].lstrip() - if line.startswith('bind'): - assert not binds - line = line[4:].lstrip() - i = line.find(')') - assert line.startswith('(') and i != -1,`line` - binds = split_comma(line[1:i], self.item) - line = line[i+1:].lstrip() + self.bind, line = parse_bind(line, self.item) + self.result, line = parse_result(line, self.item) + if line: + assert self.bind is None,`self.bind` + self.bind, line = parse_bind(line, self.item) assert not line,`line` self.name = name self.items = items - self.result = result - self.binds = binds return def __str__(self): tab = self.get_indent_tab() @@ -1164,8 +1180,8 @@ class Entry(Statement): s += ' (%s)' % (', '.join(self.items)) if self.result: s += ' RESULT (%s)' % (self.result) - if self.binds: - s += ' BIND (%s)' % (', '.join(self.binds)) + if self.bind: + s += ' BIND (%s)' % (', '.join(self.bind)) return s class Import(StatementWithNamelist): @@ -1359,15 +1375,8 @@ class Bind(Statement): """ match = re.compile(r'bind\s*\(.*\)',re.I).match def process_item(self): - line = self.item.get_line()[4:].lstrip() - specs = [] - for spec in specs_split_comma(line[1:line.index(')')].strip(), self.item): - if is_name(spec): - specs.append(spec.upper()) - else: - specs.append(spec) - self.specs = specs - line = line[line.index(')')+1:].lstrip() + line = self.item.line + self.specs, line = parse_bind(line, self.item) if line.startswith('::'): line = line[2:].lstrip() items = [] @@ -1461,7 +1470,7 @@ class Case(Statement): items = split_comma(line[1:i].strip(), self.item) line = line[i+1:].lstrip() else: - assert line.startswith('default'),`line` + assert line.lower().startswith('default'),`line` items = [] line = line[7:].lstrip() for i in range(len(items)): @@ -1530,7 +1539,7 @@ class ElseWhere(Statement): ELSE WHERE ( <mask-expr> ) [ <where-construct-name> ] ELSE WHERE [ <where-construct-name> ] """ - match = re.compile(r'else\s*where\b').match + match = re.compile(r'else\s*where\b',re.I).match def process_item(self): line = self.item.get_line()[4:].lstrip()[5:].lstrip() self.expr = None diff --git a/numpy/f2py/lib/test_parser.py b/numpy/f2py/lib/test_parser.py index 47b20b652..af54efdf2 100644 --- a/numpy/f2py/lib/test_parser.py +++ b/numpy/f2py/lib/test_parser.py @@ -14,7 +14,12 @@ def parse(cls, line, label='', raise ValueError, '%r does not match %s pattern' % (line, cls.__name__) stmt = cls(item, item) if stmt.isvalid: - return str(stmt) + r = str(stmt) + if not isstrict: + r1 = parse(cls, r, isstrict=True) + if r != r1: + raise ValueError, 'Failed to parse %r with %s pattern in pyf mode, got %r' % (r, cls.__name__, r1) + return r raise ValueError, 'parsing %r with %s pattern failed' % (line, cls.__name__) class test_Statements(NumpyTestCase): @@ -107,7 +112,7 @@ class test_Statements(NumpyTestCase): 'allocate (a, stat=b)'), 'ALLOCATE (a, STAT = b)') assert_equal(parse(Allocate, 'allocate (a,b(:1))'), 'ALLOCATE (a, b(:1))') assert_equal(parse(Allocate, \ - 'allocate (real(8)::a)'), 'ALLOCATE (REAL(8) :: a)') + 'allocate (real(8)::a)'), 'ALLOCATE (REAL(KIND=8) :: a)') def check_deallocate(self): assert_equal(parse(Deallocate, 'deallocate (a)'), 'DEALLOCATE (a)') assert_equal(parse(Deallocate, 'deallocate (a, stat=b)'), 'DEALLOCATE (a, STAT = b)') @@ -289,12 +294,12 @@ class test_Statements(NumpyTestCase): assert_equal(parse(Entry,'entry a(b)'), 'ENTRY a (b)') assert_equal(parse(Entry,'entry a(b,*)'), 'ENTRY a (b, *)') assert_equal(parse(Entry,'entry a bind(c , name="a b")'), - 'ENTRY a BIND (c, name="a b")') + 'ENTRY a BIND (C, NAME = "a b")') assert_equal(parse(Entry,'entry a result (b)'), 'ENTRY a RESULT (b)') assert_equal(parse(Entry,'entry a bind(d) result (b)'), 'ENTRY a RESULT (b) BIND (d)') - assert_equal(parse(Entry,'entry a result (b) bind(c)'), - 'ENTRY a RESULT (b) BIND (c)') + assert_equal(parse(Entry,'entry a result (b) bind( c )'), + 'ENTRY a RESULT (b) BIND (C)') assert_equal(parse(Entry,'entry a(b,*) result (g)'), 'ENTRY a (b, *) RESULT (g)') @@ -423,5 +428,53 @@ class test_Statements(NumpyTestCase): assert_equal(parse(Pause,'pause "hey"'),'PAUSE "hey"') assert_equal(parse(Pause,'pause "hey pa"'),'PAUSE "hey pa"') + def check_integer(self): + assert_equal(parse(Integer,'integer'),'INTEGER') + assert_equal(parse(Integer,'integer*4'),'INTEGER(KIND=4)') + assert_equal(parse(Integer,'integer*4 a'),'INTEGER(KIND=4) a') + assert_equal(parse(Integer,'integer*4, a'),'INTEGER(KIND=4) a') + assert_equal(parse(Integer,'integer*4 a ,b'),'INTEGER(KIND=4) a, b') + assert_equal(parse(Integer,'integer*4 :: a ,b'),'INTEGER(KIND=4) a, b') + assert_equal(parse(Integer,'integer*4 a(1,2)'),'INTEGER(KIND=4) a(1,2)') + assert_equal(parse(Integer,'integer*4 :: a(1,2),b'),'INTEGER(KIND=4) a(1,2), b') + assert_equal(parse(Integer,'integer*4 external :: a'), + 'INTEGER(KIND=4), external :: a') + assert_equal(parse(Integer,'integer*4, external :: a'), + 'INTEGER(KIND=4), external :: a') + assert_equal(parse(Integer,'integer*4 external , intent(in) :: a'), + 'INTEGER(KIND=4), external, intent(in) :: a') + assert_equal(parse(Integer,'integer(kind=4)'),'INTEGER(KIND=4)') + assert_equal(parse(Integer,'integer ( kind = 4)'),'INTEGER(KIND=4)') + assert_equal(parse(Integer,'integer(kind=2+2)'),'INTEGER(KIND=2+2)') + assert_equal(parse(Integer,'integer(kind=f(4,5))'),'INTEGER(KIND=f(4,5))') + + def check_character(self): + assert_equal(parse(Character,'character'),'CHARACTER') + assert_equal(parse(Character,'character*2'),'CHARACTER(LEN=2)') + assert_equal(parse(Character,'character**'),'CHARACTER(LEN=*)') + assert_equal(parse(Character,'character*(2)'),'CHARACTER(LEN=2)') + assert_equal(parse(Character,'character*(len =2)'),'CHARACTER(LEN=2)') + assert_equal(parse(Character,'character*(len =2),'),'CHARACTER(LEN=2)') + assert_equal(parse(Character,'character*(len =:)'),'CHARACTER(LEN=:)') + assert_equal(parse(Character,'character(len =2)'),'CHARACTER(LEN=2)') + assert_equal(parse(Character,'character(2)'),'CHARACTER(LEN=2)') + assert_equal(parse(Character,'character(kind=2)'),'CHARACTER(KIND=2)') + assert_equal(parse(Character,'character(kind=2,len=3)'), + 'CHARACTER(LEN=3, KIND=2)') + assert_equal(parse(Character,'character(lEN=3,kind=2)'), + 'CHARACTER(LEN=3, KIND=2)') + assert_equal(parse(Character,'character(len=3,kind=2)', isstrict=True), + 'CHARACTER(LEN=3, KIND=2)') + assert_equal(parse(Character,'chaRACTER(len=3,kind=fA(1,2))', isstrict=True), + 'CHARACTER(LEN=3, KIND=fA(1,2))') + assert_equal(parse(Character,'character(len=3,kind=fA(1,2))'), + 'CHARACTER(LEN=3, KIND=fa(1,2))') + + def check_implicit(self): + assert_equal(parse(Implicit,'implicit none'),'IMPLICIT NONE') + assert_equal(parse(Implicit,'implicit'),'IMPLICIT NONE') + assert_equal(parse(Implicit,'implicit integer (i-m)'), + 'IMPLICIT INTEGER ( i-m )') + if __name__ == "__main__": NumpyTest().run() diff --git a/numpy/f2py/lib/typedecl_statements.py b/numpy/f2py/lib/typedecl_statements.py index 3f91d4a35..dba912743 100644 --- a/numpy/f2py/lib/typedecl_statements.py +++ b/numpy/f2py/lib/typedecl_statements.py @@ -1,6 +1,9 @@ import re -from base_classes import Statement, BeginStatement, EndStatement +import string +from base_classes import Statement, BeginStatement, EndStatement,\ + AttributeHolder, Variable +from utils import split_comma, AnalyzeError # Intrinsic type specification statements @@ -66,7 +69,7 @@ class TypeDeclarationStatement(Statement): line = item.get_line() from block_statements import Function - if not line.startswith(clsname): + if not line.lower().startswith(clsname): i = 0 j = 0 for c in line: @@ -77,7 +80,7 @@ class TypeDeclarationStatement(Statement): break line = line[:i].replace(' ','') + line[i:] - assert line.startswith(clsname),`line,clsname` + assert line.lower().startswith(clsname),`line,clsname` line = line[len(clsname):].lstrip() if line.startswith('('): @@ -121,31 +124,137 @@ class TypeDeclarationStatement(Statement): line = line[1:].lstrip() self.raw_selector = selector + if isinstance(self, Character): + self.selector = self._parse_char_selector(selector) + else: + self.selector = self._parse_kind_selector(selector) + i = line.find('::') if i==-1: - self.attrspec = '' - self.entity_decls = line + self.attrspec = [] + self.entity_decls = split_comma(line, self.item) else: - self.attrspec = line[:i].rstrip() - self.entity_decls = line[i+2:].lstrip() - if isinstance(self.parent, Function) and self.parent.name==self.entity_decls: + self.attrspec = split_comma(line[:i].rstrip(), self.item) + self.entity_decls = split_comma(line[i+2:].lstrip(), self.item) + if isinstance(self.parent, Function) \ + and self.parent.name in self.entity_decls: assert self.parent.typedecl is None,`self.parent.typedecl` self.parent.typedecl = self self.ignore = True return + def _parse_kind_selector(self, selector): + if not selector: + return '' + if selector.startswith('*'): + kind = selector[1:].lstrip() + else: + assert selector[0]+selector[-1]=='()',`selector` + l = selector[1:-1].strip() + if l.lower().startswith('kind'): + l = l[4:].lstrip() + assert l.startswith('='),`l` + kind = l[1:].lstrip() + else: + kind = l + return kind + + def _parse_char_selector(self, selector): + if not selector: + return '','' + if selector.startswith('*'): + l = selector[1:].lstrip() + if l.startswith('('): + if l.endswith(','): l = l[:-1].rstrip() + assert l.endswith(')'),`l` + l = l[1:-1].strip() + if l.lower().startswith('len'): + l = l[3:].lstrip()[1:].lstrip() + kind='' + else: + assert selector[0]+selector[-1]=='()',`selector` + l = split_comma(selector[1:-1].strip(), self.item) + if len(l)==1: + l = l[0] + if l.lower().startswith('len'): + l=l[3:].lstrip() + assert l.startswith('='),`l` + l=l[1:].lstrip() + kind = '' + elif l.lower().startswith('kind'): + kind = l[4:].lstrip()[1:].lstrip() + l = '' + else: + kind = '' + else: + assert len(l)==2 + if l[0].lower().startswith('len'): + assert l[1].lower().startswith('kind'),`l` + kind = l[1][4:].lstrip()[1:].lstrip() + l = l[0][3:].lstrip()[1:].lstrip() + elif l[0].lower().startswith('kind'): + assert l[1].lower().startswith('len'),`l` + kind = l[0][4:].lstrip()[1:].lstrip() + l = l[1][3:].lstrip()[1:].lstrip() + else: + if l[1].lower().startswith('kind'): + kind = l[1][4:].lstrip()[1:].lstrip() + l = l[0] + else: + kind = l[1] + l = l[0] + return l,kind + def tostr(self): - clsname = self.__class__.__name__.upper() - return clsname + self.raw_selector + clsname = self.__class__.__name__.upper() + s = '' + if isinstance(self, Character): + length, kind = self.selector + if length and kind: + s += '(LEN=%s, KIND=%s)' % (length,kind) + elif length: + s += '(LEN=%s)' % (length) + elif kind: + s += '(KIND=%s)' % (kind) + else: + kind = self.selector + if kind: + s += '(KIND=%s)' % (kind) + return clsname + s def __str__(self): tab = self.get_indent_tab() - if not self.attrspec: - return tab + '%s :: %s' \ - % (self.tostr(), self.entity_decls) - return tab + '%s, %s :: %s' \ - % (self.tostr(), self.attrspec, self.entity_decls) - + s = self.tostr() + if self.attrspec: + s += ', ' + ', '.join(self.attrspec) + if self.entity_decls: + s += ' ::' + if self.entity_decls: + s += ' ' + ', '.join(self.entity_decls) + return tab + s + + def __eq__(self, other): + if self.__class__ is not other.__class__: + return False + return self.selector==other.selector + + def astypedecl(self): + return self.__class__(self.parent, self.item.copy(self.tostr())) + + def analyze(self): + if not self.entity_decls: + return + variables = self.parent.a.variables + typedecl = self.astypedecl() + for name in self.entity_decls: + if not variables.has_key(name): + variables[name] = var = Variable(self, name) + else: + var = variables[name] + var.set_type(typedecl) + var.update(self.attrspec) + return + class Integer(TypeDeclarationStatement): match = re.compile(r'integer\b',re.I).match @@ -187,18 +296,73 @@ class Implicit(Statement): <letter-spec> = <letter> [ - <letter> ] """ match = re.compile(r'implicit\b',re.I).match + + letters = string.lowercase + def process_item(self): line = self.item.get_line()[8:].lstrip() - if line=='none': + if line.lower()=='none': self.items = [] return - self.items = [s.strip() for s in line.split()] - assert self.items + items = [] + for item in split_comma(line, self.item): + i = item.find('(') + assert i!=-1 and item.endswith(')'),`item` + specs = [] + for spec in split_comma(item[i+1:-1].strip(), self.item): + if '-' in spec: + s,e = spec.lower().split('-') + assert s in self.letters and e in self.letters + else: + e = s = spec.lower() + assert s in self.letters + specs.append((s,e)) + self.specs = specs + tspec = item[:i].rstrip() + stmt = None + for cls in declaration_type_spec: + if cls.match(tspec): + stmt = cls(self, self.item.copy(tspec)) + if stmt.isvalid: + break + assert stmt is not None,`item,line` + items.append((stmt,specs)) + self.items = items + return def __str__(self): tab = self.get_indent_tab() if not self.items: return tab + 'IMPLICIT NONE' - return tab + 'IMPLICIT ' + ', '.join(self.items) + l = [] + for stmt,specs in self.items: + l1 = [] + for s,e in specs: + if s==e: + l1.append(s) + else: + l1.append(s + '-' + e) + l.append('%s ( %s )' % (stmt.tostr(), ', '.join(l1))) + return tab + 'IMPLICIT ' + ', '.join(l) + def analyze(self): + implicit_rules = self.parent.a.implicit_rules + if not self.items: + if implicit_rules: + raise AnalyzeError,'IMPLICIT NONE implies emtpy implicit_rules'\ + ' but got %r' % (implicit_rules) + return + if implicit_rules is None: + self.parent.a.implicit_rules = implicit_rules = {} + for stmt,specs in self.items: + s,e = specs + for l in string.lowercase[string.lowercase.index(s.lower()):\ + string.lowercase.index(e.lower())+1]: + implicit_rules[l] = stmt + return +intrinsic_type_spec = [ \ + Integer , Real, + DoublePrecision, Complex, DoubleComplex, Character, Logical, Byte + ] +declaration_type_spec = intrinsic_type_spec + [ TypeStmt, Class ] diff --git a/numpy/f2py/lib/utils.py b/numpy/f2py/lib/utils.py new file mode 100644 index 000000000..7a501144e --- /dev/null +++ b/numpy/f2py/lib/utils.py @@ -0,0 +1,121 @@ + +import re +import os, glob + +class ParseError(Exception): + pass + +class AnalyzeError(Exception): + pass + +is_name = re.compile(r'^[a-z_]\w*$',re.I).match + +def split_comma(line, item = None, comma=','): + items = [] + if item is None: + for s in line.split(comma): + s = s.strip() + if not s: continue + items.append(s) + return items + newitem = item.copy(line, True) + apply_map = newitem.apply_map + for s in newitem.get_line().split(comma): + s = apply_map(s).strip() + if not s: continue + items.append(s) + return items + +def specs_split_comma(line, item = None): + specs0 = split_comma(line, item) + specs = [] + for spec in specs0: + i = spec.find('=') + if i!=-1: + kw = spec[:i].strip().upper() + v = spec[i+1:].strip() + specs.append('%s = %s' % (kw, v)) + else: + specs.append(spec) + return specs + +def parse_bind(line, item = None): + if not line.lower().startswith('bind'): + return None, line + if item is not None: + newitem = item.copy(line, apply_map=True) + newline = newitem.get_line() + else: + newitem = None + newline = newline[4:].lstrip() + i = newline.find(')') + assert i!=-1,`newline` + args = [] + for a in specs_split_comma(newline[1:i].strip(), newitem): + if a=='c': a = a.upper() + args.append(a) + rest = newline[i+1:].lstrip() + if item is not None: + rest = newitem.apply_map(rest) + return args, rest + +def parse_result(line, item = None): + if not line.lower().startswith('result'): + return None, line + line = line[6:].lstrip() + i = line.find(')') + assert i != -1,`line` + name = line[1:i].strip() + assert is_name(name),`name` + return name, line[i+1:].lstrip() + +def filter_stmts(content, classes): + stmts = [] + indices = [] + for i in range(len(content)): + stmt = content[i] + if isinstance(stmt, classes): + stmts.append(stmt) + indices.append(i) + indices.reverse() + for i in indices: + del content[i] + return stmts + + +def get_module_files(directory, _cache={}): + if _cache.has_key(directory): + return _cache[directory] + module_line = re.compile(r'(\A|^)module\s+(?P<name>\w+)\s*(!.*|)$',re.I | re.M) + d = {} + for fn in glob.glob(os.path.join(directory,'*.f90')): + f = open(fn,'r') + for name in module_line.findall(f.read()): + name = name[1] + if d.has_key(name): + print d[name],'already defines',name + continue + d[name] = fn + _cache[directory] = d + return d + +def get_module_file(name, directory, _cache={}): + fn = _cache.get(name, None) + if fn is not None: + return fn + if name.endswith('_module'): + f1 = os.path.join(directory,name[:-7]+'.f90') + if os.path.isfile(f1): + _cache[name] = fn + return f1 + pattern = re.compile(r'\s*module\s+(?P<name>[a-z]\w*)', re.I).match + for fn in glob.glob(os.path.join(directory,'*.f90')): + f = open(fn,'r') + for line in f: + m = pattern(line) + if m and m.group('name')==name: + _cache[name] = fn + f.close() + return fn + f.close() + return |