diff options
-rw-r--r-- | numpy/f2py/lib/base_classes.py | 180 | ||||
-rw-r--r-- | numpy/f2py/lib/block_statements.py | 37 | ||||
-rw-r--r-- | numpy/f2py/lib/parsefortran.py | 20 | ||||
-rw-r--r-- | numpy/f2py/lib/readfortran.py | 9 | ||||
-rw-r--r-- | numpy/f2py/lib/statements.py | 253 | ||||
-rw-r--r-- | numpy/f2py/lib/typedecl_statements.py | 47 | ||||
-rw-r--r-- | numpy/f2py/lib/utils.py | 10 |
7 files changed, 434 insertions, 122 deletions
diff --git a/numpy/f2py/lib/base_classes.py b/numpy/f2py/lib/base_classes.py index 21aa63b8e..ee19caaae 100644 --- a/numpy/f2py/lib/base_classes.py +++ b/numpy/f2py/lib/base_classes.py @@ -5,7 +5,8 @@ import re import sys import copy from readfortran import Line -from utils import split_comma, specs_split_comma +from numpy.distutils.misc_util import yellow_text, red_text +from utils import split_comma, specs_split_comma, is_int_literal_constant class AttributeHolder: # copied from symbolic.base module @@ -81,27 +82,111 @@ class Variable: """ def __init__(self, parent, name): self.parent = parent + self.parents = [parent] self.name = name self.typedecl = None self.dimension = None + self.bounds = None + self.length = None self.attributes = [] self.intent = None self.bind = [] self.check = [] + self.init = None + return + + def add_parent(self, parent): + if id(parent) not in map(id, self.parents): + self.parents.append(parent) + self.parent = parent 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.parent.warning(\ + 'variable %r already has type %s,'\ + ' resetting to %s' \ + % (self.name, self.typedecl.tostr(),typedecl.tostr())) self.typedecl = typedecl return - def update(self, attrs): + def set_init(self, expr): + if self.init is not None: + if not self.init==expr: + self.parent.warning(\ + 'variable %r already has initialization %r, '\ + ' resetting to %r' % (self.name, self.expr, expr)) + self.init = expr + return + + def set_dimension(self, dims): + if self.dimension is not None: + if not self.dimension==dims: + self.parent.warning(\ + 'variable %r already has dimension %r, '\ + ' resetting to %r' % (self.name, self.dimension, dims)) + self.dimension = dims + return + + def set_bounds(self, bounds): + if self.bounds is not None: + if not self.bounds==bounds: + self.parent.warning(\ + 'variable %r already has bounds %r, '\ + ' resetting to %r' % (self.name, self.bounds, bounds)) + self.bounds = bounds + return + + def set_length(self, length): + if self.length is not None: + if not self.length==length: + self.parent.warning(\ + 'variable %r already has length %r, '\ + ' resetting to %r' % (self.name, self.length, length)) + self.length = length + return + + known_intent_specs = ['IN','OUT','INOUT','CACHE','HIDE', 'COPY', + 'OVERWRITE', 'CALLBACK', 'AUX', 'C', 'INPLACE', + 'OUT='] + + def set_intent(self, intent): + if self.intent is None: + self.intent = [] + for i in intent: + if i not in self.intent: + if i not in self.known_intent_specs: + self.parent.warning('unknown intent-spec %r for %r'\ + % (i, self.name)) + self.intent.append(i) + return + + known_attributes = ['PUBLIC', 'PRIVATE', 'ALLOCATABLE', 'ASYNCHRONOUS', + 'EXTERNAL', 'INTRINSIC', 'OPTIONAL', 'PARAMETER', + 'POINTER', 'PROTECTED', 'SAVE', 'TARGET', 'VALUE', + 'VOLATILE', 'REQUIRED'] + + def is_private(self): + if 'PUBLIC' in self.attributes: return False + if 'PRIVATE' in self.attributes: return True + parent_attrs = self.parent.parent.a.attributes + if 'PUBLIC' in parent_attrs: return False + if 'PRIVATE' in parent_attrs: return True + return + def is_public(self): return not self.is_private() + + def is_allocatable(self): return 'ALLOCATABLE' in self.attributes + def is_external(self): return 'EXTERNAL' in self.attributes + def is_intrinsic(self): return 'INTRINSIC' in self.attributes + def is_parameter(self): return 'PARAMETER' in self.attributes + def is_optional(self): return 'OPTIONAL' in self.attributes + def is_required(self): return 'REQUIRED' in self.attributes + + def update(self, *attrs): attributes = self.attributes + if len(attrs)==1 and isinstance(attrs[0],(tuple,list)): + attrs = attrs[0] for attr in attrs: lattr = attr.lower() uattr = attr.upper() @@ -109,20 +194,19 @@ class Variable: 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) + self.set_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) + self.set_intent(specs_split_comma(l[1:-1].strip(), + self.parent.item, upper=True)) 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) + self.bind = specs_split_comma(l[1:-1].strip(), self.parent.item, + upper = True) continue if lattr.startswith('check'): l = attr[5:].lstrip() @@ -130,6 +214,8 @@ class Variable: self.check.extend(split_comma(l[1:-1].strip()), self.parent.item) continue if uattr not in attributes: + if uattr not in self.known_attributes: + self.parent.warning('unknown attribute %r' % (attr)) attributes.append(uattr) return @@ -148,7 +234,17 @@ class Variable: a.append('CHECK(%s)' % (', '.join(self.check))) if a: s += ', '.join(a) + ' :: ' - return s + self.name + s += self.name + if self.bounds: + s += '(%s)' % (', '.join(self.bounds)) + if self.length: + if is_int_literal_constant(self.length): + s += '*%s' % (self.length) + else: + s += '*(%s)' % (self.length) + if self.init: + s += ' = ' + self.init + return s class ProgramBlock: pass @@ -164,7 +260,10 @@ class Statement: def __init__(self, parent, item): self.parent = parent - self.reader = parent.reader + if item is not None: + self.reader = item.reader + else: + self.reader = parent.reader self.top = getattr(parent,'top',None) if isinstance(parent, ProgramBlock): self.programblock = parent @@ -221,15 +320,44 @@ class Statement: tab = c + s + colon + tab return tab - def show_message(self, message): - print >> sys.stderr, message - sys.stderr.flush() + def format_message(self, kind, message): + message = self.reader.format_message(kind, message, + self.item.span[0], self.item.span[1]) + return message + + def show_message(self, message, stream=sys.stderr): + print >> stream, message + stream.flush() + return + + def error(self, message): + message = self.format_message('ERROR', red_text(message)) + self.show_message(message) + return + + def warning(self, message): + message = self.format_message('WARNING', yellow_text(message)) + self.show_message(message) + return + + def info(self, message): + message = self.format_message('INFO', message) + self.show_message(message) return def analyze(self): - self.show_message('nothing analyzed in %s' % (self.__class__.__name__)) + self.warning('nothing analyzed') return + def get_variable(self, name): + variables = self.parent.a.variables + if not variables.has_key(name): + variables[name] = var = Variable(self, name) + else: + var = variables[name] + var.add_parent(self) + return var + class BeginStatement(Statement): """ <blocktype> <name> @@ -300,12 +428,7 @@ class BeginStatement(Statement): item = self.get_item() if not end_flag: - message = self.item.reader.format_message(\ - 'WARNING', - 'failed to find the end of block for %s'\ - % (self.__class__.__name__), - self.item.span[0],self.item.span[1]) - self.show_message(message) + self.warning('failed to find the end of block') return def process_subitem(self, item): @@ -439,12 +562,9 @@ class EndStatement(Statement): self.isvalid = False if line: if not line==self.parent.name: - message = item.reader.format_message(\ - 'WARNING', - 'expected the end of %r block but got end of %r, skipping.'\ - % (self.parent.name, line), - item.span[0],item.span[1]) - self.show_message(message) + self.warning(\ + 'expected the end of %r block but got the end of %r, skipping.'\ + % (self.parent.name, line)) self.isvalid = False self.name = self.parent.name diff --git a/numpy/f2py/lib/block_statements.py b/numpy/f2py/lib/block_statements.py index 5c72fb808..767bd0893 100644 --- a/numpy/f2py/lib/block_statements.py +++ b/numpy/f2py/lib/block_statements.py @@ -62,14 +62,14 @@ class HasTypeDecls: a = AttributeHolder(type_decls = {}) -class HasExternal: - - a = AttributeHolder(external = []) - class HasAttributes: a = AttributeHolder(attributes = []) +class HasModuleProcedures: + + a = AttributeHolder(module_procedures = []) + # File block class EndSource(EndStatement): @@ -110,8 +110,7 @@ class BeginSource(BeginStatement): stmt.analyze() self.a.blockdata[stmt.name] = stmt else: - message = 'Unexpected statement %r in %r block. Ignoring.' \ - % (stmt.__class__, self.__class__) + stmt.analyze() return def get_classes(self): @@ -185,7 +184,7 @@ class Module(BeginStatement, HasAttributes, stmt.analyze() if content: - print 'Not analyzed content:',content + self.show_message('Not analyzed content: %s' % content) return @@ -208,8 +207,8 @@ class PythonModule(BeginStatement, HasImplicitStmt, HasUseStmt): return [Interface, Function, Subroutine, Module] def process_item(self): - name = self.item.get_line().replace(' ','')[len(self.blocktype):].strip() - self.name = name + self.name = self.item.get_line().replace(' ','')\ + [len(self.blocktype):].strip() return BeginStatement.process_item(self) # Program @@ -268,7 +267,9 @@ class EndInterface(EndStatement): match = re.compile(r'end\s*interface\s*\w*\Z', re.I).match blocktype = 'interface' -class Interface(BeginStatement, HasImplicitStmt, HasUseStmt): +class Interface(BeginStatement, HasImplicitStmt, HasUseStmt, + HasModuleProcedures + ): """ INTERFACE [<generic-spec>] | ABSTRACT INTERFACE END INTERFACE [<generic-spec>] @@ -311,8 +312,8 @@ class Interface(BeginStatement, HasImplicitStmt, HasUseStmt): class SubProgramStatement(BeginStatement, ProgramBlock, HasImplicitStmt, HasAttributes, - HasUseStmt, HasVariables, HasTypeDecls, - HasExternal): + HasUseStmt, HasVariables, HasTypeDecls + ): """ [ <prefix> ] <FUNCTION|SUBROUTINE> <name> [ ( <args> ) ] [ <suffix> ] """ @@ -399,20 +400,12 @@ class SubProgramStatement(BeginStatement, ProgramBlock, 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 + self.show_message('Not analyzed content: %s' % content) return @@ -618,7 +611,7 @@ class If(BeginStatement): i = line.find(')') expr = line[1:i].strip() line = line[i+1:].strip() - if line=='then': + if line.lower()=='then': self.isvalid = False return self.expr = expr[1:-1] diff --git a/numpy/f2py/lib/parsefortran.py b/numpy/f2py/lib/parsefortran.py index 314bbfa24..00b53f0b0 100644 --- a/numpy/f2py/lib/parsefortran.py +++ b/numpy/f2py/lib/parsefortran.py @@ -21,8 +21,19 @@ from utils import AnalyzeError class FortranParser: + cache = {} + def __init__(self, reader): self.reader = reader + if self.cache.has_key(reader.id): + parser = self.cache[reader.id] + self.block = parser.block + self.is_analyzed = parser.is_analyzed + self.block.show_message('using cached %s' % (reader.id)) + else: + self.cache[reader.id] = self + self.block = None + self.is_analyzed = False return def get_item(self): @@ -37,6 +48,8 @@ class FortranParser: return def parse(self): + if self.block is not None: + return try: block = self.block = BeginSource(self) except KeyboardInterrupt: @@ -55,12 +68,19 @@ class FortranParser: return def analyze(self): + if self.is_analyzed: + return + if self.block is None: + self.reader.show_message('Nothing to analyze.') + return + try: self.block.analyze() except AnalyzeError: pass except: raise + self.is_analyzed = True return def test_pyf(): diff --git a/numpy/f2py/lib/readfortran.py b/numpy/f2py/lib/readfortran.py index add45e71c..b23a7368f 100644 --- a/numpy/f2py/lib/readfortran.py +++ b/numpy/f2py/lib/readfortran.py @@ -379,10 +379,11 @@ class FortranReaderBase: def format_message(self, kind, message, startlineno, endlineno, startcolno=0, endcolno=-1): - r = ['%s while processing %s (mode=%r)..' % (kind, self.source, self.mode)] - for i in range(max(1,startlineno-3),startlineno): + back_index = {'warning':2,'error':3,'info':0}.get(kind.lower(),3) + r = ['%s while processing %r (mode=%r)..' % (kind, self.id, self.mode)] + for i in range(max(1,startlineno-back_index),startlineno): r.append('%5d:%s' % (i,self.source_lines[i-1])) - for i in range(startlineno,min(endlineno+3,len(self.source_lines))+1): + for i in range(startlineno,min(endlineno+back_index,len(self.source_lines))+1): if i==0 and not self.source_lines: break linenostr = '%5d:' % (i) @@ -695,6 +696,7 @@ class FortranFileReader(FortranReaderBase): def __init__(self, filename, include_dirs = None): isfree, isstrict = get_source_info(filename) + self.id = filename self.file = open(filename,'r') FortranReaderBase.__init__(self, self.file, isfree, isstrict) if include_dirs is None: @@ -709,6 +711,7 @@ class FortranFileReader(FortranReaderBase): class FortranStringReader(FortranReaderBase): def __init__(self, string, isfree, isstrict): + self.id = 'string-'+str(id(string)) source = StringIO(string) FortranReaderBase.__init__(self, source, isfree, isstrict) diff --git a/numpy/f2py/lib/statements.py b/numpy/f2py/lib/statements.py index 5a24f2d84..b3bd34e51 100644 --- a/numpy/f2py/lib/statements.py +++ b/numpy/f2py/lib/statements.py @@ -3,29 +3,34 @@ import re import sys from base_classes import Statement, Variable -from expression import Expression +#from expression import Expression # Auxiliary tools from utils import split_comma, specs_split_comma, AnalyzeError, ParseError,\ - get_module_file, parse_bind, parse_result - -is_name = re.compile(r'\w+\Z').match + get_module_file, parse_bind, parse_result, is_name class StatementWithNamelist(Statement): """ <statement> [ :: ] <name-list> """ def process_item(self): - assert not self.item.has_map() + if self.item.has_map(): + self.isvalid = False + return if hasattr(self,'stmtname'): clsname = self.stmtname else: - clsname = self.__class__.__name__.lower() + clsname = self.__class__.__name__ line = self.item.get_line()[len(clsname):].lstrip() if line.startswith('::'): line = line[2:].lstrip() - self.items = [s.strip() for s in line.split(',')] + self.items = items = [] + for item in split_comma(line): + if not is_name(item): + self.isvalid = False + return + items.append(item) return def __str__(self): if hasattr(self,'stmtname'): @@ -74,6 +79,8 @@ class GeneralAssignment(Statement): return self.get_indent_tab() + '%s %s %s' \ % (self.variable, self.sign, self.expr) + def analyze(self): return + class Assignment(GeneralAssignment): pass @@ -95,6 +102,7 @@ class Assign(Statement): def __str__(self): return self.get_indent_tab() + 'ASSIGN %s TO %s' \ % (self.items[0], self.items[1]) + def analyze(self): return class Call(Statement): """Call statement class @@ -167,6 +175,7 @@ class Goto(Statement): def __str__(self): return self.get_indent_tab() + 'GO TO %s' % (self.label) + def analyze(self): return class ComputedGoto(Statement): """ @@ -186,6 +195,7 @@ class ComputedGoto(Statement): def __str__(self): return self.get_indent_tab() + 'GO TO (%s) %s' \ % (', '.join(self.items), self.expr) + def analyze(self): return class AssignedGoto(Statement): """ @@ -212,6 +222,7 @@ class AssignedGoto(Statement): return tab + 'GO TO %s (%s)' \ % (self.varname, ', '.join(self.items)) return tab + 'GO TO %s' % (self.varname) + def analyze(self): return class Continue(Statement): """ @@ -290,6 +301,7 @@ class Print(Statement): def __str__(self): return self.get_indent_tab() + 'PRINT %s' \ % (', '.join([self.format]+self.items)) + def analyze(self): return class Read(Statement): """ @@ -315,6 +327,7 @@ Read1: READ <format> [, <input-item-list>] self.__class__ = Read1 self.process_item() return + def analyze(self): return class Read0(Read): @@ -365,7 +378,7 @@ class Write(Statement): if self.items: s += ' ' + ', '.join(self.items) return s - + def analyze(self): return class Flush(Statement): @@ -394,6 +407,7 @@ class Flush(Statement): def __str__(self): tab = self.get_indent_tab() return tab + 'FLUSH (%s)' % (', '.join(self.specs)) + def analyze(self): return class Wait(Statement): """ @@ -415,6 +429,7 @@ class Wait(Statement): def __str__(self): tab = self.get_indent_tab() return tab + 'WAIT (%s)' % (', '.join(self.specs)) + def analyze(self): return class Contains(Statement): """ @@ -450,7 +465,7 @@ class Allocate(Statement): if stmt is not None and stmt.isvalid: spec = stmt else: - print 'TODO: unparsed type-spec',`spec` + self.warning('TODO: unparsed type-spec' + `spec`) line2 = line2[i+2:].lstrip() else: spec = None @@ -464,6 +479,7 @@ class Allocate(Statement): t = self.spec.tostr() + ' :: ' return self.get_indent_tab() \ + 'ALLOCATE (%s%s)' % (t,', '.join(self.items)) + def analyze(self): return class Deallocate(Statement): """ @@ -481,7 +497,8 @@ class Deallocate(Statement): return def __str__(self): return self.get_indent_tab() \ + 'DEALLOCATE (%s)' % (', '.join(self.items)) - + def analyze(self): return + class ModuleProcedure(Statement): """ [ MODULE ] PROCEDURE <procedure-name-list> @@ -503,6 +520,11 @@ class ModuleProcedure(Statement): tab = self.get_indent_tab() return tab + 'MODULE PROCEDURE %s' % (', '.join(self.items)) + def analyze(self): + module_procedures = self.parent.a.module_procedures + module_procedures.extend(self.items) + return + class Access(Statement): """ <access-spec> [ [::] <access-id-list>] @@ -532,12 +554,9 @@ class Access(Statement): 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]) + for name in self.items: + var = self.get_variable(name) + var.update(clsname) else: self.parent.a.attributes.append(clsname) return @@ -564,6 +583,7 @@ class Close(Statement): def __str__(self): tab = self.get_indent_tab() return tab + 'CLOSE (%s)' % (', '.join(self.specs)) + def analyze(self): return class Cycle(Statement): """ @@ -577,6 +597,7 @@ class Cycle(Statement): if self.name: return self.get_indent_tab() + 'CYCLE ' + self.name return self.get_indent_tab() + 'CYCLE' + def analyze(self): return class FilePositioningStatement(Statement): """ @@ -608,6 +629,7 @@ class FilePositioningStatement(Statement): def __str__(self): clsname = self.__class__.__name__.upper() return self.get_indent_tab() + clsname + ' (%s)' % (', '.join(self.specs)) + def analyze(self): return class Backspace(FilePositioningStatement): pass @@ -629,6 +651,7 @@ class Open(Statement): return def __str__(self): return self.get_indent_tab() + 'OPEN (%s)' % (', '.join(self.specs)) + def analyze(self): return class Format(Statement): """ @@ -659,18 +682,15 @@ class Format(Statement): item = self.item if not item.label: # R1001: - message = self.reader.format_message(\ - 'WARNING', - 'R1001: FORMAT statement must be labeled but got %r.' \ - % (item.label), - item.span[0],item.span[1]) - self.show_message(message) + self.warning('R1001: FORMAT statement must be labeled but got %r.' \ + % (item.label)) line = item.get_line()[6:].lstrip() assert line[0]+line[-1]=='()',`line` self.specs = split_comma(line[1:-1], item) return def __str__(self): return self.get_indent_tab() + 'FORMAT (%s)' % (', '.join(self.specs)) + def analyze(self): return class Save(Statement): """ @@ -708,6 +728,7 @@ class Save(Statement): if not self.items: return tab + 'SAVE' return tab + 'SAVE %s' % (', '.join(self.items)) + def analyze(self): return class Data(Statement): """ @@ -755,6 +776,7 @@ class Data(Statement): for o,v in self.stmts: l.append('%s / %s /' %(', '.join(o),', '.join(v))) return tab + 'DATA ' + ' '.join(l) + def analyze(self): return class Nullify(Statement): """ @@ -768,6 +790,7 @@ class Nullify(Statement): return def __str__(self): return self.get_indent_tab() + 'NULLIFY (%s)' % (', '.join(self.items)) + def analyze(self): return class Use(Statement): """ @@ -830,11 +853,10 @@ class Use(Statement): fn = get_module_file(self.name, d) if fn is not None: break - - if fn is not None: + if 1 and fn is not None: from readfortran import FortranFileReader from parsefortran import FortranParser - self.show_message('Processing %r (parent file=%r)' % (fn, self.reader.name)) + self.info('looking module information from %r' % (fn)) reader = FortranFileReader(fn) parser = FortranParser(reader) parser.parse() @@ -843,15 +865,7 @@ class Use(Statement): 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 - + self.warning('no information about the use module %r' % (self.name)) return class Exit(Statement): @@ -866,6 +880,7 @@ class Exit(Statement): if self.name: return self.get_indent_tab() + 'EXIT ' + self.name return self.get_indent_tab() + 'EXIT' + def analyze(self): return class Parameter(Statement): """ @@ -879,6 +894,16 @@ class Parameter(Statement): return def __str__(self): return self.get_indent_tab() + 'PARAMETER (%s)' % (', '.join(self.items)) + def analyze(self): + for item in self.items: + i = item.find('=') + assert i!=-1,`item` + name = item[:i].rstrip() + value = item[i+1:].lstrip() + var = self.get_variable(name) + var.update('parameter') + var.set_init(value) + return class Equivalence(Statement): """ @@ -898,6 +923,7 @@ class Equivalence(Statement): return def __str__(self): return self.get_indent_tab() + 'EQUIVALENCE %s' % (', '.join(self.items)) + def analyze(self): return class Dimension(Statement): """ @@ -913,6 +939,15 @@ class Dimension(Statement): return def __str__(self): return self.get_indent_tab() + 'DIMENSION %s' % (', '.join(self.items)) + def analyze(self): + for line in self.items: + i = line.find('(') + assert i!=-1 and line.endswith(')'),`line` + name = line[:i].rstrip() + array_spec = split_comma(line[i+1:-1].strip(), self.item) + var = self.get_variable(name) + var.set_bounds(array_spec) + return class Target(Statement): """ @@ -928,6 +963,17 @@ class Target(Statement): return def __str__(self): return self.get_indent_tab() + 'TARGET %s' % (', '.join(self.items)) + def analyze(self): + for line in self.items: + i = line.find('(') + assert i!=-1 and line.endswith(')'),`line` + name = line[:i].rstrip() + array_spec = split_comma(line[i+1:-1].strip(), self.item) + var = self.get_variable(name) + var.set_bounds(array_spec) + var.update('target') + return + class Pointer(Statement): """ @@ -945,25 +991,53 @@ class Pointer(Statement): return def __str__(self): return self.get_indent_tab() + 'POINTER %s' % (', '.join(self.items)) + def analyze(self): + for line in self.items: + i = line.find('(') + if i==-1: + name = line + array_spec = None + else: + assert line.endswith(')'),`line` + name = line[:i].rstrip() + array_spec = split_comma(line[i+1:-1].strip(), self.item) + var = self.get_variable(name) + var.set_bounds(array_spec) + var.update('pointer') + return class Protected(StatementWithNamelist): """ PROTECTED [ :: ] <entity-name-list> """ match = re.compile(r'protected\b',re.I).match - + def analyze(self): + for name in self.items: + var = self.get_variable(name) + var.update('protected') + return class Volatile(StatementWithNamelist): """ - Volatile [ :: ] <object-name-list> + VOLATILE [ :: ] <object-name-list> """ match = re.compile(r'volatile\b',re.I).match + def analyze(self): + for name in self.items: + var = self.get_variable(name) + var.update('volatile') + return class Value(StatementWithNamelist): """ VALUE [ :: ] <dummy-arg-name-list> """ match = re.compile(r'value\b',re.I).match + def analyze(self): + for name in self.items: + var = self.get_variable(name) + var.update('value') + return class ArithmeticIf(Statement): """ @@ -982,12 +1056,18 @@ class ArithmeticIf(Statement): def __str__(self): return self.get_indent_tab() + 'IF (%s) %s' \ % (self.expr,', '.join(self.labels)) + def analyze(self): return class Intrinsic(StatementWithNamelist): """ INTRINSIC [ :: ] <intrinsic-procedure-name-list> """ match = re.compile(r'intrinsic\b',re.I).match + def analyze(self): + for name in self.items: + var = self.get_variable(name) + var.update('intrinsic') + return class Inquire(Statement): """ @@ -1013,6 +1093,7 @@ class Inquire(Statement): % (', '.join(self.specs), ', '.join(self.items)) return self.get_indent_tab() + 'INQUIRE (%s)' \ % (', '.join(self.specs)) + def analyze(self): return class Sequence(Statement): """ @@ -1022,12 +1103,21 @@ class Sequence(Statement): def process_item(self): return def __str__(self): return self.get_indent_tab() + 'SEQUENCE' + def analyze(self): + self.parent.a.attributes.append('SEQUENCE') + return class External(StatementWithNamelist): """ EXTERNAL [ :: ] <external-name-list> """ match = re.compile(r'external\b', re.I).match + def analyze(self): + for name in self.items: + var = self.get_variable(name) + var.update('external') + return + class Namelist(Statement): """ @@ -1107,6 +1197,21 @@ class Common(Statement): l.append(s) tab = self.get_indent_tab() return tab + 'COMMON ' + ' '.join(l) + def analyze(self): + for cname, items in self.items: + for item in items: + i = item.find('(') + if i!=-1: + assert item.endswith(')'),`item` + name = item[:i].rstrip() + shape = split_comma(item[i+1:-1].strip(), self.item) + else: + name = item + shape = None + var = self.get_variable(name) + if shape is not None: + var.set_bounds(shape) + return class Optional(StatementWithNamelist): """ @@ -1114,6 +1219,11 @@ class Optional(StatementWithNamelist): <dummy-arg-name> = <name> """ match = re.compile(r'optional\b',re.I).match + def analyze(self): + for name in self.items: + var = self.get_variable(name) + var.update('optional') + return class Intent(Statement): """ @@ -1128,7 +1238,7 @@ class Intent(Statement): def process_item(self): line = self.item.get_line()[6:].lstrip() i = line.find(')') - self.specs = split_comma(line[1:i], self.item) + self.specs = specs_split_comma(line[1:i], self.item, upper=True) line = line[i+1:].lstrip() if line.startswith('::'): line = line[2:].lstrip() @@ -1141,6 +1251,12 @@ class Intent(Statement): def __str__(self): return self.get_indent_tab() + 'INTENT (%s) %s' \ % (', '.join(self.specs), ', '.join(self.items)) + def analyze(self): + for name in self.items: + var = self.get_variable(name) + var.set_intent(self.specs) + return + class Entry(Statement): """ @@ -1252,6 +1368,7 @@ class Forall(Statement): s += ', ' + self.mask return tab + 'FORALL (%s) %s' % \ (s, str(self.content[0]).lstrip()) + def analyze(self): return ForallStmt = Forall @@ -1360,12 +1477,33 @@ class Allocatable(Statement): return def __str__(self): return self.get_indent_tab() + 'ALLOCATABLE ' + ', '.join(self.items) + def analyze(self): + for line in self.items: + i = line.find('(') + if i==-1: + name = line + array_spec = None + else: + assert line.endswith(')') + name = line[:i].rstrip() + array_spec = split_comma(line[i+1:-1], self.item) + var = self.get_variable(name) + var.update('allocatable') + if array_spec is not None: + var.set_bounds(array_spec) + return class Asynchronous(StatementWithNamelist): """ ASYNCHRONOUS [ :: ] <object-name-list> """ match = re.compile(r'asynchronous\b',re.I).match + def analyze(self): + for name in self.items: + var = self.get_variable(name) + var.update('asynchronous') + return + class Bind(Statement): """ @@ -1404,12 +1542,8 @@ class Else(Statement): self.name = item.get_line()[4:].strip() parent_name = getattr(self.parent,'name','') if self.name and self.name!=parent_name: - message = self.reader.format_message(\ - 'WARNING', - 'expected if-construct-name %r but got %r, skipping.'\ - % (parent_name, self.name), - item.span[0],item.span[1]) - print >> sys.stderr, message + self.warning('expected if-construct-name %r but got %r, skipping.'\ + % (parent_name, self.name)) self.isvalid = False return @@ -1418,6 +1552,8 @@ class Else(Statement): return self.get_indent_tab(deindent=True) + 'ELSE ' + self.name return self.get_indent_tab(deindent=True) + 'ELSE' + def analyze(self): return + class ElseIf(Statement): """ ELSE IF ( <scalar-logical-expr> ) THEN [ <if-construct-name> ] @@ -1433,12 +1569,8 @@ class ElseIf(Statement): self.name = line[i+1:].lstrip()[4:].strip() parent_name = getattr(self.parent,'name','') if self.name and self.name!=parent_name: - message = self.reader.format_message(\ - 'WARNING', - 'expected if-construct-name %r but got %r, skipping.'\ - % (parent_name, self.name), - item.span[0],item.span[1]) - self.show_message(message) + self.warning('expected if-construct-name %r but got %r, skipping.'\ + % (parent_name, self.name)) self.isvalid = False return @@ -1449,6 +1581,8 @@ class ElseIf(Statement): return self.get_indent_tab(deindent=True) + 'ELSE IF (%s) THEN%s' \ % (self.expr, s) + def analyze(self): return + # SelectCase construct statements class Case(Statement): @@ -1483,12 +1617,8 @@ class Case(Statement): self.name = line parent_name = getattr(self.parent, 'name', '') if self.name and self.name!=parent_name: - message = self.reader.format_message(\ - 'WARNING', - 'expected case-construct-name %r but got %r, skipping.'\ - % (parent_name, self.name), - self.item.span[0],self.item.span[1]) - self.show_message(message) + self.warning('expected case-construct-name %r but got %r, skipping.'\ + % (parent_name, self.name)) self.isvalid = False return @@ -1505,6 +1635,7 @@ class Case(Statement): if self.name: s += ' ' + self.name return s + def analyze(self): return # Where construct statements @@ -1531,6 +1662,7 @@ class Where(Statement): def __str__(self): tab = self.get_indent_tab() return tab + 'WHERE ( %s ) %s' % (self.expr, str(self.content[0]).lstrip()) + def analyze(self): return WhereStmt = Where @@ -1551,12 +1683,8 @@ class ElseWhere(Statement): self.name = line parent_name = getattr(self.parent,'name','') if self.name and not self.name==parent_name: - message = self.reader.format_message(\ - 'WARNING', - 'expected where-construct-name %r but got %r, skipping.'\ - % (parent_name, self.name), - self.item.span[0],self.item.span[1]) - print >> sys.stderr, message + self.warning('expected where-construct-name %r but got %r, skipping.'\ + % (parent_name, self.name)) self.isvalid = False return @@ -1568,6 +1696,7 @@ class ElseWhere(Statement): if self.name: s += ' ' + self.name return tab + s + def analyze(self): return # Enum construct statements @@ -1685,3 +1814,5 @@ class Pause(Statement): if self.value: return self.get_indent_tab() + 'PAUSE ' + self.value return self.get_indent_tab() + 'PAUSE' + def analyze(self): return + diff --git a/numpy/f2py/lib/typedecl_statements.py b/numpy/f2py/lib/typedecl_statements.py index dba912743..63b9a346e 100644 --- a/numpy/f2py/lib/typedecl_statements.py +++ b/numpy/f2py/lib/typedecl_statements.py @@ -3,7 +3,7 @@ import re import string from base_classes import Statement, BeginStatement, EndStatement,\ AttributeHolder, Variable -from utils import split_comma, AnalyzeError +from utils import split_comma, AnalyzeError, name_re, is_entity_decl # Intrinsic type specification statements @@ -136,6 +136,11 @@ class TypeDeclarationStatement(Statement): else: self.attrspec = split_comma(line[:i].rstrip(), self.item) self.entity_decls = split_comma(line[i+2:].lstrip(), self.item) + for entity in self.entity_decls: + if not is_entity_decl(entity): + self.isvalid = False + return + if isinstance(self.parent, Function) \ and self.parent.name in self.entity_decls: assert self.parent.typedecl is None,`self.parent.typedecl` @@ -246,15 +251,51 @@ class TypeDeclarationStatement(Statement): return variables = self.parent.a.variables typedecl = self.astypedecl() - for name in self.entity_decls: + for item in self.entity_decls: + name, array_spec, char_length, value = self._parse_entity(item) if not variables.has_key(name): variables[name] = var = Variable(self, name) else: var = variables[name] - var.set_type(typedecl) + var.add_parent(self) + if char_length: + var.set_length(char_length) + else: + var.set_type(typedecl) var.update(self.attrspec) + if array_spec: + var.set_bounds(array_spec) + if value: + var.set_init(value) return + def _parse_entity(self, line): + m = name_re(line) + assert m,`line,self.item,self.__class__.__name__` + name = line[:m.end()] + line = line[m.end():].lstrip() + array_spec = None + item = self.item.copy(line) + line = item.get_line() + if line.startswith('('): + i = line.find(')') + assert i!=-1,`line` + array_spec = split_comma(line[1:i].strip(), item) + line = line[i+1:].lstrip() + char_length = None + if line.startswith('*'): + i = line.find('=') + if i==-1: + char_length = item.apply_map(line[1:].lstrip()) + line = '' + else: + char_length = item.apply_map(line[1:i].strip()) + line = line[i:] + value = None + if line.startswith('='): + value = item.apply_map(line[1:].lstrip()) + return name, array_spec, char_length, value + class Integer(TypeDeclarationStatement): match = re.compile(r'integer\b',re.I).match diff --git a/numpy/f2py/lib/utils.py b/numpy/f2py/lib/utils.py index 7a501144e..7a194c9c7 100644 --- a/numpy/f2py/lib/utils.py +++ b/numpy/f2py/lib/utils.py @@ -9,6 +9,9 @@ class AnalyzeError(Exception): pass is_name = re.compile(r'^[a-z_]\w*$',re.I).match +name_re = re.compile(r'[a-z_]\w*',re.I).match +is_entity_decl = re.compile(r'^[a-z_]\w*',re.I).match +is_int_literal_constant = re.compile(r'^\d+(_\w+|)$').match def split_comma(line, item = None, comma=','): items = [] @@ -26,7 +29,7 @@ def split_comma(line, item = None, comma=','): items.append(s) return items -def specs_split_comma(line, item = None): +def specs_split_comma(line, item = None, upper=False): specs0 = split_comma(line, item) specs = [] for spec in specs0: @@ -36,6 +39,8 @@ def specs_split_comma(line, item = None): v = spec[i+1:].strip() specs.append('%s = %s' % (kw, v)) else: + if upper: + spec = spec.upper() specs.append(spec) return specs @@ -51,8 +56,7 @@ def parse_bind(line, item = None): 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() + for a in specs_split_comma(newline[1:i].strip(), newitem, upper=True): args.append(a) rest = newline[i+1:].lstrip() if item is not None: |