summaryrefslogtreecommitdiff
path: root/numpy/f2py/lib
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/f2py/lib')
-rw-r--r--numpy/f2py/lib/block.py174
-rw-r--r--numpy/f2py/lib/parsefortran.py16
-rw-r--r--numpy/f2py/lib/readfortran.py29
-rw-r--r--numpy/f2py/lib/sourceinfo.py16
-rw-r--r--numpy/f2py/lib/splitline.py55
5 files changed, 241 insertions, 49 deletions
diff --git a/numpy/f2py/lib/block.py b/numpy/f2py/lib/block.py
index cea71a593..f7cdb7dd3 100644
--- a/numpy/f2py/lib/block.py
+++ b/numpy/f2py/lib/block.py
@@ -15,19 +15,25 @@ __all__ = ['Block','Module','PythonModule','Interface',
'Subroutine','Function','Type']
import re
+import sys
from readfortran import Line
+from splitline import split2, string_replace_map
class Block:
classes = []
+ end_re = re.compile(r'\s*end\s*\Z', re.I)
def __init__(self, parent):
self.parent = parent
- self.content = []
-
+ self.isfix77 = parent.isfix77
+ self.reader = parent.reader
self.get_item = parent.get_item
self.put_item = parent.put_item
+ self.lower = not self.reader.ispyf
+
+ self.content = []
self.name = None
def get_name(self):
@@ -48,35 +54,70 @@ class Block:
l.append(tab+'end '+self.__class__.__name__ +' '+ name)
return '\n'.join(l)
- def isendline(self, line):
+ def isenditem(self, item):
+ line,sline = split2(item.get_line())
+ if sline: return False # end statement never contains strings
if self.__class__ is Block:
# MAIN block does not define start/end line conditions,
- # so it never ends.
+ # so it should never end until all lines are read.
+ # However, sometimes F77 programs lack the PROGRAM statement,
+ # and here we fix that:
+ if self.isfix77:
+ m = self.end_re.match(line)
+ if m:
+ message = self.reader.format_message(\
+ 'WARNING',
+ 'assuming the end of undefined PROGRAM statement',
+ item.span[0],item.span[1])
+ print >> sys.stderr, message
+ p = Program(self,Program.start_re.match('program UNDEFINED'))
+ p.content.extend(self.content)
+ self.content[:] = [p]
+ return None
return False
m = self.end_re.match(line)
if not m: return False
+ # check if the block start name matches with the block end name
+
+ if m.groupdict().has_key('name'):
+ end_name = m.group('name')
+ name = self.get_name()
+ if end_name and name != end_name:
+ message = self.reader.format_message(\
+ 'WARNING',
+ 'expected the end of %r block but got end of %r'\
+ % (name, end_name),
+ item.span[0],item.span[1])
+ print >> sys.stderr, message
return True
+ def isblock(self, item):
+ line = item.get_line()
+ for cls in self.classes:
+ m = cls.start_re.match(line)
+ if m:
+ subblock = cls(self, m, item)
+ self.content.append(subblock)
+ subblock.fill()
+ return True
+ return False
+
def fill(self):
item = self.get_item()
while item is not None:
if isinstance(item, Line):
- line = item.line
- if self.isendline(line):
+ # handle end of a block
+ flag = self.isenditem(item)
+ if flag: # end of block
break
- # check if line contains subblock start
- found_block = False
- for cls in self.classes:
- m = cls.start_re.match(line)
- if m:
- found_block = True
- subblock = cls(self, m)
- self.content.append(subblock)
- subblock.fill()
- break
- if found_block:
+ if flag is None: # fixing the end of undefined start
item = self.get_item()
continue
+ # handle subblocks
+ if self.isblock(item):
+ item = self.get_item()
+ continue
+
# line contains something else
self.content.append(item)
item = self.get_item()
@@ -84,63 +125,114 @@ class Block:
class Program(Block):
classes = []
- start_re = re.compile(r'\s*program', re.I)
-
+ start_re = re.compile(r'\s*program\s*((?P<name>\w+)|)', re.I)
+ end_re = re.compile(r'\s*end(\s*program(\s*(?P<name>\w+)|)|)\s*\Z', re.I)
+ def __init__(self, parent, start_re_match, item):
+ Block.__init__(self, parent)
+ self.name = start_re_match.group('name')
class Module(Block):
classes = []
- start_re = re.compile(r'\s*module\s(?P<name>\w+)', re.I)
- end_re = re.compile(r'\s*end(\s*module(\s*(?P<name>\w+)|)|)\s*\Z')
- def __init__(self, parent, start_re_match):
+ start_re = re.compile(r'\s*module\s*(?P<name>\w+)\s*\Z', re.I)
+ end_re = re.compile(r'\s*end(\s*module(\s*(?P<name>\w+)|)|)\s*\Z', re.I)
+ def __init__(self, parent, start_re_match, item):
Block.__init__(self, parent)
self.name = start_re_match.group('name')
class Interface(Block):
classes = []
start_re = re.compile(r'\s*interface(\s*(?P<name>\w+)|)', re.I)
- end_re = re.compile(r'\s*end(\s*interface(\s*(?P<name>\w+)|)|)\s*\Z')
- def __init__(self, parent, start_re_match):
+ end_re = re.compile(r'\s*end(\s*interface(\s*(?P<name>\w+)|)|)\s*\Z', re.I)
+ def __init__(self, parent, start_re_match, item):
Block.__init__(self, parent)
self.name = start_re_match.group('name')
class PythonModule(Block):
classes = []
- start_re = re.compile(r'\s*python\s*module\s(?P<name>\w+)', re.I)
- end_re = re.compile(r'\s*end(\s*python\s*module(\s*(?P<name>\w+)|)|)\s*\Z')
- def __init__(self, parent, start_re_match):
+ start_re = re.compile(r'\s*python\s*module\s*(?P<name>\w+)', re.I)
+ end_re = re.compile(r'\s*end(\s*python\s*module(\s*(?P<name>\w+)|)|)\s*\Z', re.I)
+ def __init__(self, parent, start_re_match, item):
Block.__init__(self, parent)
self.name = start_re_match.group('name')
class Subroutine(Block):
classes = []
start_re = re.compile(r'\s*subroutine\s*(?P<name>\w+)', re.I)
- end_re = re.compile(r'\s*end(\s*subroutine(\s*(?P<name>.*)|)|)\s*\Z')
- def __init__(self, parent, start_re_match):
+ end_re = re.compile(r'\s*end(\s*subroutine(\s*(?P<name>\w+)|)|)\s*\Z', re.I)
+ def __init__(self, parent, start_re_match, item):
Block.__init__(self, parent)
self.name = start_re_match.group('name')
class Function(Block):
classes = []
start_re = re.compile(r'\s*function\s*(?P<name>\w+)', re.I)
- end_re = re.compile(r'\s*end(\s*function(\s*(?P<name>.*)|)|)\s*\Z')
- def __init__(self, parent, start_re_match):
+ end_re = re.compile(r'\s*end(\s*function(\s*(?P<name>\w+)|)|)\s*\Z')
+ def __init__(self, parent, start_re_match, item):
Block.__init__(self, parent)
self.name = start_re_match.group('name')
class Type(Block):
classes = []
- start_re = re.compile(r'\s*type(?!\s*\()', re.I)
- end_re = re.compile(r'\s*end(\s*type(\s*(?P<name>.*)|)|)\s*\Z')
- def __init__(self, parent, start_re_match):
+ start_re = re.compile(r'\s*type(?!\s*\()(.*::|)\s*(?P<name>\w+)\s*\Z', re.I)
+ end_re = re.compile(r'\s*end(\s*type(\s*(?P<name>\w+)|)|)\s*\Z', re.I)
+ def __init__(self, parent, start_re_match, item):
+ Block.__init__(self, parent)
+ self.name = start_re_match.group('name')
+
+
+class StatementBlock(Block):
+ classes = []
+
+ def fill(self):
+ item = self.get_item()
+ while item is not None:
+ if isinstance(item, Line):
+ # handle end of a block
+ flag = self.isenditem(item)
+ if flag: # end of block
+ break
+ # handle subblocks
+ if self.isblock(item):
+ item = self.get_item()
+ continue
+
+ # line contains something else
+ self.content.append(item)
+ item = self.get_item()
+ return
+
+class DoBlock(StatementBlock):
+
+ start_re = re.compile(r'\s*do\b', re.I)
+ end_re = re.compile(r'\s*end\s*do(\s*(?P<name>.*)|)\s*\Z', re.I)
+
+ def __init__(self, parent, start_re_match, item):
+ Block.__init__(self, parent)
+ self.name = item.label
+
+class IfThenBlock(StatementBlock):
+
+ start_re = re.compile(r'\s*if\b.*?\bthen\s*\Z', re.I)
+ #start_re = re.compile(r'\s*if\b', re.I)
+ end_re = re.compile(r'\s*end\s*if(\s*(?P<name>.*)|)\s*\Z', re.I)
+
+ def __init__(self, parent, start_re_match, item):
Block.__init__(self, parent)
- self.name = 'notimpl'#start_re_match.group('name')
+ self.name = item.label
+
# Initialize classes lists
-Block.classes.extend([Program,PythonModule,Module,Interface,Subroutine,Function,Type])
-Module.classes.extend([PythonModule,Interface,Subroutine,Function,Type])
+
+basic_blocks = [Program,PythonModule,Module,Interface,Subroutine,Function,Type]
+stmt_blocks = [DoBlock,IfThenBlock]
+
+Block.classes.extend(basic_blocks + stmt_blocks)
+Module.classes.extend(Block.classes[1:])
PythonModule.classes.extend(Module.classes)
-Interface.classes.extend([PythonModule,Module,Interface,Subroutine,Function,Type])
-Subroutine.classes.extend([PythonModule,Module,Interface,Subroutine,Function,Type])
-Subroutine.classes.extend(Function.classes)
-Type.classes.extend([Type,Function,Subroutine,Interface])
+Interface.classes.extend(Block.classes[1:])
+Subroutine.classes.extend(Block.classes[1:])
+Function.classes.extend(Subroutine.classes)
+Type.classes.extend(Block.classes[3:])
+
+StatementBlock.classes.extend(stmt_blocks)
diff --git a/numpy/f2py/lib/parsefortran.py b/numpy/f2py/lib/parsefortran.py
index d4dc3d594..2341a0f22 100644
--- a/numpy/f2py/lib/parsefortran.py
+++ b/numpy/f2py/lib/parsefortran.py
@@ -19,6 +19,7 @@ class FortranParser:
def __init__(self, reader):
self.reader = reader
+ self.isfix77 = reader.isfix77
def get_item(self):
try:
@@ -45,7 +46,19 @@ python module foo
end python module
"""
reader = FortranStringReader(string, True, True)
- reader = FortranFileReader(filename)
+ parser = FortranParser(reader)
+ block = parser.parse()
+ print block
+
+def test_f77():
+ string = """\
+c program foo
+ a = 3
+ end
+ subroutine bar
+ end
+"""
+ reader = FortranStringReader(string, False, True)
parser = FortranParser(reader)
block = parser.parse()
print block
@@ -60,5 +73,6 @@ def simple_main():
print block
if __name__ == "__main__":
+ #test_f77()
#test_pyf()
simple_main()
diff --git a/numpy/f2py/lib/readfortran.py b/numpy/f2py/lib/readfortran.py
index 503fa123a..4937cb4b3 100644
--- a/numpy/f2py/lib/readfortran.py
+++ b/numpy/f2py/lib/readfortran.py
@@ -28,13 +28,13 @@ from cStringIO import StringIO
from numpy.distutils.misc_util import yellow_text, red_text, blue_text
from sourceinfo import get_source_info
-from splitline import LineSplitter, String
+from splitline import LineSplitter, String, string_replace_map
_spacedigits=' 0123456789'
_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[0]==' '
_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)
class FortranReaderError: # TODO: may be derive it from Exception
def __init__(self, message):
@@ -49,11 +49,19 @@ class Line:
self.span = linenospan
self.label = label
self.reader = reader
+ self.strline = None
def __repr__(self):
return self.__class__.__name__+'(%r,%s,%r)' \
- % (self.line, self.span, self.label)
+ % (self.get_line(), self.span, self.label)
def isempty(self, ignore_comments=False):
return not (self.line.strip() or (self.label and self.label.strip()))
+ 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)
+ self.strline = line
+ self.strlinemap = str_map
+ return line
class SyntaxErrorLine(Line, FortranReaderError):
def __init__(self, line, linenospan, label, reader, message):
@@ -376,7 +384,7 @@ class FortranReaderBase:
if line is None: return
startlineno = self.linecount
line = self.handle_cf2py_start(line)
-
+ label = None
if self.ispyf:
# handle multilines
for mlstr in ['"""',"'''"]:
@@ -464,6 +472,13 @@ class FortranReaderBase:
self.linecount, self.linecount))
line = get_single_line()
continue
+ else:
+ # first line, check for a f90 label
+ m = _f90label_re.match(line)
+ if m:
+ assert label is None,`label`
+ label = m.group('label')
+ line = line[m.end():]
line,qc = handle_inline_comment(line, self.linecount, qc)
i = line.rfind('&')
@@ -496,7 +511,7 @@ class FortranReaderBase:
'following character continuation: %r, expected None.' % (qc),
startlineno, self.linecount)
print >> sys.stderr, message
- return self.line_item(''.join(lines),startlineno,self.linecount,None)
+ return self.line_item(''.join(lines),startlineno,self.linecount,label)
## FortranReaderBase
@@ -559,6 +574,10 @@ python module foo
b=3! hey, fake line continuation:&
c=4& !line cont
&45
+ thisis_label_2 : c = 3
+ xxif_isotropic_2 : if ( string_upper_compare ( o%opt_aniso, 'ISOTROPIC' ) ) then
+ g=3
+ endif
end interface
end python module foo
! end of file
diff --git a/numpy/f2py/lib/sourceinfo.py b/numpy/f2py/lib/sourceinfo.py
index 635e22f7d..457c3d8b3 100644
--- a/numpy/f2py/lib/sourceinfo.py
+++ b/numpy/f2py/lib/sourceinfo.py
@@ -6,6 +6,7 @@ Created: May 2006
__all__ = ['get_source_info']
import re
import os
+import sys
_has_f_extension = re.compile(r'.*[.](for|ftn|f77|f)\Z',re.I).match
_has_f_header = re.compile(r'-[*]-\s*fortran\s*-[*]-',re.I).search
@@ -49,12 +50,25 @@ def is_free_format(file):
elif _has_f90_header(line):
n = 0
isfree = True
+ contline = False
while n>0 and line:
if line[0]!='!' and line.strip():
n -= 1
- if (line[0]!='\t' and _free_f90_start(line[:5])) or line[-2:-1]=='&':
+ if not contline and (line[0]!='\t' and _free_f90_start(line[:5])):
isfree = True
break
+ elif line[-2:-1]=='&':
+ contline = True
+ else:
+ contline = False
line = f.readline()
f.close()
return isfree
+
+def simple_main():
+ for filename in sys.argv[1:]:
+ isfree, isstrict = get_source_info(filename)
+ print '%s: isfree=%s, isstrict=%s' % (filename, isfree, isstrict)
+
+if __name__ == '__main__':
+ simple_main()
diff --git a/numpy/f2py/lib/splitline.py b/numpy/f2py/lib/splitline.py
index 295929dc6..f5be43e9b 100644
--- a/numpy/f2py/lib/splitline.py
+++ b/numpy/f2py/lib/splitline.py
@@ -13,10 +13,41 @@ $Date: 2000/07/31 07:04:03 $
Pearu Peterson
"""
-__all__ = ['LineSplitter','String']
+__all__ = ['LineSplitter','String','split2','string_replace_map']
class String(str): pass
+def split2(line, lower=False):
+ """
+ Split line into non-string part and into a start of a string part.
+ Returns 2-tuple. The second item either is empty string or start
+ of a string part.
+ """
+ return LineSplitter(line,lower=lower).split2()
+
+def string_replace_map(line, lower=False, _cache={'index':0}):
+ """
+ Replaces string constants with name _F2PY_STRING_CONSTANT_<index>
+ and returns a new line and a map
+ {_F2PY_STRING_CONSTANT_<index>: <original string constant>}
+ """
+ items = []
+ string_map = {}
+ rev_string_map = {}
+ for item in LineSplitter(line, lower=lower):
+ if isinstance(item, String):
+ key = rev_string_map.get(item)
+ if key is None:
+ _cache['index'] += 1
+ index = _cache['index']
+ key = '_F2PY_STRING_CONSTANT_%s' % (index)
+ string_map[key] = item
+ rev_string_map[item] = key
+ items.append(key)
+ else:
+ items.append(item)
+ return ''.join(items),string_map
+
class LineSplitter:
""" Splits a line into non strings and strings. E.g.
abc=\"123\" -> ['abc=','\"123\"']
@@ -37,6 +68,20 @@ class LineSplitter:
item = self.get_item() # get_item raises StopIteration
return item
+ def split2(self):
+ """
+ Split line until the first start of a string.
+ """
+ try:
+ item1 = self.get_item()
+ except StopIteration:
+ return '',''
+ i = len(item1)
+ l = self.fifo_line[:]
+ l.reverse()
+ item2 = ''.join(l)
+ return item1,item2
+
def get_item(self):
fifo_pop = self.fifo_line.pop
try:
@@ -109,6 +154,14 @@ def test():
assert l==[' &abc"','123']
assert splitter.quotechar is None
+ l = split2('')
+ assert l==('',''),`l`
+ l = split2('12')
+ assert l==('12',''),`l`
+ l = split2('1"a"//"b"')
+ assert l==('1','"a"//"b"'),`l`
+ l = split2('"ab"')
+ assert l==('','"ab"'),`l`
if __name__ == '__main__':
test()