summaryrefslogtreecommitdiff
path: root/numpy/f2py/lib/readfortran.py
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/f2py/lib/readfortran.py')
-rw-r--r--numpy/f2py/lib/readfortran.py190
1 files changed, 135 insertions, 55 deletions
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():