#!/usr/bin/env python """ Fortran 2003 Syntax Rules. ----- Permission to use, modify, and distribute this software is given under the terms of the NumPy License. See http://scipy.org. NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. Author: Pearu Peterson Created: Oct 2006 ----- """ import re from splitline import string_replace_map import pattern_tools as pattern from readfortran import FortranReaderBase ############################################################################### ############################## BASE CLASSES ################################### ############################################################################### class NoMatchError(Exception): pass class ParseError(Exception): pass class Base(object): """ Base class for Fortran 2003 syntax rules. All Base classes have the following attributes: .string - original argument to construct a class instance, it's type is either str or FortranReaderBase. .item - Line instance (holds label) or None. """ subclasses = {} def __new__(cls, string, parent_cls = None): """ """ if parent_cls is None: parent_cls = [cls] elif cls not in parent_cls: parent_cls.append(cls) #print '__new__:',cls.__name__,`string` match = cls.__dict__.get('match', None) if isinstance(string, FortranReaderBase) and not issubclass(cls, BlockBase) \ and match is not None: reader = string item = reader.get_item() if item is None: return try: obj = cls(item.line, parent_cls = parent_cls) except NoMatchError: obj = None if obj is None: reader.put_item(item) return obj.item = item return obj errmsg = '%s: %r' % (cls.__name__, string) if match is not None: try: result = cls.match(string) except NoMatchError, msg: if str(msg)==errmsg: # avoid recursion 1. raise result = None else: result = None #print '__new__:result:',cls.__name__,`string,result` if isinstance(result, tuple): obj = object.__new__(cls) obj.string = string obj.item = None if hasattr(cls, 'init'): obj.init(*result) return obj elif isinstance(result, Base): return result elif result is None: for subcls in Base.subclasses.get(cls.__name__,[]): if subcls in parent_cls: # avoid recursion 2. continue #print '%s:%s: %r' % (cls.__name__,subcls.__name__,string) try: obj = subcls(string, parent_cls = parent_cls) except NoMatchError, msg: obj = None if obj is not None: return obj else: raise AssertionError,`result` raise NoMatchError,errmsg ## def restore_reader(self): ## self._item.reader.put_item(self._item) ## return def init(self, *items): self.items = items return def torepr(self): return '%s(%s)' % (self.__class__.__name__, ', '.join(map(repr,self.items))) def compare(self, other): return cmp(self.items,other.items) def __str__(self): return self.tostr() def __repr__(self): return self.torepr() def __cmp__(self, other): if self is other: return 0 if not isinstance(other, self.__class__): return cmp(self.__class__, other.__class__) return self.compare(other) def tofortran(self, tab='', isfix=None): return tab + str(self) class BlockBase(Base): """ = [ ] [ ]... ... [ ]... [ ] """ def match(startcls, subclasses, endcls, reader): assert isinstance(reader,FortranReaderBase),`reader` content = [] if startcls is not None: try: obj = startcls(reader) except NoMatchError: obj = None if obj is None: return content.append(obj) if endcls is not None: classes = subclasses + [endcls] else: classes = subclasses[:] i = 0 while 1: cls = classes[i] try: obj = cls(reader) except NoMatchError: obj = None if obj is None: j = i for cls in classes[i+1:]: j += 1 try: obj = cls(reader) except NoMatchError: obj = None if obj is not None: break if obj is not None: i = j if obj is not None: content.append(obj) if endcls is not None and isinstance(obj, endcls): break continue if endcls is not None: item = reader.get_item() if item is not None: reader.error('failed to parse with %s, skipping.' % ('|'.join([c.__name__ for c in classes[i:]])), item) continue if hasattr(content[0],'name'): reader.error('unexpected eof file while looking line for <%s> of %s.'\ % (classes[-1].__name__.lower().replace('_','-'), content[0].name)) else: reader.error('unexpected eof file while looking line for <%s>.'\ % (classes[-1].__name__.lower().replace('_','-'))) break if not content: return if startcls is not None and endcls is not None: # check names of start and end statements: start_stmt = content[0] end_stmt = content[-1] if isinstance(end_stmt, endcls) and hasattr(end_stmt, 'get_name') and hasattr(start_stmt, 'get_name'): if end_stmt.get_name() is not None: if start_stmt.get_name() != end_stmt.get_name(): end_stmt._item.reader.error('expected <%s-name> is %s but got %s. Ignoring.'\ % (end_stmt.get_type().lower(), start_stmt.get_name(), end_stmt.get_name())) else: end_stmt.set_name(start_stmt.get_name()) return content, match = staticmethod(match) def init(self, content): self.content = content return def compare(self, other): return cmp(self.content,other.content) def tostr(self): return self.tofortran() def torepr(self): return '%s(%s)' % (self.__class__.__name__,', '.join(map(repr, self.content))) def tofortran(self, tab='', isfix=None): l = [] start = self.content[0] end = self.content[-1] extra_tab = '' if isinstance(end, EndStmtBase): extra_tab = ' ' l.append(start.tofortran(tab=tab,isfix=isfix)) for item in self.content[1:-1]: l.append(item.tofortran(tab=tab+extra_tab,isfix=isfix)) if len(self.content)>1: l.append(end.tofortran(tab=tab,isfix=isfix)) return '\n'.join(l) ## def restore_reader(self): ## content = self.content[:] ## content.reverse() ## for obj in content: ## obj.restore_reader() ## return class SequenceBase(Base): """ = , [ , ]... """ def match(separator, subcls, string): line, repmap = string_replace_map(string) if isinstance(separator, str): splitted = line.split(separator) else: splitted = separator[1].split(line) separator = separator[0] if len(splitted)<=1: return lst = [] for p in splitted: lst.append(subcls(repmap(p.strip()))) return separator, tuple(lst) match = staticmethod(match) def init(self, separator, items): self.separator = separator self.items = items return def tostr(self): s = self.separator if s==',': s = s + ' ' elif s==' ': pass else: s = ' ' + s + ' ' return s.join(map(str, self.items)) def torepr(self): return '%s(%r, %r)' % (self.__class__.__name__, self.separator, self.items) def compare(self, other): return cmp((self.separator,self.items),(other.separator,self.items)) class UnaryOpBase(Base): """ = """ def tostr(self): return '%s %s' % tuple(self.items) def match(op_pattern, rhs_cls, string): m = op_pattern.match(string) if not m: return #if not m: return rhs_cls(string) rhs = string[m.end():].lstrip() if not rhs: return op = string[:m.end()].rstrip().upper() return op, rhs_cls(rhs) match = staticmethod(match) class BinaryOpBase(Base): """ = is searched from right by default. """ def match(lhs_cls, op_pattern, rhs_cls, string, right=True): line, repmap = string_replace_map(string) if isinstance(op_pattern, str): if right: t = line.rsplit(op_pattern,1) else: t = line.split(op_pattern,1) if len(t)!=2: return lhs, rhs = t[0].rstrip(), t[1].lstrip() op = op_pattern else: if right: t = op_pattern.rsplit(line) else: t = op_pattern.lsplit(line) if t is None or len(t)!=3: return lhs, op, rhs = t lhs = lhs.rstrip() rhs = rhs.lstrip() op = op.upper() if not lhs: return if not rhs: return lhs_obj = lhs_cls(repmap(lhs)) rhs_obj = rhs_cls(repmap(rhs)) return lhs_obj, op, rhs_obj match = staticmethod(match) def tostr(self): return '%s %s %s' % tuple(self.items) class SeparatorBase(Base): """ = [ ] : [ ] """ def match(lhs_cls, rhs_cls, string, require_lhs=False, require_rhs=False): line, repmap = string_replace_map(string) if ':' not in line: return lhs,rhs = line.split(':',1) lhs = lhs.rstrip() rhs = rhs.lstrip() lhs_obj, rhs_obj = None, None if lhs: if lhs_cls is None: return lhs_obj = lhs_cls(repmap(lhs)) elif require_lhs: return if rhs: if rhs_cls is None: return rhs_obj = rhs_cls(repmap(rhs)) elif require_rhs: return return lhs_obj, rhs_obj match = staticmethod(match) def tostr(self): s = '' if self.items[0] is not None: s += '%s :' % (self.items[0]) else: s += ':' if self.items[1] is not None: s += ' %s' % (self.items[1]) return s class KeywordValueBase(Base): """ = [ = ] """ def match(lhs_cls, rhs_cls, string, require_lhs = True, upper_lhs = False): if require_lhs and '=' not in string: return if isinstance(lhs_cls, (list, tuple)): for s in lhs_cls: try: obj = KeywordValueBase.match(s, rhs_cls, string, require_lhs=require_lhs, upper_lhs=upper_lhs) except NoMatchError: obj = None if obj is not None: return obj return obj lhs,rhs = string.split('=',1) lhs = lhs.rstrip() rhs = rhs.lstrip() if not rhs: return if not lhs: if require_lhs: return return None, rhs_cls(rhs) if isinstance(lhs_cls, str): if upper_lhs: lhs = lhs.upper() if lhs_cls!=lhs: return return lhs, rhs_cls(rhs) return lhs_cls(lhs),rhs_cls(rhs) match = staticmethod(match) def tostr(self): if self.items[0] is None: return str(self.items[1]) return '%s = %s' % tuple(self.items) class BracketBase(Base): """ = """ def match(brackets, cls, string, require_cls=True): i = len(brackets)/2 left = brackets[:i] right = brackets[-i:] if string.startswith(left) and string.endswith(right): line = string[i:-i].strip() if not line: if require_cls: return return left,None,right return left,cls(line),right return match = staticmethod(match) def tostr(self): if self.items[1] is None: return '%s%s' % (self.items[0], self.items[2]) return '%s%s%s' % tuple(self.items) class NumberBase(Base): """ = [ _ ] """ def match(number_pattern, string): m = number_pattern.match(string) if m is None: return return m.group('value').upper(),m.group('kind_param') match = staticmethod(match) def tostr(self): if self.items[1] is None: return str(self.items[0]) return '%s_%s' % tuple(self.items) def compare(self, other): return cmp(self.items[0], other.items[0]) class CallBase(Base): """ = ( [ ] ) """ def match(lhs_cls, rhs_cls, string, upper_lhs = False, require_rhs=False): if not string.endswith(')'): return line, repmap = string_replace_map(string) i = line.find('(') if i==-1: return lhs = line[:i].rstrip() if not lhs: return rhs = line[i+1:-1].strip() lhs = repmap(lhs) if upper_lhs: lhs = lhs.upper() rhs = repmap(rhs) if isinstance(lhs_cls, str): if lhs_cls!=lhs: return else: lhs = lhs_cls(lhs) if rhs: if isinstance(rhs_cls, str): if rhs_cls!=rhs: return else: rhs = rhs_cls(rhs) return lhs, rhs elif require_rhs: return return lhs, None match = staticmethod(match) def tostr(self): if self.items[1] is None: return '%s()' % (self.items[0]) return '%s(%s)' % (self.items[0], self.items[1]) class CALLBase(CallBase): """ = ( [ ] ) """ def match(lhs_cls, rhs_cls, string, require_rhs = False): return CallBase.match(lhs_cls, rhs_cls, string, upper_lhs=True, require_rhs = require_rhs) match = staticmethod(match) class StringBase(Base): """ = """ def match(pattern, string): if isinstance(pattern, (list,tuple)): for p in pattern: obj = StringBase.match(p, string) if obj is not None: return obj return if isinstance(pattern, str): if len(pattern)==len(string) and pattern==string: return string, return if pattern.match(string): return string, return match = staticmethod(match) def init(self, string): self.string = string return def tostr(self): return str(self.string) def torepr(self): return '%s(%r)' % (self.__class__.__name__, self.string) def compare(self, other): return cmp(self.string,other.string) class STRINGBase(StringBase): """ = """ match = staticmethod(StringBase.match) def match(pattern, string): if isinstance(pattern, (list,tuple)): for p in pattern: obj = STRINGBase.match(p, string) if obj is not None: return obj return STRING = string.upper() if isinstance(pattern, str): if len(pattern)==len(string) and pattern==STRING: return STRING, return if pattern.match(STRING): return STRING, return match = staticmethod(match) class StmtBase(Base): """ [