summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorPearu Peterson <pearu.peterson@gmail.com>2006-06-29 09:33:15 +0000
committerPearu Peterson <pearu.peterson@gmail.com>2006-06-29 09:33:15 +0000
commit109772b31ff4c3e755fc21eb2ef3c0db267002ef (patch)
treef7b4b3c7dfb52bc871f4c0f872378aa71abdd6af /numpy
parent9913769d0719b6cfca1b3d51baee8421ba7cf288 (diff)
downloadnumpy-109772b31ff4c3e755fc21eb2ef3c0db267002ef.tar.gz
Finished impl. Fortran 77-2003 parse pattern classes.
Diffstat (limited to 'numpy')
-rw-r--r--numpy/f2py/lib/base_classes.py152
-rw-r--r--numpy/f2py/lib/block_statements.py388
-rw-r--r--numpy/f2py/lib/parsefortran.py21
-rw-r--r--numpy/f2py/lib/readfortran.py190
-rw-r--r--numpy/f2py/lib/sourceinfo.py9
-rw-r--r--numpy/f2py/lib/splitline.py6
-rw-r--r--numpy/f2py/lib/statements.py596
-rw-r--r--numpy/f2py/lib/typedecl_statements.py77
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)
+
+