diff options
author | Pearu Peterson <pearu.peterson@gmail.com> | 2006-06-29 09:33:15 +0000 |
---|---|---|
committer | Pearu Peterson <pearu.peterson@gmail.com> | 2006-06-29 09:33:15 +0000 |
commit | 109772b31ff4c3e755fc21eb2ef3c0db267002ef (patch) | |
tree | f7b4b3c7dfb52bc871f4c0f872378aa71abdd6af /numpy | |
parent | 9913769d0719b6cfca1b3d51baee8421ba7cf288 (diff) | |
download | numpy-109772b31ff4c3e755fc21eb2ef3c0db267002ef.tar.gz |
Finished impl. Fortran 77-2003 parse pattern classes.
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/f2py/lib/base_classes.py | 152 | ||||
-rw-r--r-- | numpy/f2py/lib/block_statements.py | 388 | ||||
-rw-r--r-- | numpy/f2py/lib/parsefortran.py | 21 | ||||
-rw-r--r-- | numpy/f2py/lib/readfortran.py | 190 | ||||
-rw-r--r-- | numpy/f2py/lib/sourceinfo.py | 9 | ||||
-rw-r--r-- | numpy/f2py/lib/splitline.py | 6 | ||||
-rw-r--r-- | numpy/f2py/lib/statements.py | 596 | ||||
-rw-r--r-- | numpy/f2py/lib/typedecl_statements.py | 77 |
8 files changed, 1158 insertions, 281 deletions
diff --git a/numpy/f2py/lib/base_classes.py b/numpy/f2py/lib/base_classes.py index 4f588ff32..b5be7d00a 100644 --- a/numpy/f2py/lib/base_classes.py +++ b/numpy/f2py/lib/base_classes.py @@ -20,8 +20,10 @@ class Statement: self.reader = parent.reader self.item = item - # If statement instance is constructed by error, set isvalid to False + # when a statement instance is constructed by error, set isvalid to False self.isvalid = True + # when a statement should be ignored, set ignore to True + self.ignore = False self.process_item() @@ -48,6 +50,11 @@ class Statement: tab = c + s + colon + tab return tab + def show_message(self, message): + print >> sys.stderr, message + sys.stderr.flush() + return + class BeginStatement(Statement): """ <blocktype> <name> @@ -82,14 +89,6 @@ class BeginStatement(Statement): Statement.__init__(self, parent, item) return - def process_item(self): - """ Process the line - """ - item = self.item - if item is None: return - self.fill() - return - def tostr(self): return self.blocktype.upper() + ' '+ self.name @@ -99,6 +98,41 @@ class BeginStatement(Statement): l.append(str(c)) return '\n'.join(l) + def process_item(self): + """ Process the line + """ + item = self.item + if item is None: return + self.fill() + return + + def fill(self, end_flag = False): + """ + Fills blocks content until the end of block statement. + """ + + mode = self.reader.mode + classes = self.get_classes() + self.classes = [cls for cls in classes if mode in cls.modes] + self.pyf_classes = [cls for cls in classes if 'pyf' in cls.modes] + + item = self.get_item() + while item is not None: + if isinstance(item, Line): + if self.process_subitem(item): + end_flag = True + break + 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) + return + def process_subitem(self, item): """ Check is item is blocks start statement, if it is, read the block. @@ -115,15 +149,77 @@ class BeginStatement(Statement): self.content.append(stmt) return True + if item.is_f2py_directive: + classes = self.pyf_classes + else: + classes = self.classes + # Look for statement match - for cls in self.classes: + for cls in classes: if cls.match(line): stmt = cls(self, item) if stmt.isvalid: - self.content.append(stmt) + if not stmt.ignore: + self.content.append(stmt) return False + # item may be cloned that changes the items line: + line = item.get_line() + + # Check if f77 code contains inline comments or other f90 + # constructs that got undetected by get_source_info. + if item.reader.isfix77: + i = line.find('!') + if i != -1: + message = item.reader.format_message(\ + 'WARNING', + 'no parse pattern found for "%s" in %r block'\ + ' maybe due to inline comment.'\ + ' Trying to remove the comment.'\ + % (item.get_line(),self.__class__.__name__), + item.span[0], item.span[1]) + # .. but at the expense of loosing the comment. + self.show_message(message) + newitem = item.copy(line[:i].rstrip()) + return self.process_subitem(newitem) + + # try fix90 statement classes + f77_classes = self.classes + classes = [] + for cls in self.get_classes(): + if 'fix90' in cls.modes and cls not in f77_classes: + classes.append(cls) + if classes: + message = item.reader.format_message(\ + 'WARNING', + 'no parse pattern found for "%s" in %r block'\ + ' maybe due to strict f77 mode.'\ + ' Trying f90 fix mode patterns..'\ + % (item.get_line(),self.__class__.__name__), + item.span[0], item.span[1]) + self.show_message(message) + + item.reader.set_mode(False, False) + self.classes = classes + + r = BeginStatement.process_subitem(self, item) + if r is None: + # restore f77 fix mode + self.classes = f77_classes + item.reader.set_mode(False, True) + else: + message = item.reader.format_message(\ + 'INFORMATION', + 'The f90 fix mode resolved the parse pattern issue.'\ + ' Setting reader to f90 fix mode.', + item.span[0], item.span[1]) + self.show_message(message) + # set f90 fix mode + self.classes = f77_classes + classes + self.reader.set_mode(False, False) + return r + self.handle_unknown_item(item) - return False + return def handle_unknown_item(self, item): message = item.reader.format_message(\ @@ -131,37 +227,11 @@ class BeginStatement(Statement): 'no parse pattern found for "%s" in %r block.'\ % (item.get_line(),self.__class__.__name__), item.span[0], item.span[1]) - print >> sys.stderr, message - sys.stderr.flush() + self.show_message(message) self.content.append(item) + #sys.exit() return - def fill(self, end_flag = False): - """ - Fills blocks content until the end of block statement. - """ - - mode = self.reader.mode - classes = self.get_classes() - self.classes = [cls for cls in classes if mode in cls.modes] - - item = self.get_item() - while item is not None: - if isinstance(item, Line): - if self.process_subitem(item): - end_flag = True - break - 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]) - print >> sys.stderr, message - sys.stderr.flush() - return class EndStatement(Statement): """ @@ -195,7 +265,7 @@ class EndStatement(Statement): 'expected the end of %r block but got end of %r, skipping.'\ % (self.parent.name, line), item.span[0],item.span[1]) - print >> sys.stderr, message + self.show_message(message) 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 92a7f1493..dcc44bc5f 100644 --- a/numpy/f2py/lib/block_statements.py +++ b/numpy/f2py/lib/block_statements.py @@ -118,11 +118,36 @@ class Program(BeginStatement): return specification_part + execution_part + internal_subprogram_part def process_item(self): - name = self.item.get_line().replace(' ','')[len(self.blocktype):].strip() - if name: - self.name = name + if self.item is not None: + name = self.item.get_line().replace(' ','')\ + [len(self.blocktype):].strip() + if name: + self.name = name return BeginStatement.process_item(self) +# BlockData + +class EndBlockData(EndStatement): + """ + END [ BLOCK DATA [ <block-data-name> ] ] + """ + match = re.compile(r'end(\s*block\s*data\s*\w*|)\Z', re.I).match + blocktype = 'blockdata' + +class BlockData(BeginStatement): + """ + BLOCK DATA [ <block-data-name> ] + """ + end_stmt_cls = EndBlockData + match = re.compile(r'block\s*data\s*\w*\Z', re.I).match + + def process_item(self): + self.name = self.item.get_line()[5:].lstrip()[4:].lstrip() + return BeginStatement.process_item(self) + + def get_classes(self): + return specification_part + # Interface class EndInterface(EndStatement): @@ -150,7 +175,7 @@ class Interface(BeginStatement): blocktype = 'interface' def get_classes(self): - return interface_specification + return intrinsic_type_spec + interface_specification def process_item(self): line = self.item.get_line() @@ -170,35 +195,64 @@ class Interface(BeginStatement): # Subroutine -class EndSubroutine(EndStatement): +class SubProgramStatement(BeginStatement): """ - END [SUBROUTINE [name]] + [ <prefix> ] <FUNCTION|SUBROUTINE> <name> [ ( <args> ) ] [ <suffix> ] """ - match = re.compile(r'end(\s*subroutine\s*\w*|)\Z', re.I).match -class Subroutine(BeginStatement): - """ - [prefix] SUBROUTINE <name> [ ( [<dummy-arg-list>] ) [<proc-language-binding-spec>]] - """ - end_stmt_cls = EndSubroutine - match = re.compile(r'[\w\s]*subroutine\s*\w+', re.I).match - - item_re = re.compile(r'(?P<prefix>[\w\s]*)\s*subroutine\s*(?P<name>\w+)', re.I).match def process_item(self): - line = self.item.get_line() - m = self.item_re(line) - self.name = m.group('name') - line = line[m.end():].strip() + clsname = self.__class__.__name__.lower() + item = self.item + line = item.get_line() + m = self.match(line) + i = line.find(clsname) + assert i!=-1,`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('('): - assert line.endswith(')'),`line` - for a in line.split(','): - args.append(a.strip()) + 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() + self.suffix = item.apply_map(line) 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 + return '%s %s(%s) %s' % (s, self.name,', '.join(self.args),self.suffix) + def get_classes(self): - return specification_part + execution_part + internal_subprogram_part + return f2py_stmt + specification_part + execution_part \ + + internal_subprogram_part + +class EndSubroutine(EndStatement): + """ + END [SUBROUTINE [name]] + """ + match = re.compile(r'end(\s*subroutine\s*\w*|)\Z', re.I).match + + +class Subroutine(SubProgramStatement): + """ + [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 # Function @@ -208,35 +262,40 @@ class EndFunction(EndStatement): """ match = re.compile(r'end(\s*function\s*\w*|)\Z', re.I).match -class Function(BeginStatement): +class Function(SubProgramStatement): """ - [prefix] SUBROUTINE <name> [ ( [<dummy-arg-list>] ) [suffix] + [ <prefix> ] FUNCTION <name> ( [<dummy-arg-list>] ) [<suffix>] + <prefix> = <prefix-spec> [ <prefix-spec> ]... + <prefix-spec> = <declaration-type-spec> + | RECURSIVE | PURE | ELEMENTAL """ end_stmt_cls = EndFunction - match = re.compile(r'([\w\s]+(\(\s*\w+\s*\)|)|)\s*function\s*\w+', re.I).match - item_re = re.compile(r'(?P<prefix>([\w\s](\(\s*\w+\s*\)|))*)\s*function\s*(?P<name>\w+)\s*\((?P<args>.*)\)\s*(?P<suffix>.*)\Z', re.I).match + match = re.compile(r'(recursive|pure|elemental|\s)*function\s*\w+', re.I).match +# Handle subprogram prefixes + +class SubprogramPrefix(Statement): + """ + <prefix> <declaration-type-spec> <function|subroutine> ... + """ + match = re.compile(r'(pure|elemental|recursive|\s)+\b',re.I).match def process_item(self): line = self.item.get_line() - m = self.item_re(line) - if m is None: + m = self.match(line) + prefix = line[:m.end()].rstrip() + rest = self.item.get_line()[m.end():].lstrip() + if rest: + self.parent.put_item(self.item.copy(prefix)) + self.item.clone(rest) self.isvalid = False return - self.name = m.group('name') - self.prefix = m.group('prefix').strip() - self.suffix = m.group('suffix').strip() - args = [] - for a in m.group('args').split(','): - args.append(a.strip()) - self.args = args - return BeginStatement.process_item(self) - - def tostr(self): - return '%s FUNCTION %s(%s) %s' % (self.prefix, self.name, - ', '.join(self.args), self.suffix) - - def get_classes(self): - return specification_part + execution_part + internal_subprogram_part + if self.parent.__class__ not in [Function,Subroutine]: + self.isvalid = False + return + prefix = prefix + ' ' + self.parent.prefix + self.parent.prefix = prefix.strip() + self.ignore = True + return # SelectCase @@ -265,7 +324,10 @@ class Select(BeginStatement): # Where class EndWhere(EndStatement): - match = re.compile(r'end\s*\w*\Z',re.I).match + """ + END WHERE [ <where-construct-name> ] + """ + match = re.compile(r'end\s*\where\s*\w*\Z',re.I).match class Where(BeginStatement): @@ -290,9 +352,47 @@ class Where(BeginStatement): WhereConstruct = Where +# Forall + +class EndForall(EndStatement): + """ + END FORALL [ <forall-construct-name> ] + """ + match = re.compile(r'end\s*forall\s*\w*\Z',re.I).match + +class Forall(BeginStatement): + """ + [ <forall-construct-name> : ] FORALL <forall-header> + [ <forall-body-construct> ]... + <forall-body-construct> = <forall-assignment-stmt> + | <where-stmt> + | <where-construct> + | <forall-construct> + | <forall-stmt> + <forall-header> = ( <forall-triplet-spec-list> [ , <scalar-mask-expr> ] ) + <forall-triplet-spec> = <index-name> = <subscript> : <subscript> [ : <stride> ] + <subscript|stride> = <scalar-int-expr> + <forall-assignment-stmt> = <assignment-stmt> | <pointer-assignment-stmt> + """ + end_stmt_cls = EndForall + match = re.compile(r'forarr\s*\(.*\)\Z',re.I).match + name = '' + def process_item(self): + self.specs = self.item.get_line()[6:].lstrip()[1:-1].strip() + return BeginStatement.process_item(self) + def tostr(self): + return 'FORALL (%s)' % (self.specs) + def get_classes(self): + return [Assignment, WhereStmt, WhereConstruct, ForallConstruct, ForallStmt] + +ForallConstruct = Forall + # IfThen class EndIfThen(EndStatement): + """ + END IF [ <if-construct-name> ] + """ match = re.compile(r'end\s*if\s*\w*\Z', re.I).match blocktype = 'if' @@ -344,13 +444,19 @@ class If(BeginStatement): return self.expr = expr[1:-1] - newitem = item.copy(line) + if not line: + newitem = self.get_item() + else: + newitem = item.copy(line) + newline = newitem.get_line() for cls in classes: - if cls.match(line): + if cls.match(newline): stmt = cls(self, newitem) if stmt.isvalid: self.content.append(stmt) return + if not line: + self.put_item(newitem) self.isvalid = False return @@ -368,6 +474,7 @@ class If(BeginStatement): class EndDo(EndStatement): """ + END DO [ <do-construct-name> ] """ match = re.compile(r'end\s*do\s*\w*\Z', re.I).match blocktype = 'do' @@ -410,6 +517,36 @@ class Do(BeginStatement): def get_classes(self): return execution_part_construct +# Associate + +class EndAssociate(EndStatement): + """ + END ASSOCIATE [ <associate-construct-name> ] + """ + match = re.compile(r'end\s*associate\s*\w*\Z',re.I).match + +class Associate(BeginStatement): + """ + [ <associate-construct-name> : ] ASSOCIATE ( <association-list> ) + <block> + + <association> = <associate-name> => <selector> + <selector> = <expr> | <variable> + """ + match = re.compile(r'associate\s*\(.*\)\Z',re.I).match + end_stmt_cls = EndAssociate + + def process_item(self): + line = self.item.get_line()[9:].lstrip() + self.associations = line[1:-1].strip() + return BeginStatement.process_item(self) + def tostr(self): + return 'ASSOCIATE (%s)' % (self.associations) + def get_classes(self): + return execution_part_construct + +# Type + class EndType(EndStatement): """ END TYPE [<type-name>] @@ -467,90 +604,81 @@ class Type(BeginStatement): TypeDecl = Type +# Enum + +class EndEnum(EndStatement): + """ + END ENUM + """ + match = re.compile(r'end\s*enum\Z',re.I).match + blocktype = 'enum' + +class Enum(BeginStatement): + """ + ENUM , BIND(C) + <enumerator-def-stmt> + [ <enumerator-def-stmt> ]... + """ + blocktype = 'enum' + end_stmt_cls = EndEnum + match = re.compile(r'enum\s*,\s*bind\s*\(\s*c\s*\)\Z',re.I).match + def process_item(self): + return BeginStatement.process_item(self) + def get_classes(self): + return [Enumerator] + ################################################### from statements import * from typedecl_statements import * +f2py_stmt = [ThreadSafe, FortranName, Depend, Check, CallStatement, + CallProtoArgument] + access_spec = [Public, Private] interface_specification = [Function, Subroutine, ModuleProcedure ] -module_subprogram_part = [ - Contains, - Function, - Subroutine - ] +module_subprogram_part = [ Contains, Function, Subroutine ] -specification_stmt = [ - # Access, Allocatable, Asynchronous, Bind, - Common, - Data, Dimension, - Equivalence, External, #Intent - Intrinsic, - #Namelist, - Optional, #Pointer, Protected, - Save, #Target, Volatile, Value - ] -intrinsic_type_spec = [ - Integer , Real, DoublePrecision, Complex, DoubleComplex, Character, Logical - ] -declaration_type_spec = intrinsic_type_spec + [ - TypeStmt, - Class +specification_stmt = access_spec + [ Allocatable, Asynchronous, Bind, + Common, Data, Dimension, Equivalence, External, Intent, Intrinsic, + Namelist, Optional, Pointer, Protected, Save, Target, Volatile, + Value ] + +intrinsic_type_spec = [ SubprogramPrefix, Integer , Real, + DoublePrecision, Complex, DoubleComplex, Character, Logical, Byte ] + +declaration_type_spec = intrinsic_type_spec + [ TypeStmt, Class ] + type_declaration_stmt = declaration_type_spec -private_or_sequence = [ - Private, Sequence - ] +private_or_sequence = [ Private, Sequence ] -component_part = declaration_type_spec + [ - #Procedure - ] +component_part = declaration_type_spec + [ ModuleProcedure ] -type_bound_procedure_part = [ - Contains, Private, #Procedure, Generic, Final - ] +proc_binding_stmt = [SpecificBinding, GenericBinding, FinalBinding] + +type_bound_procedure_part = [Contains, Private] + proc_binding_stmt #R214 -action_stmt = [ - Allocate, - Assignment, #PointerAssignment, - Backspace, - Call, - Close, - Continue, - Cycle, - Deallocate, - Endfile, #EndFunction, EndProgram, EndSubroutine, - Exit, - # Flush, Forall, - Goto, If, Inquire, - Nullify, - Open, - Print, Read, - Return, - Rewind, - Stop, #Wait, - WhereStmt, - Write, - ArithmeticIf, - ComputedGoto - ] +action_stmt = [ Allocate, Assignment, Assign, Backspace, Call, Close, + Continue, Cycle, Deallocate, Endfile, Exit, Flush, ForallStmt, + Goto, If, Inquire, Nullify, Open, Print, Read, Return, Rewind, + Stop, Wait, WhereStmt, Write, ArithmeticIf, ComputedGoto, + AssignedGoto, Pause ] +#PointerAssignment,EndFunction, EndProgram, EndSubroutine, + +executable_construct = [ Associate, Do, ForallConstruct, IfThen, + Select, WhereConstruct ] + action_stmt +#Case, see Select + +execution_part_construct = executable_construct + [ Format, Entry, + Data ] -executable_construct = [ - # Associate, Case, - Do, - # Forall, - IfThen, - Select, WhereConstruct - ] + action_stmt -execution_part_construct = executable_construct + [ - Format, #Entry, Data - ] execution_part = execution_part_construct[:] #C201, R208 @@ -559,24 +687,24 @@ for cls in [EndFunction, EndProgram, EndSubroutine]: except ValueError: pass internal_subprogram = [Function, Subroutine] -internal_subprogram_part = [ - Contains, - ] + internal_subprogram - -declaration_construct = [ - TypeDecl, #Entry, Enum, - Format, - Interface, - Parameter, #Procedure, - ] + specification_stmt + type_declaration_stmt # stmt-function-stmt -implicit_part = [ - Implicit, Parameter, Format, #Entry - ] -specification_part = [ - Use, #Import - ] + implicit_part + declaration_construct + +internal_subprogram_part = [ Contains, ] + internal_subprogram + +declaration_construct = [ TypeDecl, Entry, Enum, Format, Interface, + Parameter, ModuleProcedure, ] + specification_stmt + \ + type_declaration_stmt +# stmt-function-stmt + +implicit_part = [ Implicit, Parameter, Format, Entry ] + +specification_part = [ Use, Import ] + implicit_part + \ + declaration_construct + + external_subprogram = [Function, Subroutine] -main_program = [Program] + specification_part + execution_part + internal_subprogram_part + +main_program = [Program] + specification_part + execution_part + \ + internal_subprogram_part + program_unit = main_program + external_subprogram + [Module, - #BlockData - ] + BlockData ] diff --git a/numpy/f2py/lib/parsefortran.py b/numpy/f2py/lib/parsefortran.py index 21eec49fe..99dd2fea4 100644 --- a/numpy/f2py/lib/parsefortran.py +++ b/numpy/f2py/lib/parsefortran.py @@ -88,8 +88,9 @@ module foo hey = 1 end subroutine bar abstract interface - + end interface + end module foo """ reader = FortranStringReader(string, True, False) @@ -99,11 +100,15 @@ end module foo def test_f77(): string = """\ -c program foo + program foo a = 3 end subroutine bar end + pure function foo(a) + end + pure real*4 recursive function bar() + end """ reader = FortranStringReader(string, False, True) parser = FortranParser(reader) @@ -112,6 +117,8 @@ c program foo def simple_main(): import sys + if not sys.argv[1:]: + return parse_all_f() for filename in sys.argv[1:]: reader = FortranFileReader(filename) print yellow_text('Processing '+filename+' (mode=%r)' % (reader.mode)) @@ -130,10 +137,20 @@ def profile_main(): stats.sort_stats('time', 'calls') stats.print_stats(30) +def parse_all_f(): + for filename in open('opt_all_f90.txt'): + filename = filename.strip() + reader = FortranFileReader(filename) + #print yellow_text('Processing '+filename+' (mode=%r)' % (reader.mode)) + + parser = FortranParser(reader) + block = parser.parse() + if __name__ == "__main__": #test_f77() #test_free90() #test_pyf() simple_main() #profile_main() + #parse_all_f() diff --git a/numpy/f2py/lib/readfortran.py b/numpy/f2py/lib/readfortran.py index 26bfa1b0b..b275d220f 100644 --- a/numpy/f2py/lib/readfortran.py +++ b/numpy/f2py/lib/readfortran.py @@ -36,7 +36,10 @@ _cf2py_re = re.compile(r'(?P<indent>\s*)!f2py(?P<rest>.*)',re.I) _is_fix_cont = lambda line: line and len(line)>5 and line[5]!=' ' and line[:5]==5*' ' _is_f90_cont = lambda line: line and '&' in line and line.rstrip()[-1]=='&' _f90label_re = re.compile(r'\s*(?P<label>(\w+\s*:|\d+))\s*(\b|(?=&)|\Z)',re.I) -_is_include_line = re.compile(r'\s*include\s*"[^"]+"\s*\Z',re.I).match +_is_include_line = re.compile(r'\s*include\s*("[^"]+"|\'[^\']+\')\s*\Z',re.I).match +_is_fix_comment = lambda line: line and line[0] in '*cC!' +_hollerith_start_search = re.compile(r'(?P<pre>\A|,\s*)(?P<num>\d+)h',re.I).search +_is_call_stmt = re.compile(r'call\b', re.I).match class FortranReaderError: # TODO: may be derive it from Exception def __init__(self, message): @@ -56,25 +59,69 @@ class Line: self.label = label self.reader = reader self.strline = None + self.is_f2py_directive = linenospan[0] in reader.f2py_comment_lines + + def apply_map(self, line): + if not hasattr(self,'strlinemap'): return line + findall = self.f2py_strmap_findall + str_map = self.strlinemap + keys = findall(line) + for k in keys: + line = line.replace(k, str_map[k]) + return line + def copy(self, line = None, apply_map = False): if line is None: line = self.line - if apply_map and hasattr(self,'strlinemap'): - findall = self.f2py_strmap_findall - str_map = self.strlinemap - keys = findall(line) - for k in keys: - line = line.replace(k, str_map[k]) + if apply_map: + line = self.apply_map(line) return Line(line, self.span, self.label, self.reader) + + def clone(self, line): + self.line = self.apply_map(line) + self.strline = None + return + def __repr__(self): return self.__class__.__name__+'(%r,%s,%r)' \ % (self.line, self.span, self.label) + def isempty(self, ignore_comments=False): return not (self.line.strip() or self.label) + def get_line(self): if self.strline is not None: return self.strline - line, str_map = string_replace_map(self.line, lower=not self.reader.ispyf) + line = self.line + if self.reader.isfix77: + # Handle Hollerith constants by replacing them + # with char-literal-constants. + # H constants may appear only in DATA statements and + # in the argument list of CALL statement. + # Holleriht constants were removed from the Fortran 77 standard. + # The following handling is not perfect but works for simple + # usage cases. + # todo: Handle hollerith constants in DATA statement + if _is_call_stmt(line): + l2 = self.line[4:].lstrip() + i = l2.find('(') + if i != -1 and l2[-1]==')': + substrings = ['call '+l2[:i+1]] + start_search = _hollerith_start_search + l2 = l2[i+1:-1].strip() + m = start_search(l2) + while m: + substrings.append(l2[:m.start()]) + substrings.append(m.group('pre')) + num = int(m.group('num')) + substrings.append("'"+l2[m.end():m.end()+num]+"'") + l2 = l2[m.end()+num:] + m = start_search(l2) + substrings.append(l2) + substrings.append(')') + line = ''.join(substrings) + + line, str_map = string_replace_map(line, lower=not self.reader.ispyf) self.strline = line self.strlinemap = str_map return line @@ -133,6 +180,24 @@ class FortranReaderBase: - free format Fortran 90 code - .pyf signatures - extended free format Fortran 90 syntax """ + + self.linecount = 0 + self.source = source + self.isclosed = False + + self.filo_line = [] + self.fifo_item = [] + self.source_lines = [] + + self.f2py_comment_lines = [] # line numbers that contain f2py directives + + self.reader = None + self.include_dirs = ['.'] + + self.set_mode(isfree, isstrict) + return + + def set_mode(self, isfree, isstrict): self.isfree90 = isfree and not isstrict self.isfix90 = not isfree and not isstrict self.isfix77 = not isfree and isstrict @@ -146,19 +211,7 @@ class FortranReaderBase: elif self.isfix77: mode = 'fix77' else: mode = 'pyf' self.mode = mode - - self.linecount = 0 - self.source = source - self.isclosed = False - - self.filo_line = [] - self.fifo_item = [] - self.source_lines = [] - - self.name = '%s mode=%s' % (source, mode) - - self.reader = None - self.include_dirs = ['.'] + self.name = '%s mode=%s' % (self.source, mode) return def close_source(self): @@ -170,6 +223,7 @@ class FortranReaderBase: def put_single_line(self, line): self.filo_line.append(line) self.linecount -= 1 + return def get_single_line(self): try: @@ -188,7 +242,7 @@ class FortranReaderBase: return None self.linecount += 1 # expand tabs, replace special symbols, get rid of nl characters - line = line.expandtabs().replace('\xa0',' ').rstrip('\n\r\f') + line = line.expandtabs().replace('\xa0',' ').rstrip() self.source_lines.append(line) if not line: return self.get_single_line() @@ -215,6 +269,7 @@ class FortranReaderBase: self.reader = None item = self._next(ignore_comments) if isinstance(item, Line) and _is_include_line(item.line): + reader = item.reader filename = item.line.strip()[7:].lstrip()[1:-1] include_dirs = self.include_dirs[:] path = filename @@ -224,17 +279,19 @@ class FortranReaderBase: break if not os.path.isfile(path): dirs = os.pathsep.join(include_dirs) - message = self.format_message('WARNING', - 'include file not found in %s, ignoring.' % (dirs), - self.linecount, self.linecount) - self.show_message(message, sys.stdout) - return item - message = self.format_message('INFORMATION', + message = reader.format_message(\ + 'WARNING', + 'include file %r not found in %r,'\ + ' ignoring.' % (filename, dirs), + item.span[0], item.span[1]) + reader.show_message(message, sys.stdout) + return self.next(ignore_comments = ignore_comments) + message = reader.format_message('INFORMATION', 'found file %r' % (path), - self.linecount, self.linecount) - self.show_message(message, sys.stdout) + item.span[0], item.span[1]) + reader.show_message(message, sys.stdout) self.reader = FortranFileReader(path, include_dirs = include_dirs) - return self.reader.next() + return self.reader.next(ignore_comments = ignore_comments) return item except StopIteration: raise @@ -260,7 +317,10 @@ class FortranReaderBase: break # else ignore empty lines and comments if not isinstance(item, Comment): - if isinstance(item, Line) and ';' in item.get_line(): + if not self.ispyf and isinstance(item, Line) \ + and not item.is_f2py_directive \ + and ';' in item.get_line(): + # ;-separator not recognized in pyf-mode items = [] for line in item.get_line().split(';'): line = line.strip() @@ -358,11 +418,13 @@ class FortranReaderBase: if line[0] in '*cC!#': if line[1:5].lower() == 'f2py': line = 5*' ' + line[5:] + self.f2py_comment_lines.append(self.linecount) if self.isfix77: return line m = _cf2py_re.match(line) if m: newline = m.group('indent')+5*' '+m.group('rest') + self.f2py_comment_lines.append(self.linecount) assert len(newline)==len(line),`newlinel,line` return newline return line @@ -404,6 +466,7 @@ class FortranReaderBase: if commentline.startswith('!f2py'): # go to next iteration: newline = ''.join(noncomment_items) + commentline[5:] + self.f2py_comment_lines.append(lineno) return self.handle_inline_comment(newline, lineno, quotechar) put_item(self.comment_item(commentline, lineno, lineno)) return ''.join(noncomment_items), newquotechar @@ -447,7 +510,7 @@ class FortranReaderBase: prefix,multilines,suffix,\ startlineno, self.linecount, message) suffix,qc = self.handle_inline_comment(suffix, self.linecount) - # no line continuation allowed in mulitline suffix + # no line continuation allowed in multiline suffix if qc is not None: message = self.format_message(\ 'ASSERTION FAILURE(pyf)', @@ -474,19 +537,22 @@ class FortranReaderBase: if line is None: return startlineno = self.linecount line = self.handle_cf2py_start(line) + is_f2py_directive = startlineno in self.f2py_comment_lines + label = None if self.ispyf: # handle multilines for mlstr in ['"""',"'''"]: r = self.handle_multilines(line, startlineno, mlstr) if r: return r + if self.isfix: label = line[:5].strip().lower() if label.endswith(':'): label = label[:-1].strip() if not line.strip(): # empty line return self.line_item(line[6:],startlineno,self.linecount,label) - if line[0] in '*cC!': + if _is_fix_comment(line): return self.comment_item(line, startlineno, startlineno) for i in range(5): if line[i] not in _spacedigits: @@ -497,14 +563,13 @@ class FortranReaderBase: message = self.format_warning_message(\ message,startlineno, self.linecount) self.show_message(message, sys.stderr) - self.isfree = True - self.isfix90 = False - self.isfree90 = True + self.set_mode(True, False) else: return self.line_item(line[6:], startlineno, self.linecount, label, self.format_error_message(\ message, startlineno, self.linecount)) - if self.isfix77: + + if self.isfix77 and not is_f2py_directive: lines = [line[6:72]] while _is_fix_cont(self.get_next_line()): # handle fix format line continuations for F77 code @@ -513,18 +578,26 @@ class FortranReaderBase: return self.line_item(''.join(lines),startlineno,self.linecount,label) handle_inline_comment = self.handle_inline_comment - - if self.isfix90 and _is_fix_cont(self.get_next_line()): + + if self.isfix90 and not is_f2py_directive: # handle inline comment newline,qc = handle_inline_comment(line[6:], startlineno) lines = [newline] - while _is_fix_cont(self.get_next_line()): + next_line = self.get_next_line() + while _is_fix_cont(next_line) or _is_fix_comment(next_line): # handle fix format line continuations for F90 code. # mixing fix format and f90 line continuations is not allowed # nor detected, just eject warnings. - line = get_single_line() - newline,qc = self.handle_inline_comment(line[6:], self.linecount, qc) - lines.append(newline) + line2 = get_single_line() + if _is_fix_comment(line2): + # handle fix format comments inside line continuations + citem = self.comment_item(line2,self.linecount,self.linecount) + self.fifo_item.append(citem) + else: + newline, qc = self.handle_inline_comment(line2[6:], + self.linecount, qc) + lines.append(newline) + next_line = self.get_next_line() # no character continuation should follows now if qc is not None: message = self.format_message(\ @@ -532,16 +605,17 @@ class FortranReaderBase: 'following character continuation: %r, expected None.'\ % (qc), startlineno, self.linecount) self.show_message(message, sys.stderr) - for i in range(len(lines)): - l = lines[i] - if l.rstrip().endswith('&'): - message = self.format_warning_message(\ + if len(lines)>1: + for i in range(len(lines)): + l = lines[i] + if l.rstrip().endswith('&'): + message = self.format_warning_message(\ 'f90 line continuation character `&\' detected'\ ' in fix format code', startlineno + i, startlineno + i, l.rfind('&')+5) - self.show_message(message, sys.stderr) - return self.line_item(''.join(lines),startlineno,self.linecount,label) - + self.show_message(message, sys.stderr) + return self.line_item(''.join(lines),startlineno, + self.linecount,label) start_index = 0 if self.isfix90: start_index = 6 @@ -554,6 +628,7 @@ class FortranReaderBase: if start_index: # fix format code line,qc = handle_inline_comment(line[start_index:], self.linecount,qc) + is_f2py_directive = self.linecount in self.f2py_comment_lines else: line_lstrip = line.lstrip() if lines: @@ -573,6 +648,7 @@ class FortranReaderBase: if not self.ispyf: label = label.lower() line = line[m.end():] line,qc = handle_inline_comment(line, self.linecount, qc) + is_f2py_directive = self.linecount in self.f2py_comment_lines i = line.rfind('&') if i!=-1: @@ -702,10 +778,14 @@ cComment &5 a = 3!f2py.14 ! pi! ! KDMO - write (obj%print_lun, *) ' KDMO : ' - write (obj%print_lun, *) ' COORD = ',coord, ' BIN_WID = ', & + write (obj%print_lun, *) ' KDMO : ' + write (obj%print_lun, *) ' COORD = ',coord, ' BIN_WID = ', & obj%bin_wid,' VEL_DMO = ', obj%vel_dmo end subroutine foo + subroutine + + & foo + end """ reader = FortranStringReader(string_fix90,False, False) for item in reader: @@ -716,8 +796,8 @@ def simple_main(): print 'Processing',filename reader = FortranFileReader(filename) for item in reader: - #print >> sys.stdout, item - #sys.stdout.flush() + print >> sys.stdout, item + sys.stdout.flush() pass def profile_main(): diff --git a/numpy/f2py/lib/sourceinfo.py b/numpy/f2py/lib/sourceinfo.py index a31c99d73..65b90ca21 100644 --- a/numpy/f2py/lib/sourceinfo.py +++ b/numpy/f2py/lib/sourceinfo.py @@ -52,15 +52,12 @@ def is_free_format(file): isfree = True contline = False while n>0 and line: - if line[0]!='!' and line.strip(): + line = line.rstrip() + if line and line[0]!='!': n -= 1 - if line[0]!='\t' and _free_f90_start(line[:5]) or line[-2:-1]=='&': + if line[0]!='\t' and _free_f90_start(line[:5]) or line[-1:]=='&': isfree = True break - #elif line[-2:-1]=='&': - # contline = True - #else: - # contline = False line = f.readline() f.close() return isfree diff --git a/numpy/f2py/lib/splitline.py b/numpy/f2py/lib/splitline.py index c5d6869b0..b95e0d014 100644 --- a/numpy/f2py/lib/splitline.py +++ b/numpy/f2py/lib/splitline.py @@ -28,12 +28,12 @@ def split2(line, lower=False): """ return LineSplitter(line,lower=lower).split2() -_f2py_str_findall = re.compile(r' _F2PY_STRING_CONSTANT_\d+_ ').findall +_f2py_str_findall = re.compile(r"'_F2PY_STRING_CONSTANT_\d+_'").findall def string_replace_map(line, lower=False, _cache={'index':0,'pindex':0}): """ - 1) Replaces string constants with symbol ` _F2PY_STRING_CONSTANT_<index>_ ` + 1) Replaces string constants with symbol `'_F2PY_STRING_CONSTANT_<index>_'` 2) Replaces (expression) with symbol `(F2PY_EXPR_TUPLE_<index>)` Returns a new line and the replacement map. """ @@ -46,7 +46,7 @@ def string_replace_map(line, lower=False, if key is None: _cache['index'] += 1 index = _cache['index'] - key = ' _F2PY_STRING_CONSTANT_%s_ ' % (index) + key = "'_F2PY_STRING_CONSTANT_%s_'" % (index) string_map[key] = item rev_string_map[item] = key items.append(key) diff --git a/numpy/f2py/lib/statements.py b/numpy/f2py/lib/statements.py index 803493aed..be65e6ae4 100644 --- a/numpy/f2py/lib/statements.py +++ b/numpy/f2py/lib/statements.py @@ -4,6 +4,8 @@ import sys from base_classes import Statement +is_name = re.compile(r'\w+\Z').match + # Execution statements class Assignment(Statement): @@ -12,8 +14,6 @@ class Assignment(Statement): <pointer variable> => <expr> """ - #match = re.compile(r'\w(\s*\(\s*[^)]*\)|[\w%]*)*\s*=\>?',re.I).match - #item_re = re.compile(r'(?P<variable>\w(\s*\(\s*[^)]*\)|[\w%]*)*)\s*(?P<sign>=\>?)\s*(?P<expr>.*)\Z',re.I).match match = re.compile(r'\w[^=]*\s*=\>?').match item_re = re.compile(r'(?P<variable>\w[^=]*)\s*(?P<sign>=\>?)\s*(?P<expr>.*)\Z',re.I).match @@ -27,6 +27,22 @@ class Assignment(Statement): def __str__(self): return self.get_indent_tab() + '%s %s %s' \ % (self.variable, self.sign, self.expr) + +class Assign(Statement): + """ + ASSIGN <label> TO <int-variable-name> + """ + modes = ['fix77'] + 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') + self.items = [line[:i].rstrip(),line[i+2:].lstrip()] + return + def __str__(self): + return self.get_indent_tab() + 'ASSIGN %s TO %s' \ + % (self.items[0], self.items[1]) + class Call(Statement): """Call statement class @@ -46,6 +62,10 @@ class Call(Statement): if i==-1: self.designator = line.strip() else: + j = line.find(')') + if j == -1 or len(line)-1 != j: + self.isvalid = False + return self.designator = line[:i].strip() for n in line[i+1:-1].split(','): n = n.strip() @@ -88,8 +108,26 @@ class ComputedGoto(Statement): return self.get_indent_tab() + 'GO TO (%s) %s' \ % (', '.join(self.items), self.expr) +class AssignedGoto(Statement): + """ + GO TO <int-variable-name> [ ( <label> [ , <label> ]... ) ] + """ + modes = ['fix77'] + match = re.compile(r'go\s*to\s*\w+\s*,?\s*\(',re.I).match + def process_item(self): + line = self.item.get_line()[2:].lstrip()[2:].lstrip() + i = line.find('(') + assert line[-1]==')',`line` + varname = line[:i].rstrip() + if varname.endswith(','): + varname = varname[:-1].rstrip() + self.varname = varname + self.items = [s.strip() for s in line[i+1:-1].split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'GO TO %s (%s)' \ + % (self.varname, ', '.join(self.items)) - class Continue(Statement): """ CONTINUE @@ -106,13 +144,12 @@ class Continue(Statement): class Return(Statement): """ - RETURN [scalar-int-expr] + RETURN [ <scalar-int-expr> ] """ match = re.compile(r'return\b',re.I).match def process_item(self): - line = self.item.get_line()[6:].lstrip() - self.expr = line + self.expr = self.item.get_line()[6:].lstrip() return def __str__(self): @@ -120,9 +157,10 @@ class Return(Statement): class Stop(Statement): """ - STOP [stop-code] + STOP [ <stop-code> ] + <stop-code> = <scalar-char-constant> | <1-5-digit> """ - match = re.compile(r'stop\b\s*\w*\s*\Z',re.I).match + match = re.compile(r'stop\s*(\'\w*\'|\d+|)\Z',re.I).match def process_item(self): self.stopcode = self.item.get_line()[4:].lstrip() @@ -134,9 +172,15 @@ class Stop(Statement): class Print(Statement): """ PRINT <format> [, <output-item-list>] - <format> == <default-char-expr> | <label> | * + <format> = <default-char-expr> | <label> | * + + <output-item> = <expr> | <io-implied-do> + <io-implied-do> = ( <io-implied-do-object-list> , <implied-do-control> ) + <io-implied-do-object> = <input-item> | <output-item> + <implied-do-control> = <do-variable> = <scalar-int-expr> , <scalar-int-expr> [ , <scalar-int-expr> ] + <input-item> = <variable> | <io-implied-do> """ - match = re.compile(r'print\b\s*[\w*]', re.I).match + match = re.compile(r'print\s*(\'\w*\'|\d+|[*]|\b\w)', re.I).match def process_item(self): item = self.item @@ -209,13 +253,59 @@ class Write(Statement): return self.get_indent_tab() + 'WRITE (%s) %s' \ % (self.io_control_specs, ', '.join(self.items)) +class Flush(Statement): + """ + FLUSH <file-unit-number> + FLUSH ( <flush-spec-list> ) + <flush-spec> = [ UNIT = ] <file-unit-number> + | IOSTAT = <scalar-int-variable> + | IOMSG = <iomsg-variable> + | ERR = <label> + """ + match = re.compile(r'flush\b',re.I).match + def process_item(self): + line = self.item.get_line()[5:].lstrip() + if not line: + self.isvalid = False + return + if line.startswith('('): + if not line.endswith(')'): + self.isvalid = False + return + self.specs = line[1:-1].strip() + else: + self.specs = line + return + def __str__(self): + tab = self.get_indent_tab() + return tab + 'FLUSH (%s)' % (self.specs) + +class Wait(Statement): + """ + WAIT ( <wait-spec-list> ) + <wait-spec> = [ UNIT = ] <file-unit-number> + | END = <label> + | EOR = <label> + | ERR = <label> + | ID = <scalar-int-expr> + | IOMSG = <iomsg-variable> + | IOSTAT = <scalar-int-variable> + + """ + match = re.compile(r'wait\s*\(.*\)\Z',re.I).match + def process_item(self): + self.specs = self.item.get_line()[4:].lstrip()[1:-1].strip() + return + def __str__(self): + tab = self.get_indent_tab() + return tab + 'WAIT (%s)' % (self.specs) + class Contains(Statement): """ CONTAINS """ match = re.compile(r'contains\Z',re.I).match - def process_item(self): - return + def process_item(self): return def __str__(self): return self.get_indent_tab() + 'CONTAINS' class Allocate(Statement): @@ -393,7 +483,6 @@ class Save(Statement): <object-name> = <name> """ match = re.compile(r'save\b',re.I).match - is_name = re.compile(r'\w+\Z').match def process_item(self): line = self.item.get_line()[4:].lstrip() if line.startswith('::'): @@ -405,9 +494,9 @@ class Save(Statement): if s.startswith('/'): assert s.endswith('/'),`s` n = s[1:-1].strip() - assert self.is_name(n) - items.append('/%s/' % ()) - elif self.is_name(s): + assert is_name(n),`n` + items.append('/%s/' % (n)) + elif is_name(s): items.append(s) else: self.isvalid = False @@ -522,7 +611,7 @@ class Use(Statement): s += ',' if self.items: s += ' ' + ', '.join(self.items) - return s + return tab + s class Exit(Statement): """ @@ -556,9 +645,9 @@ class Equivalence(Statement): match = re.compile(r'equivalence\s*\(.*\)\Z', re.I).match def process_item(self): items = [] - for s in self.item.get_line()[12:].lstrip().split(','): + for s in self.item.get_line()[11:].lstrip().split(','): s = s.strip() - assert s[0]+s[-1]=='()',`s` + assert s[0]+s[-1]=='()',`s,self.item.get_line()` items.append(s) self.items = items def __str__(self): @@ -574,11 +663,85 @@ class Dimension(Statement): line = self.item.get_line()[9:].lstrip() if line.startswith('::'): line = line[2:].lstrip() - self.items = [s.split() for s in line.split(',')] + self.items = [s.strip() for s in line.split(',')] return def __str__(self): return self.get_indent_tab() + 'DIMENSION %s' % (', '.join(self.items)) +class Target(Statement): + """ + TARGET [ :: ] <object-name> ( <array-spec> ) [ , <object-name> ( <array-spec> ) ]... + + """ + match = re.compile(r'target\b').match + def process_item(self): + line = self.item.get_line()[6:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.items = [s.strip() for s in line.split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'TARGET %s' % (', '.join(self.items)) + +class Pointer(Statement): + """ + POINTER [ :: ] <pointer-decl-list> + <pointer-decl> = <object-name> [ ( <deferred-shape-spec-list> ) ] + | <proc-entity-name> + + """ + match = re.compile(r'pointer\b',re.I).match + def process_item(self): + line = self.item.get_line()[7:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.items = [s.strip() for s in line.split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'POINTER %s' % (', '.join(self.items)) + +class Protected(Statement): + """ + PROTECTED [ :: ] <entity-name-list> + """ + match = re.compile(r'protected\b',re.I).match + def process_item(self): + line = self.item.get_line()[9:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.items = [s.strip() for s in line.split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'PROTECTED %s' % (', '.join(self.items)) + +class Volatile(Statement): + """ + Volatile [ :: ] <object-name-list> + """ + match = re.compile(r'volatile\b',re.I).match + def process_item(self): + line = self.item.get_line()[8:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.items = [s.strip() for s in line.split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'VOLATILE %s' % (', '.join(self.items)) + +class Value(Statement): + """ + VALUE [ :: ] <dummy-arg-name-list> + """ + match = re.compile(r'value\b',re.I).match + def process_item(self): + line = self.item.get_line()[5:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.items = [s.strip() for s in line.split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'VALUE %s' % (', '.join(self.items)) + class ArithmeticIf(Statement): """ IF ( <scalar-numeric-expr> ) <label> , <label> , <label> @@ -655,6 +818,41 @@ class External(Statement): def __str__(self): return self.get_indent_tab() + 'EXTERNAL ' + ', '.join(self.items) +class Namelist(Statement): + """ + NAMELIST / <namelist-group-name> / <namelist-group-object-list> [ [ , ] / <namelist-group-name> / <namelist-group-object-list> ]... + <namelist-group-object> = <variable-name> + """ + match = re.compile(r'namelist\b',re.I).match + def process_item(self): + line = self.item.get_line()[8:].lstrip() + items = [] + while line: + assert line.startswith('/'),`line` + i = line.find('/',1) + assert i!=-1,`line` + name = line[:i+1] + line = line[i+1:].lstrip() + i = line.find('/') + if i==-1: + items.append((name,line)) + line = '' + continue + s = line[:i].rstrip() + if s.endswith(','): + s = s[:-1].rstrip() + items.append((name,s)) + line = line[i+1:].lstrip() + self.items = items + return + + def __str__(self): + l = [] + for name,s in self.items: + l.append('%s %s' % (name,s)) + tab = self.get_indent_tab() + return tab + 'NAMELIST ' + ', '.join(l) + class Common(Statement): """ COMMON [ / [ <common-block-name> ] / ] <common-block-object-list> \ @@ -709,6 +907,242 @@ class Optional(Statement): def __str__(self): return self.get_indent_tab() + 'OPTIONAL ' + ', '.join(self.items) +class Intent(Statement): + """ + INTENT ( <intent-spec> ) [ :: ] <dummy-arg-name-list> + <intent-spec> = IN | OUT | INOUT + """ + match = re.compile(r'intent\s*\(',re.I).match + def process_item(self): + line = self.item.get_line()[6:].lstrip() + i = line.find(')') + self.specs = [s.strip() for s in line[1:i].strip().split(',')] + line = line[i+1:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.items = [s.strip() for s in line.split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'INTENT (%s) %s' \ + % (', '.join(self.specs), ', '.join(self.items)) + +class Entry(Statement): + """ + ENTRY <entry-name> [ ( [ <dummy-arg-list> ] ) [ <suffix> ] ] + <suffix> = <proc-language-binding-spec> [ RESULT ( <result-name> ) ] + | RESULT ( <result-name> ) [ <proc-language-binding-spec> ] + <proc-language-binding-spec> = <language-binding-spec> + <language-binding-spec> = BIND ( C [ , NAME = <scalar-char-initialization-expr> ] ) + """ + match = re.compile(r'entry\b', re.I).match + def process_item(self): + line = self.item.get_line()[5:].lstrip() + i = line.find('(') + if i==-1: + self.entryname = line + self.items = [] + self.suffix = '' + else: + self.entryname = line[:i].rstrip() + line = line[i:] + i = line.find(')') + self.items = [s.split() for s in line[1:i].split(',')] + self.suffix = line[i+1:].lstrip() + return + def __str__(self): + tab = self.get_indent_tab() + s = tab + 'ENTRY '+self.entryname + if self.items: + s += ' (%s)' % (', '.join(self.items)) + if self.suffix: + if not self.items: + s += ' ()' + s += ' ' + self.suffix + return s + +class Import(Statement): + """ + IMPORT [ [ :: ] <import-name-list> ] + """ + match = re.compile(r'import\b',re.I).match + def process_item(self): + line = self.item.get_line()[6:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + items = [] + for s in line.split(','): + s = s.strip() + if not is_name(s): + self.isvalid = False + return + items.append(s) + self.items = items + return + def __str__(self): + return self.get_indent_tab() + 'IMPORT ' + ', '.join(self.items) + +class Forall(Statement): + """ + FORALL <forall-header> <forall-assignment-stmt> + <forall-header> = ( <forall-triplet-spec-list> [ , <scalar-mask-expr> ] ) + <forall-triplet-spec> = <index-name> = <subscript> : <subscript> [ : <stride> ] + <subscript|stride> = <scalar-int-expr> + <forall-assignment-stmt> = <assignment-stmt> | <pointer-assignment-stmt> + """ + match = re.compile(r'forall\s*\(.*\).*=', re.I).match + def process_item(self): + line = self.item.get_line()[6:].lstrip() + i = line.index(')') + self.specs = line[1:i].strip() + line = line[i+1:].lstrip() + stmt = Assignment(self, self.item.copy(line)) + if stmt.isvalid: + self.content = [stmt] + else: + self.isvalid = False + return + def __str__(self): + tab = self.get_indent_tab() + return tab + 'FORALL (%s) %s' % (self.specs, str(self.content[0]).lstrip()) + +ForallStmt = Forall + +class SpecificBinding(Statement): + """ + PROCEDURE [ (<interface-name>) ] [ [ , <binding-attr-list> ] :: ] <binding-name> [ => <procedure-name> ] + <binding-attr> = PASS [ ( <arg-name> ) ] + | NOPASS + | NON_OVERRIDABLE + | DEFERRED + | <access-spec> + """ + match = re.compile(r'procedure\b',re.I).match + def process_item(self): + line = self.item.get_line()[9:].lstrip() + if line.startswith('('): + i = line.index(')') + name = line[1:i].strip() + line = line[i+1:].lstrip() + else: + name = '' + self.interface_name = name + if line.startswith(','): + line = line[1:].lstrip() + i = line.find('::') + if i != -1: + attrs = line[:i].rstrip() + line = line[i+2:].lstrip() + else: + attrs = '' + self.attrs = attrs + self.rest = line + return + def __str__(self): + tab = self.get_indent_tab() + s = 'PROCEDURE ' + if self.interface_name: + s += ' (' + self.interface_name + ')' + if self.attrs: + s += ' , ' + self.attrs + ' :: ' + return tab + s + rest + +class GenericBinding(Statement): + """ + GENERIC [ , <access-spec> ] :: <generic-spec> => <binding-name-list> + """ + match = re.compile(r'generic\b.*::.*=.*\Z', re.I).match + def process_item(self): + line = self.item.get_line()[7:].lstrip() + if line.startswith(','): + line = line[1:].lstrip() + i = line.index('::') + self.specs = line[:i].lstrip() + self.rest = line[i+2:].lstrip() + return + def __str__(self): + tab = self.get_indent_tab() + s = 'GENERIC' + if self.specs: + s += ', '+self.specs + s += ' :: ' + self.rest + return tab + s + + +class FinalBinding(Statement): + """ + FINAL [ :: ] <final-subroutine-name-list> + """ + match = re.compile(r'final\b', re.I).match + def process_item(self): + line = self.item.get_line()[5:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + items = [] + for s in line.split(','): + s = s.strip() + if not is_name(s): + self.isvalid = False + return + items.append(s) + self.items = items + return + def __str__(self): + return self.get_indent_tab() + 'FINAL ' + ', '.join(self.items) + +class Allocatable(Statement): + """ + ALLOCATABLE [ :: ] <object-name> [ ( <deferred-shape-spec-list> ) ] [ , <object-name> [ ( <deferred-shape-spec-list> ) ] ]... + """ + match = re.compile(r'allocatable\b',re.I).match + def process_item(self): + line = self.item.get_line()[11:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + items = [] + for s in line.split(','): + s = s.strip() + items.append(s) + self.items = items + return + def __str__(self): + return self.get_tab_indent() + 'ALLOCATABLE ' + ', '.join(self.items) + +class Asynchronous(Statement): + """ + ASYNCHRONOUS [ :: ] <object-name-list> + """ + match = re.compile(r'asynchronous\b',re.I).match + + def process_item(self): + line = self.item.get_line()[12:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + items = [] + for s in line.split(','): + s = s.strip() + if not is_name(s): + self.isvalid = False + return + items.append(s) + self.items = items + return + + def __str__(self): + return self.get_indent_tab() + 'ASYNCHRONOUS ' + ', '.join(self.items) + +class Bind(Statement): + """ + <language-binding-spec> [ :: ] <bind-entity-list> + <language-binding-spec> = BIND ( C [ , NAME = <scalar-char-initialization-expr> ] ) + <bind-entity> = <entity-name> | / <common-block-name> / + """ + match = re.compile(r'bind\s*\(.*\)\Z',re.I).match + def process_item(self): + self.value = self.item.get_line()[4].lstrip()[1:-1].strip() + return + def __str__(self): + return self.get_indent_tab() + 'BIND (%s)' % (self.value) + # IF construct statements class Else(Statement): @@ -824,8 +1258,8 @@ WhereStmt = Where class ElseWhere(Statement): """ - ELSEWHERE ( <mask-expr> ) [ <where-construct-name> ] - ELSEWHERE [ <where-construct-name> ] + ELSE WHERE ( <mask-expr> ) [ <where-construct-name> ] + ELSE WHERE [ <where-construct-name> ] """ match = re.compile(r'else\s*where\b').match def process_item(self): @@ -850,5 +1284,119 @@ class ElseWhere(Statement): def __str__(self): tab = self.get_indent_tab() if self.expr is None: - return tab + 'ELSEWHERE ' + self.name - return tab + 'ELSEWHERE (%s) ' % (self.expr) + self.name + return tab + 'ELSE WHERE ' + self.name + return tab + 'ELSE WHERE (%s) ' % (self.expr) + self.name + +# Enum construct statements + +class Enumerator(Statement): + """ + ENUMERATOR [ :: ] <enumerator-list> + <enumerator> = <named-constant> [ = <scalar-int-initialization-expr> ] + """ + match = re.compile(r'enumerator\b',re.I).match + def process_item(self): + line = self.item.get_line()[10:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.rest = line + return + def __str__(self): + return self.get_indent_tab() + 'ENUMERATOR ' + self.rest + +# F2PY specific statements + +class FortranName(Statement): + """ + FORTRANNAME <name> + """ + match = re.compile(r'fortranname\s*\w+\Z',re.I).match + def process_item(self): + self.value = self.item.get_line()[11:].lstrip() + return + def __str__(self): + return self.get_indent_tab() + 'FORTRANNAME ' + self.value + +class ThreadSafe(Statement): + """ + THREADSAFE + """ + match = re.compile(r'threadsafe\Z',re.I).match + def process_item(self): + return + def __str__(self): + return self.get_indent_tab() + 'THREADSAFE' + +class Depend(Statement): + """ + DEPEND ( <name-list> ) [ :: ] <dummy-arg-name-list> + + """ + match = re.compile(r'depend\s*\(',re.I).match + def process_item(self): + line = self.item.get_line()[6:].lstrip() + i = line.find(')') + self.depends = [s.strip() for s in line[1:i].strip().split(',')] + line = line[i+1:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.items = [s.strip() for s in line.split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'DEPEND (%s) %s' \ + % (', '.join(self.depends), ', '.join(self.items)) + +class Check(Statement): + """ + CHECK ( <c-int-scalar-expr> ) [ :: ] <dummy-arg-name-list> + + """ + match = re.compile(r'check\s*\(',re.I).match + def process_item(self): + line = self.item.get_line()[5:].lstrip() + i = line.find(')') + self.expr = line[1:i].strip() + line = line[i+1:].lstrip() + if line.startswith('::'): + line = line[2:].lstrip() + self.items = [s.strip() for s in line.split(',')] + return + def __str__(self): + return self.get_indent_tab() + 'CHECK (%s) %s' \ + % (self.expr, ', '.join(self.items)) + +class CallStatement(Statement): + """ + CALLSTATEMENT <c-expr> + """ + match = re.compile(r'callstatement\b', re.I).match + def process_item(self): + self.expr = self.item.get_line()[13:].lstrip() + return + def __str__(self): + return self.get_indent_tab() + 'CALLSTATEMENT ' + self.expr + +class CallProtoArgument(Statement): + """ + CALLPROTOARGUMENT <c-type-spec-list> + """ + match = re.compile(r'callprotoargument\b', re.I).match + def process_item(self): + self.specs = self.item.get_line()[17:].lstrip() + return + def __str__(self): + return self.get_indent_tab() + 'CALLPROTOARGUMENT ' + self.specs + +# Non-standard statements + +class Pause(Statement): + """ + PAUSE [ <char-literal-constant|int-literal-constant> ] + """ + match = re.compile(r'pause\b\s*(\d+|\'\w*\')\Z', re.I).match + def process_item(self): + self.value = self.item.get_line()[5:].lstrip() + return + def __str__(self): + return self.get_indent_tab() + 'PAUSE ' + self.value + diff --git a/numpy/f2py/lib/typedecl_statements.py b/numpy/f2py/lib/typedecl_statements.py index ee9d96b98..2d5deded9 100644 --- a/numpy/f2py/lib/typedecl_statements.py +++ b/numpy/f2py/lib/typedecl_statements.py @@ -30,7 +30,7 @@ class TypeDeclarationStatement(Statement): | ( KIND = <scalar-int-initialization-expr> [, LEN = <type-param-value>] ) <length-selector> = ( [ LEN = ] <type-param-value> ) | * <char-length> [ , ] - <char-length> = ( <type-param-value> ) | <scalar-int-literal-expr> + <char-length> = ( <type-param-value> ) | <scalar-int-literal-constant> <attr-spec> = <access-spec> | ALLOCATABLE | ASYNCHRONOUS | DIMENSION ( <array-spec> ) | EXTERNAL @@ -54,6 +54,9 @@ class TypeDeclarationStatement(Statement): <assumed-size-spec> = [ <explicit-shape-spec-list> , ] [ <lower-bound> : ] * <bound> = <specification-expr> + <int-literal-constant> = <digit-string> [ _ <kind-param> ] + <digit-string> = <digit> [ <digit> ].. + <kind-param> = <digit-string> | <scalar-int-constant-name> """ def process_item(self): @@ -61,11 +64,17 @@ class TypeDeclarationStatement(Statement): clsname = self.__class__.__name__.lower() line = item.get_line() from block_statements import Function - if Function.match(line): - self.isvalid = False - return + if not line.startswith(clsname): - line = line[:len(clsname)].replace(' ','') + line[len(clsname):] + i = 0 + j = 0 + for c in line: + i += 1 + if c==' ': continue + j += 1 + if j==len(clsname): + break + line = line[:i].replace(' ','') + line[i:] assert line.startswith(clsname),`line,clsname` line = line[len(clsname):].lstrip() @@ -82,18 +91,31 @@ class TypeDeclarationStatement(Statement): selector += line[:i+1].rstrip() line = line[i+1:].lstrip() else: - i = len(line) - ci = '' - for c in [',','::',' ']: - j = line.find(c) - if j!=-1 and j<i: - i = j - ci = c - assert i!=len(line),`i,line` + m = re.match(r'\d+(_\w+|)|[*]',line) + if not m: + self.isvalid = False + return + i = m.end() selector += line[:i].rstrip() - line = line[i+len(ci):].lstrip() + line = line[i:].lstrip() else: selector = '' + + fm = Function.match(line) + if fm: + l2 = line[:fm.end()] + m2 = re.match(r'.*?\b(?P<name>\w+)\Z',l2) + if not m2: + self.isvalid = False + return + fname = m2.group('name') + fitem = item.copy(clsname+selector+' :: '+fname, + apply_map=True) + self.parent.put_item(fitem) + item.clone(line) + self.isvalid = False + return + if line.startswith(','): line = line[1:].lstrip() @@ -105,13 +127,23 @@ class TypeDeclarationStatement(Statement): 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: + assert self.parent.typedecl is None,`self.parent.typedecl` + self.parent.typedecl = self + self.ignore = True return + def tostr(self): + clsname = self.__class__.__name__.upper() + return clsname + self.raw_selector + def __str__(self): - clsname = self.__class__.__name__.upper() tab = self.get_indent_tab() - return tab + clsname + '%s, %s :: %s' \ - % (self.raw_selector, self.attrspec, self.entity_decls) + 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) class Integer(TypeDeclarationStatement): match = re.compile(r'integer\b',re.I).match @@ -126,8 +158,8 @@ class Complex(TypeDeclarationStatement): match = re.compile(r'complex\b',re.I).match class DoubleComplex(TypeDeclarationStatement): + # not in standard match = re.compile(r'double\s*complex\b',re.I).match - modes = ['pyf','fix77'] class Logical(TypeDeclarationStatement): match = re.compile(r'logical\b',re.I).match @@ -135,6 +167,10 @@ class Logical(TypeDeclarationStatement): class Character(TypeDeclarationStatement): match = re.compile(r'character\b',re.I).match +class Byte(TypeDeclarationStatement): + # not in standard + match = re.compile(r'byte\b',re.I).match + class Type(TypeDeclarationStatement): match = re.compile(r'type\s*\(', re.I).match TypeStmt = Type @@ -142,7 +178,6 @@ TypeStmt = Type class Class(TypeDeclarationStatement): match = re.compile(r'class\s*\(', re.I).match - class Implicit(Statement): """ IMPLICIT <implicit-spec-list> @@ -150,7 +185,7 @@ class Implicit(Statement): <implicit-spec> = <declaration-type-spec> ( <letter-spec-list> ) <letter-spec> = <letter> [ - <letter> ] """ - match = re.compile(r'implicit\b').match + match = re.compile(r'implicit\b',re.I).match def process_item(self): line = self.item.get_line()[8:].lstrip() if line=='none': @@ -164,3 +199,5 @@ class Implicit(Statement): if not self.items: return tab + 'IMPLICIT NONE' return tab + 'IMPLICIT ' + ', '.join(self.items) + + |