diff options
Diffstat (limited to 'numpy/f2py/lib')
-rw-r--r-- | numpy/f2py/lib/block.py | 174 | ||||
-rw-r--r-- | numpy/f2py/lib/parsefortran.py | 16 | ||||
-rw-r--r-- | numpy/f2py/lib/readfortran.py | 29 | ||||
-rw-r--r-- | numpy/f2py/lib/sourceinfo.py | 16 | ||||
-rw-r--r-- | numpy/f2py/lib/splitline.py | 55 |
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() |