BlockIt
blocks.py
Go to the documentation of this file.
00001 #............................................................................
00002 # Copyright (c) 2009,2010,2011 David Car, david.car7@gmail.com
00003 # Copyright (c) 2009,2010,2011 Michael List, michael.list@gmail.com
00004 #
00005 # This program is free software; you can redistribute it and/or modify it under
00006 # the terms of the GNU General Public License as published by the Free Software
00007 # Foundation; either version 2 of the License, or (at your option) any later
00008 # version.
00009 #
00010 # This program is distributed in the hope that it will be useful, but WITHOUT
00011 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00012 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00013 #
00014 # You should have received a copy of the GNU General Public License along with
00015 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
00016 # Place, Suite 330, Boston, MA 02111-1307 USA
00017 #..........................................................................
00018 __all__ = ['FortranBlock',
00019            'AFile', 'PyF95Directives',
00020            'Template', 'Specialization',
00021            'Type',
00022            'Subroutine',
00023            'Module',
00024            'Function',
00025            'Program',
00026            'AutoInterface', 'AutoPrivate',
00027            'AutoUse',
00028            'AutoDelete', 'GarbageCollect',
00029            'Enumeration',
00030            'Foreach',
00031            'Instantiate',
00032            'Interface',
00033            'InterfaceA',
00034            'TestRunner', 'ParallelTestRunner',
00035            'createSymMap',
00036            'Pod', 'Prepod', 'isPod', 'isPrepod',
00037            'isFunction', 'isModule', 'isAType', 'isAFile', 'isInterface',
00038            'isProgram', 'isSubroutine', 'isTemplate', 'isAutoInterface',
00039            'isAutoPrivate', 'isTemplated', 'isAutoDelete', 'isRoot',
00040            'hasAsName',
00041            'isPyF95Directives',
00042            'hasTempParent',
00043            'parseTemplateSpecString',
00044            'mergeTemplateAndObjects',
00045            'templateSpecToTuple',
00046            'izip_longest',
00047            'globalSymbolTable',
00048            'globalSymbolMap' ]
00049 
00050 from blockit.funcs import *
00051 from blockit.blocks import Block
00052 from blockit.sets import ASet
00053 from blockit.scanners import LineScanner
00054 from blockit.tables import SymbolTable
00055 from blockit.parsers import pyp as pp
00056 from parsers import FortranBlockParser
00057 from grammars import *
00058 try:
00059     from itertools import izip_longest
00060 except ImportError:
00061     from itertools import chain
00062     from itertools import repeat
00063     from itertools import izip
00064 #    def chain(*iterables):
00065 #        # chain('ABC', 'DEF') --> A B C D E F
00066 #        for it in iterables:
00067 #            for element in it:
00068 #                yield element
00069 #
00070 #    def repeat(object, times=None):
00071 #        # repeat(10, 3) --> 10 10 10
00072 #        if times is None:
00073 #            while True:
00074 #                yield object
00075 #        else:
00076 #            for i in xrange(times):
00077 #                yield object
00078 
00079     def izip_longest(*args, **kwds):
00080         # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
00081         fillvalue = kwds.get('fillvalue')
00082         def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
00083             yield counter()         # yields the fillvalue, or raises IndexError
00084         fillers = repeat(fillvalue)
00085         iters = [chain(it, sentinel(), fillers) for it in args]
00086         try:
00087             for tup in izip(*iters):
00088                 yield tup
00089         except IndexError:
00090             pass
00091 #from blockit.Fortran.pod import isPrepod
00092 
00093 import os
00094 import re
00095 import hashlib
00096 import copy
00097 import pickle
00098 from os.path import basename as baseFileName
00099 
00100 globalSymbolTable = SymbolTable( '|' )
00101 globalSymbolMap = SymbolTable()
00102 globalDependencyTable = SymbolTable()
00103 
00104 #................................................................................
00105 # Replacement functions for stuff not in python 2.5
00106 #................................................................................
00107 # def izip_longest( l1, l2, default ):
00108 #     diff = len( l1 ) - len( l2 )
00109 #     _l = zip( l1, l2 )
00110 #     if diff > 0:
00111 #         _l.extend( [( x, default ) for x in l1[ (len( l1 ) - diff): ]] )
00112 #     elif diff < 0:
00113 #         diff = - diff
00114 #         _l.extend( [( default, x ) for x in l2[ (len( l2 ) - diff): ]] )
00115 
00116 #     return _l
00117 
00118 #................................................................................
00119 # Filter functions for blocks
00120 #................................................................................
00121 isPod = lambda (x,v): isinstance(v, Pod)
00122 isPrepod = lambda (x,v): isinstance(v, Prepod)
00123 isForeach = lambda (x,v): isinstance(v, Foreach)
00124 isFunction = lambda (x,v): isinstance(v, Function)
00125 isModule = lambda (x,v): isinstance(v, Module)
00126 isAType = lambda (x,v): isinstance(v, Type)
00127 isAFile = lambda (x,v): isinstance(v, AFile)
00128 isInterface = lambda (x,v): isinstance(v, Interface)
00129 isProgram = lambda (x,v): isinstance(v, Program)
00130 isSubroutine = lambda (x,v): isinstance(v, Subroutine)
00131 isTemplate = lambda (x,v): isinstance(v, Template)
00132 isAutoInterface = lambda (x,v): isinstance(v, AutoInterface)
00133 isAutoPrivate = lambda (x,v): isinstance(v, AutoPrivate)
00134 isAutoDelete = lambda (x,v): isinstance(v, AutoDelete)
00135 isTemplated = lambda (x,v): v.isTemplated()
00136 isRoot = lambda (x,v): ((v.parent() is None) or (isinstance(v.parent(), AFile)))
00137 isSpecialization = lambda (x,v): isinstance(v, Specialization)
00138 isPyF95Directives = lambda (x,v) : isinstance(v, PyF95Directives)
00139 hasAsName = lambda (x,v): hasattr(v, 'asName') and v.asName is not None
00140 hasTempParent = lambda (x,v): isinstance(v.parent(), Template)
00141 
00142 #================================================================================
00143 # Global functions for template handling
00144 #================================================================================
00145 def parseTemplateSpecString(s):
00146     '''Scan the template string and parse out the defined templates.  Returns
00147     a list of tuples with each type and its template specification.  Uses the
00148     TEMPLATE_DECL grammar.
00149 
00150     Examples:
00151     ---------
00152     >>> parseTemplateSpecString('DLList<List<integer>, Dict<String, List<integer>>>')
00153     >>> [('DLList', 'List<integer>,Dict<String,List<integer>>')]
00154 
00155     >>> parseTemplateSpecString('List<integer>,Dict<String,List<integer>>')
00156     >>> [('List', 'integer'), ('Dict', 'String,List<integer>')]
00157 
00158     '''
00159     return [(x[0], ''.join(x[1][1:-1]))
00160             for x in TEMPLATE_DECL.searchString(s)]
00161 
00162 #================================================================================
00163 def mergeTemplateAndObjects( cls, objs ):
00164     '''Merge a class pairs list with an objects tuple and return a substitution
00165     pair list.  If there is no object to pair with a class, use the default
00166     specified. For example:
00167 
00168     >>> classes = [('object', None), ('element', 'integer')]
00169     >>> objects = ('String', '*')
00170     >>> mergeTemplateAndObjects(classes, objects)
00171     [('object', 'String'), ('element', 'integer')]
00172 
00173     '''
00174     choose = lambda x,y: x!='*' and x or y
00175     if len( objs ) < len( cls ):
00176         if any( [ (x is None)  for x,y in cls[ ( len( objs ) - 1 ): ] ] ):
00177             msg = '''Insufficient template parameter specification and no default values for
00178 missing parameters.'''
00179             raise Exception( msg )
00180     elif len( objs ) > len( cls ):
00181         msg = '''More template parameters specified than are available.'''
00182         raise Exception( msg )
00183 
00184     return [ ( c[ 0 ], ( choose( o, str( c[ 1 ] ) ) ) )
00185             for c,o in izip_longest( cls, objs, fillvalue='*' )]
00186 
00187 #================================================================================
00188 def templateSpecToTuple( string_ ):
00189     '''Explode a template string into its parts and return a tuple of the
00190     components.
00191 
00192     Example:
00193     --------
00194     >>> templateSpecToTuple('integer, AList<integer,real>')
00195     ('integer','AList<integer,real>')
00196 
00197     Input
00198     -----
00199     string_ : template spec string
00200 
00201     '''
00202     GRAMMAR = pp.delimitedList(pp.Combine(VALID_TEMPLATE_PARAM))
00203     # GRAMMAR = pp.delimitedList(VALID_TEMPLATE_PARAM, combine=True)
00204     return tuple(GRAMMAR.parseString(string_).asList())
00205 
00206 #================================================================================
00207 def createSymMap( table ):
00208     '''Loop through all the files blocks and resolve the symbols in the
00209     dependency lists.  Return a dictionary of names with mappings
00210 
00211     '''
00212 
00213     files = table.filter( isAFile )
00214     symMap = {}
00215 
00216     for blk in files.values():
00217         symMap.update( map( lambda x: ( x, blk.symbolTuple( x ) ),
00218                             [ x for x in blk.dependencies() ] ) )
00219 
00220     return symMap
00221 
00222 #=========================
00223 # Derived Block classes
00224 #=========================
00225 class FortranBlock(Block):
00226     #................................................................................
00227     # Intrinsic types list
00228     #................................................................................
00229     intrinsicList = ['integer', 'double', 'character', 'logical', 'complex',
00230                      'real']
00231 
00232     #................................................................................
00233     # pyparsing grammars for internal dependencies within a block
00234     #................................................................................
00235     BLOCKTYPE = 'FortranBlock'
00236     SENTINEL = None
00237     VERSION = '1.3.1'
00238     #................................................................................
00239 
00240     def __init__(self, blockType, parameters, childToken=None):
00241         '''Base FortranBlock class that all F90/95/2003 block types should inherit from.
00242 
00243         '''
00244         name = parameters[0]
00245         self._modLine = ''
00246         self._parameters = parameters
00247         self._objects = ASet()
00248         self._template = None
00249         self._use = ASet()
00250         self._types = SymbolTable()
00251         self._types['By Type'] = SymbolTable()
00252         self._types['By Symbol'] = SymbolTable()
00253         self._sigs = SymbolTable()
00254         self._version = FortranBlock.VERSION
00255         self._name_length = 31
00256 
00257         super(FortranBlock, self).__init__(blockType, name, childToken=childToken)
00258 
00259         #............................................................
00260         # Set parse actions for the dependency grammars
00261         #............................................................
00262         self.DEP_GRAMMAR = \
00263             ( TEMPLATE_TYPE.copy() ).setParseAction( self.__addType ) | \
00264             ( TEMPLATE_SUB.copy() ).setParseAction( self.__addSub ) | \
00265             ( TEMPLATE_FUNC.copy() ).setParseAction( self.__addFunc ) | \
00266             ( TEMPLATE_MOD_PROCEDURE_DECL.copy() ).setParseAction( self.__addProc ) | \
00267             ( INTRINSIC_TYPE.copy() ).setParseAction( self.__addIntrinsic ) | \
00268             ( USE_DECL.copy() ).setParseAction( self.__addUse )
00269         self.SHOW_GRAMMARS = \
00270             ( TEMPLATE_DECL('name') ).setParseAction( self.__mangleShow ) | \
00271             ( self.CHILDTOKEN.copy() ).setParseAction( self.__spawnChildShow )
00272 
00273     def getSymMap( self, table ):
00274         '''Get the symbol map for the block.
00275 
00276         '''
00277         return globalSymbolMap
00278 
00279     def version( self ):
00280         '''Return the block version number.
00281 
00282         '''
00283         return getattr( self, '_version', FortranBlock.VERSION )
00284 
00285     def contains( self, sym, skipList=None ):
00286         '''Search to see if the block has a symbol.
00287 
00288         '''
00289         if not self.isTemplated():
00290             if sym in self._children:  return True
00291             if skipList is not None:
00292                 _children = [ x
00293                               for x in self._children.filter( Or( isModule,
00294                                                                   Or( isTemplate,
00295                                                                       isPrepod ) ) ).values()
00296                               if x not in skipList ]
00297             else:
00298                 _children = self._children.filter( Or( isModule,
00299                                                        Or( isTemplate,
00300                                                            isPrepod ) ) ).values()
00301             for child in _children:
00302                 if child.contains( sym, skipList ): return True
00303         return False
00304 
00305     def addLine( self, line, n=None ):
00306         '''Method to add a text line to a block code listing.  If the optional
00307         line number parameter is given, then the line is inserted AFTER the
00308         given line number.
00309 
00310         Input
00311         -----
00312         line : text line to add to code listing
00313         n : line number in listing to insert after.  Negative numbers insert
00314             from the end of the code listing.
00315 
00316         '''
00317         #......................................................................
00318         # Look for dependencies and propagate up the tree only if this is not
00319         # a templated block.  The reason is that templated blocks are
00320         # resolved in a second phase since they contain unresolved
00321         # declarations.  So, first any blocks that aren't templated may
00322         # declare an insantiation of a templated type.  These instantiations
00323         # are then propogated to the templated block.  After that, the block
00324         # can resolve all symbols and all instantiations can then be
00325         # resolved.  Dependencies for templated blocks are then known.
00326         #......................................................................
00327         self._modLine = line
00328         if not line.lstrip().startswith( '!' ) and not self.isTemplated():
00329             ans = self.DEP_GRAMMAR.searchString( line )
00330 
00331         self._addLine( n )
00332 
00333     def _addLine( self, n ):
00334         '''Add the internal _modLine to the code listing.
00335 
00336         '''
00337         if n is not None:
00338             self._code.insert( n, self._modLine )
00339         else:
00340             self._code.append( self._modLine )
00341 
00342     def __addType( self, s, l, t ):
00343         '''Add a scanned type declaration from a given pyparsed line.
00344 
00345         '''
00346         name = pack( t.name )
00347         _types = self._types
00348         self.addDeps( name, propagate=False )
00349         self._modLine = s.replace( t.name, name )
00350         if not _types[ 'By Type' ].has_key( name ):
00351             _types[ 'By Type' ][ name ] = []
00352         _types[ 'By Type' ][ name ].extend( t.symbolList )
00353         _types[ 'By Symbol' ].update( izip_longest( t.symbolList,
00354                                                     ( name,),
00355                                                     fillvalue=name ) )
00356     def __addIntrinsic( self, s, l, t ):
00357         '''Add a scanned instrinsic type declaration from a given pyparsed line.
00358 
00359         '''
00360         name = pack( t.name )
00361         if not self._types[ 'By Type' ].has_key( name ):
00362             self._types[ 'By Type' ][ name ] = []
00363         self._types[ 'By Type' ][ name ].extend( t.symbolList )
00364         self._types[ 'By Symbol' ].update( izip_longest( t.symbolList,
00365                                                         ( name, ),
00366                                                         fillvalue=name ) )
00367 
00368     def __addFunc( self, s, l, t ):
00369         '''Add a templated function call from a given pyparsed line.
00370 
00371         '''
00372         name = pack( t.name )
00373         self.addDeps( name, propagate=False )
00374         if t.args: self._sigs[ name ] = (t.args).asList()
00375         self._modLine = self._modLine.replace( t.name, name )
00376 
00377     def __addProc( self, s, l, t ):
00378         '''Add a procedure declaration from a given pyparsed line.
00379 
00380         '''
00381         name = pack( t.name )
00382         self.addDeps( name, propagate=False )
00383         self._modLine = self._modLine.replace( t.name, name )
00384 
00385     def __addSub( self, s, l, t ):
00386         '''Add a templated subroutine call from a given pyparsed line.
00387 
00388         '''
00389         name = pack( t.name )
00390         self.addDeps( name, propagate=False )
00391         if t.args: self._sigs[ name ] = t.args.asList()
00392         self._modLine = self._modLine.replace( t.name, name )
00393 
00394     def __addUse( self, s, l, t ):
00395         '''Add a scanned `use` declaration from a given pyparsed line.  Add to
00396         dependencies if its a templated use declaration.
00397 
00398         '''
00399         name = pack( t.name )
00400         self._use.add( name )
00401         if TEMPLATE_TOKENS[ 0 ] in name:
00402             self.addDeps( name, propagate=False )
00403         self._modLine = self._modLine.replace( t.name, name )
00404 
00405     def __addImport( self, s, l, t ):
00406         '''Add a scanned `import` declaration from a given pyparsed line.
00407 
00408         '''
00409         name = pack( t.name )
00410         self._modLine = self._modLine.replace( t.name, name )
00411 
00412     def rootNotAFile(self):
00413         if self._parent and not isinstance( self._parent, AFile ):
00414             return self._parent.rootNotAFile()
00415         return self
00416 
00417     def getState(self):
00418         '''Return the internal _state variable.
00419 
00420         '''
00421         return copy.copy( self._state )
00422 
00423     def setState(self, subs):
00424         '''Set the internal state variable _state.  Can be used for anything.
00425 
00426         '''
00427         self._state = copy.copy( subs )
00428 
00429     def isTemplated(self):
00430         '''Return true if the internal _template attribute is not None.  If
00431         the block has a parent, then call the parent isTemplated() method.
00432         A block is templated if it exists within a template block
00433 
00434         '''
00435         if self._template: return True
00436         if self._parent:
00437             return self._parent.isTemplated()
00438         return False
00439 
00440     def template(self):
00441         '''Return the _template attribute if this block has one.  If it
00442         does not and has a parent, the call the parents template() method,
00443         thereby walking up the tree.
00444 
00445         '''
00446         if self._template:  return self._template
00447         if self._parent and not isinstance( self._parent, AFile ):
00448             return self._parent.template()
00449 
00450     def addObjects(self, obj):
00451         '''Add a list of template specs to the internal objects set if this is
00452         a template block.  If not, propagate up the tree to find the parent
00453         template block.
00454 
00455         '''
00456         if self._template:
00457             self._objects.add(obj)
00458         elif self._parent:
00459             self._parent.addObjects(obj)
00460         else:
00461             raise Exception("Trying to add objects to a non-templated block!")
00462 
00463     def show(self, mangle=False):
00464         '''Show a realization of a block based on its current state.
00465 
00466         '''
00467         table = globalSymbolTable
00468         self._mangle = mangle
00469         block = ''
00470         for line in self._code:
00471             self._modLine = line
00472             ans = self.SHOW_GRAMMARS.searchString( line )
00473             block += self._modLine
00474         return block
00475 
00476     def __mangleShow( self, s, l, t ):
00477         '''Mangle the template tokens and set internal _modLine.
00478 
00479         '''
00480         _sym = ''.join( t.name )
00481         new_name = self.resolveSymbol( _sym )
00482         self._modLine = self._modLine.replace( _sym, new_name )
00483 
00484         # self.SHOW_GRAMMARS.searchString( self._modLine )
00485         # newName = self.resolveSymbol( t.name )
00486         # self._modLine = self._modLine.replace( t.name, newName )
00487 
00488     def __spawnChildShow( self, s, l, t ):
00489         '''Spawn the show() method of a child token and set internal _modLine
00490 
00491         '''
00492         sym = t.symbol
00493         _blk = self._children.get(sym, None)
00494         if _blk is None:
00495             print 'Child %s has disappeared from block %s'%(sym, self.name())
00496             self._modLine = ''
00497         else:
00498             self._modLine = _blk.show(self._mangle)
00499 
00500     def resolveInternal(self):
00501         '''Resolve the internal child dependencies of instantiated templated types for
00502         a given block so that the appear in the proper order internally within
00503         the block.
00504 
00505         '''
00506         #.....................................................................
00507         # 1. Get all children who are templates and instantiated, i.e. is a
00508         #    Template type but is not templated (brain melting).
00509         # 2. Create a full symbol table from these blocks.
00510         # 3. Resolve the order same as for files using topoSort.
00511         # 4. Reorder the lines in the file.
00512         # 5. Visit each child and goto 1.
00513         #.....................................................................
00514         loc = []
00515         childList = [self]
00516         inTable = SymbolTable()
00517 
00518         while len(childList) > 0:
00519             _blk = childList.pop(0)
00520             code = _blk.codeList()
00521             temps = _blk.children().filter(And(isTemplate,
00522                                                Not(isTemplated)))#.values()
00523             inTable.registerAll(temps)
00524             childList.extend(_blk.children().filter(Not(isTemplated)).values())
00525 
00526             if not temps: continue
00527 
00528             #....................................................................
00529             # Instantiated templates found.  Build diGraph and get ordering.  Use
00530             # insertion sort to reorganize blocks internally.
00531             #....................................................................
00532             diGraph = depGraph(temps.values(), inTable)
00533             order = topoSort(diGraph, prune=True)
00534 
00535             # The current code listing
00536             for t in order:
00537                 loc.append((code.index(_blk.childString(t)), t))
00538 
00539             # Insertion sort... expensive, but list is generally small, so O(n^2)
00540             # is not too expensive
00541             while len(loc) > 0:
00542                 n1, name = loc.pop(0)
00543                 for i,pair in enumerate(loc):
00544                     n2, next = pair
00545                     # Swap code lines
00546                     if n1 > n2:
00547                         code[n1], code[n2] = code[n2], code[n1]
00548                         loc[0] = (n1, next)
00549                         break
00550 
00551             inTable.clear()
00552 
00553     @staticmethod
00554     def mangle(name, width=31, tempTokens=TEMPLATE_TOKENS):
00555         '''Generate a mangled name from a name and and a template string.
00556 
00557         Long names are contracted to fit within `width` argument size.
00558 
00559         '''
00560         def shorten( s, max_length ):
00561             s = '_'.join( [ x[ :-1 ] for x in s.split( '_' ) ] )
00562             if len( s ) > max_length:
00563                 return shorten( s, max_length )
00564             return s
00565 
00566         tok1, tok2 = tempTokens
00567         i = name.find( tok1 )
00568         if i == -1: return name
00569 
00570         _name = name[ :i ]
00571         _s = name[ i: ] #_s = tok1 + s + tok2 # Encase in template characters
00572         temp = pack(_s).replace(tok1,'_').replace(tok2,'')
00573         temp = temp.replace(',','_').replace('(','_').replace(')','')
00574         temp = temp[1:]  # skip first _ character
00575 
00576         sz = width - len(_name) - 1
00577         if len(temp) > sz:
00578             if sz <= 0: raise Exception("Symbol width " + str(width) + " not adequate for unique name!")
00579             temp = shorten( temp, width - len(_name) - 1 )
00580 
00581         return '_'.join( [ _name, temp ] )
00582 
00583     def resolveSymbol( self, sym, mangle=True ):
00584         '''Resolve a templated symbol name.  This should only be used after
00585         the globalSymbolMap has been generated.  Otherwise, it will be
00586         expensive if called many times with symbols that have wildcards (*) or
00587         missing template parameters.  If the symbol is not already in the
00588         globalSymbolMap, then add it after resolving the symbol, i.e. cache it
00589         for future use.
00590 
00591         '''
00592         if sym not in globalSymbolMap:
00593             _sym, _sym_mangle = self.symbolTuple( sym )
00594             globalSymbolMap[ sym ] = ( _sym, _sym_mangle )
00595         else:
00596             _sym, _sym_mangle = globalSymbolMap[ sym ]
00597 
00598         if mangle: return _sym_mangle
00599         return _sym
00600 
00601     def __resolveSymbol( self, sym ):
00602         '''Resolve a symbol name, i.e. search for wildcards in a template
00603         string and substitute the wildcards with the appropriate default
00604         value as well as missing template parameters
00605 
00606         sym : symbol name to resolve
00607 
00608         '''
00609         _sym = parseTemplateSpecString(pack(sym))
00610         if _sym:
00611             # Resolve wildcards if necessary
00612             name, spec = _sym[0]
00613             #..............................................................
00614             # Hack:  Needed to avoid trying to resolve 'template <a,b,c>'
00615             # statements within Specialization blocks
00616             #..............................................................
00617             if name in RESERVED_KEYWORDS: return sym
00618 
00619             table = globalSymbolTable
00620             jc = table.joinCharacter()
00621             blk = table.filter(startsWith(name+jc)).values()
00622             if len( blk ) != 1:
00623                 blk = filter( lambda x: x.isTemplated(), blk )
00624                 if len( blk ) > 1:
00625                     print "Found (%d) possible symbols:"%(len(blk),)
00626                     for i,b in enumerate(blk):
00627                         error_msg = (i,b.name(),
00628                                      b.parent().name(),
00629                                      b.upTo(AFile).fileName() )
00630                         print "(%d) (%s) in block (%s) in file (%s) "% error_msg
00631                     raise Exception("Cannot resolve symbol %s"%(sym,))
00632                 elif len( blk ) == 0:
00633                     return pack( sym )
00634 #                     if sym in self._depIgnore:
00635 #                         return pack( sym )
00636 #                     else:
00637 #                         print 'ignores: ', self._depIgnore
00638 #                         raise Exception("Cannot resolve symbol %s"%(sym,))
00639             blk = blk[0]
00640             diff = len( blk.template() ) - len( templateSpecToTuple( spec ) )
00641             spec += ',*' * diff
00642 
00643             try:
00644                 pairs = mergeTemplateAndObjects(blk.template(),
00645                                                 templateSpecToTuple(spec))
00646             except Exception as inst:
00647                 print inst
00648                 msg = "When trying to resolve symbol (%s)"%(sym,)
00649                 raise Exception(msg)
00650 
00651             #.....................................................................
00652             # Recursive call to resolve wildcards and missing template parameters
00653             #.....................................................................
00654             _pairs = []
00655             for x,y in pairs:
00656                 y = self.__resolveSymbol( y )
00657                 _pairs.append( y )
00658             #......................................
00659             spec = ','.join(_pairs)
00660 
00661             return name + TEMPLATE_TOKENS[ 0 ] + spec + TEMPLATE_TOKENS[ 1 ]
00662 
00663         return sym
00664 
00665     def symbolTuple( self, x ):
00666         '''Given a template symbol 'x' (or regular symbol), return a tuple with
00667         that contains the symbol with all wildcards and missing template
00668         parameters resolved and the mangled version of the symbol.
00669 
00670         '''
00671         sym = self.__resolveSymbol( x )
00672         return ( sym, self.mangle( sym, width=self._name_length ) )
00673 
00674     def name( self, tempTokens=TEMPLATE_TOKENS ):
00675         '''Return the name of the block.
00676 
00677         '''
00678         if self._state:
00679             temp = ','.join([obj for k,obj in self._state])
00680             name = self._name + tempTokens[0] + temp + tempTokens[1]
00681             return FortranBlock.mangle( name, width=self._name_length, tempTokens=tempTokens )
00682 
00683         return self._name
00684 
00685     def useDeclarations( self ):
00686         '''Return the set of use declarations.
00687 
00688         '''
00689         return self._use.copy()
00690 
00691     def __getstate__( self, _dd=None ):
00692         if _dd is None: _dd = self.__dict__.copy()
00693         _dd.pop('DEP_GRAMMAR')
00694         _dd.pop('SHOW_GRAMMARS')
00695         return super( FortranBlock, self ).__getstate__(_dd)
00696 
00697     def serialize( self, _dd=None ):
00698         '''Builds a dictionary tree of the block.
00699 
00700         '''
00701         return self.__getstate__(_dd)
00702 
00703     def __setstate__( self, tree ):
00704         '''Unpack the dictionary tree.
00705 
00706         '''
00707         g = globals()
00708         self.__init__( tree[ '_parameters' ] )
00709         self.__dict__.update( tree )
00710         _children = self._children
00711         self._children = SymbolTable()
00712 
00713         for node in _children:
00714             _blockType = node[ '_blockType' ]
00715             _params = node[ '_parameters' ]
00716             child = g[ _blockType ]( _params )
00717             child.__setstate__( node )
00718             child.setParent( self )
00719             self._children.register( child )
00720 
00721     def getSig( self, name ):
00722         '''Return the call signature for the given templated
00723         function/subroutine name that this block calls.
00724 
00725         '''
00726         _args = self._sigs.get( name, None )
00727 
00728         # Cross reference the internal symbols and return a lists of types
00729         # for the signature
00730         if _args:
00731             return [ self.findSymbol( x ) for x in _args ]
00732 
00733     def sentinel( self, line ):
00734         '''Overrided sentinel method. Calls super first.  If true, then looks
00735         for AutoDelete/Specialization blocks in scope and merges symbols and
00736         types found in these blocks with its own symbols and types.
00737 
00738         '''
00739         _ans = super( FortranBlock, self ).sentinel( line )
00740         if _ans:
00741             for x in self._children.filter( Or( isAutoDelete,
00742                                                 isSpecialization ) ).values():
00743                 self._types['By Symbol'].update( x.symbols )
00744                 self._types['By Type'].update( x.types )
00745         return _ans
00746 
00747     def findSymbol( self, sym ):
00748         '''Look for a type symbol in the block.  If not found, check the
00749         enclosing block.
00750 
00751         '''
00752         val = self._types['By Symbol'].get( sym, None )
00753         if val is None and self._parent: return self._parent.findSymbol( sym )
00754         return ( val is None and 'character' or val )
00755 
00756     def setNameLength( self, name_length ):
00757         """Set the max number of characters for mangled templated names.
00758 
00759         Arguments:
00760         - `name_length` : integer number of max characters
00761         """
00762         self._name_length = name_length
00763 
00764     def getNameLength( self ):
00765         """Return the max number of characters for mangled templated names.
00766 
00767         """
00768         return self._name_length
00769 
00770     def addChild( self, child, token=None, before=False, regen=True ):
00771         '''Overrided addChild method which simply is needed to set the
00772         internal _name_length parameter to that of the parent.
00773 
00774         '''
00775         super( FortranBlock, self ).addChild( child, token, before, regen )
00776         child.setNameLength( self._name_length )
00777 
00778     @staticmethod
00779     def intrinsicType(s):
00780         '''Function returning a boolean whether any of the defined built-in types
00781         are found in the given string.
00782 
00783         '''
00784         x = s.strip().split('(')[0]
00785 
00786         return any([(x in b) for b in FortranBlock.intrinsicList])
00787 
00788     #..........................................................................
00789     # Properties
00790     #..........................................................................
00791     def __propTypes():
00792         doc = "SymbolTable of types declared within block"
00793         def fget( self ):
00794             return self._types[ 'By Type' ]
00795         return locals()
00796 
00797     def __propSymbols():
00798         doc = "SymbolTable of type symbols declared within block"
00799         def fget( self ):
00800             return self._types[ 'By Symbol' ]
00801         return locals()
00802 
00803     types = property( **__propTypes() )
00804     symbols = property( **__propSymbols() )
00805 
00806 #................................................................................
00807 class AFile( FortranBlock ):
00808     # No GRAMMAR for file block
00809     GRAMMAR = None
00810     SENTINEL = None
00811     BLOCKTYPE = 'AFile'
00812 
00813     HASHTOKEN_STRING =  '!PyF95.hash: '
00814     SYMBOLTOKEN_STRING = '!PyF95.symbol: '
00815     HASHGRAMMAR = pp.Literal(HASHTOKEN_STRING)
00816     SYMBOLGRAMMAR = pp.Literal(SYMBOLTOKEN_STRING)
00817 
00818     def __init__(self, parameters):
00819         '''A file block.
00820 
00821         '''
00822         # Transform the name (basename of the filename)
00823         self._fileName = parameters[0]
00824         self._sourceFileName = parameters[0]
00825         parameters[0] = baseFileName(self._fileName)
00826         self._hash = parameters[1]
00827         super(AFile, self).__init__(self.BLOCKTYPE, parameters)
00828 
00829     def fileName(self):
00830         return self._fileName
00831 
00832     def sourceFileName(self):
00833         '''This returns the original file name that was parsed.  This is
00834         needed because many of the global templates have their file names
00835         changed so that they are written within the source tree.  The library
00836         needs the original file name to be able to associate instantiated
00837         templates that may be in this file with its source.
00838 
00839         '''
00840         return self._sourceFileName
00841 
00842     def setFileName(self, name):
00843         self._fileName = name
00844 
00845     def addFileDep( self, name ):
00846         self._crossFileDeps.add( name )
00847 
00848 #................................................................................
00849 class EmptyFile( AFile ):
00850     # No GRAMMAR for file block
00851     GRAMMAR = None
00852     SENTINEL = None
00853     BLOCKTYPE = 'EmptyFile'
00854 
00855 #................................................................................
00856 class Template(FortranBlock):
00857     #....................................................
00858     # pyparsing grammar
00859     #....................................................
00860     BLOCKTYPE = 'Template'
00861     KEYWORD =  pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
00862     TEMPLATE_PARAM_TYPE = pp.CaselessKeyword('class')('type')
00863     DEFAULT_ASSIGNMENT = pp.Suppress('=')
00864     TEMPLATE_DECL_COMB = pp.Combine(TEMPLATE_DECL, adjacent=False)
00865     VALIDNAME_WITH_PAREN_COMB = pp.Combine(VALIDNAME_WITH_PAREN, adjacent=False)
00866     TEMPLATE_ITEM = \
00867         pp.Group(
00868         TEMPLATE_PARAM_TYPE+\
00869         VALIDNAME('name')+\
00870         pp.Optional(
00871             DEFAULT_ASSIGNMENT+\
00872                 (TEMPLATE_DECL_COMB|
00873                  VALIDNAME_WITH_PAREN_COMB|
00874                  DECIMAL_NUM|
00875                  NUM)('default')))
00876     GRAMMAR = \
00877         pp.StringStart() +\
00878         KEYWORD +\
00879         pp.Suppress(LEFT_TEMPTOK)+\
00880         pp.Group(pp.delimitedList(TEMPLATE_ITEM))('specs')+\
00881         pp.Suppress(RIGHT_TEMPTOK)
00882     SENTINEL = \
00883         pp.StringStart() +\
00884         pp.CaselessKeyword('end') +\
00885         KEYWORD
00886     #................................................................................
00887 
00888     @staticmethod
00889     def factory(s, lineNo, tokens):
00890         '''Factory method called by the BlockParser to produce an
00891         instance of the class.
00892 
00893         '''
00894         pairs = [(x.name, (x.default or None)) for x in tokens.specs]
00895 
00896         return Template([pairs])
00897 
00898     def __init__(self, parameters, name=None):
00899         '''A Template block.  Template blocks begin with the line:
00900 
00901         template < ... >
00902 
00903         where ... is a list of template parameters starting with the keyword
00904         class.  For example,
00905 
00906         template <class A, class B>
00907         .
00908         .
00909         .
00910         end template
00911 
00912         is the start of a template block with two internal template paramters
00913         A and B.
00914 
00915         Input
00916         -----
00917         parameters : the groups from the factory static method
00918         name : optional name to give the template
00919 
00920         '''
00921 
00922         # There is no "name" for a template, so used the id() function to
00923         # generate a unique one if this is a template block.
00924         template = parameters[0]
00925         if not name:
00926             parameters = ['template_'+str(id(self))]
00927         else:
00928             parameters = [name]
00929 
00930         super(Template, self).__init__(self.BLOCKTYPE, parameters)
00931         self._template = template
00932         self._name_length = 256
00933 
00934     def _createTemplatePairs(self, tokens):
00935         """Create template pairs from the pyparsed tokens list.
00936 
00937         Arguments:
00938         - `self`:
00939         - `tokens`: nested tokens list from pyparsing
00940         """
00941 
00942         if len(tokens) > 0:
00943             return [(x.name, x.default) for x in tokens[0]]
00944         return []
00945 
00946     def isTemplated(self):
00947         '''Return True if the code block is templated or within a templated
00948         code block.
00949         '''
00950         if self._template:  return True
00951         return False
00952 
00953     def sentinel(self, line):
00954         '''Overrided sentinel method to strip first and last lines from code
00955         block.
00956 
00957         '''
00958         if self.SENTINEL.searchString(line):
00959             self._code.pop(0)
00960             return True
00961 
00962     def template(self):
00963         '''Return the template specification string of the template.
00964 
00965         '''
00966         return self._template
00967 
00968     def isTemplated(self):
00969         '''Returns True if template block is templated, i.e. it has a template
00970         specification.  Instantiated template blocks are template blocks that
00971         do not have template specs so they will return false.
00972 
00973         '''
00974         if self._template: return True
00975         return False
00976 
00977     def addObjects(self, obj):
00978         '''Added a specification set for the template, e.g. if the template
00979         specification is <class Object, class Element>, then if you want
00980         Object to be 'integer' and Element to be 'real' obj would be in the
00981         argument list would be ('integer', 'real').
00982 
00983         obj : specification of a template, e.g. ('integer', 'real') for the
00984               template string <class Object, class Element>.
00985 
00986         '''
00987         self._objects.add(obj)
00988 
00989     def show(self, mangle=False, force=False):
00990         '''Overloaded show method for templates.  Only show template if it is
00991         a realized template, i.e. the _template attribute is None.  This can
00992         be overridden with the force parameter to show it anyway.
00993 
00994         '''
00995         if not self._template or force:
00996             return super(Template, self).show(mangle)
00997         return ''
00998 
00999     def hash(self):
01000         '''Overloaded hash method for this block if it does not already have a
01001         _hash internal value.  Uses the mangled version for the hash.  The
01002         hash value is:
01003 
01004         <the text hash>/<template parameters>
01005 
01006         '''
01007         hsh = getattr(self, '_hash', None)
01008         if hsh is None:
01009             self._hash = hashlib.sha256(self.show(True, True)).hexdigest()
01010 
01011         if self._state:
01012             return '%s/<%s>'%(self._hash,','.join([y for x,y in self._state]))
01013         else:
01014             return self._hash
01015 
01016     def name(self):
01017         '''Name of templates does not adhere to 31 character limit
01018 
01019         '''
01020         return super(Template, self).name()
01021 
01022     def instantiate( self, objects, cache ):
01023         '''This method only exists for realizations of templated blocks.  The
01024         setState() method is called internally with the provide list of
01025         template specifications which sets the template parameter states.
01026         Then this method creates a realization of a template block using those
01027         parameters and returns it.
01028 
01029         objects : a template parameter list, e.g ('*','integer','*')
01030                   for a three parameter template with default values used for
01031                   the first and last value or ('integer', 'real') for a two
01032                   parameter list.  The wildcards are resolved internally.
01033 
01034         cache : and instance of FortranCacheManager to check for instantiated
01035                 templates
01036 
01037         '''
01038         global globalSymbolMap, subDict
01039 
01040         #.....................................................
01041         # Instantiate children of template block
01042         #.....................................................
01043         def _instantiate( tempBlk, blk ):
01044             #............................................................
01045             # Go through code and spawn children while parsing.  Use
01046             # addChild() AFTER parsing to propagate dependencies to the
01047             # parent.
01048             #............................................................
01049             global subDict
01050 
01051             totalLines = len( tempBlk._code )
01052             for lineNo,line in enumerate( tempBlk._code ):
01053                 if lineNo == 0 or lineNo == totalLines-1:
01054                     # Note:  Trick adding leading spaces
01055                     line = line.replace(' '+tempBlk.baseName(), ' '+blk.name())
01056 
01057                 isChild = tempBlk.CHILDTOKEN.searchString( line )
01058                 if isChild:
01059                     name = isChild[0].symbol
01060 
01061                     child = tempBlk.children()[ name ]
01062                     child.setState( tempBlk.getState() )
01063 
01064                     _params = list( child._parameters ) # parent block parameters
01065                     _params[0] = child.name()           # name of new block
01066 
01067                     newBlk = type( child )( _params )
01068                     newBlk.setNameLength( child.getNameLength() )
01069                     newBlk = _instantiate( child, newBlk )
01070                     blk.addChild( newBlk,
01071                                   child.childString( child.baseName() ) )
01072                     line = '' # empty string
01073                 else:
01074                     for k,v in subDict.items():
01075                         if line.lstrip().startswith( k ) and not \
01076                                 blk.intrinsicType( v ):
01077                             line = line.replace( k, 'type ('+ v +')')
01078                         else:
01079                             line = line.replace( k, v )
01080 
01081                 if line: blk.addLine( line )
01082 
01083             # blk._hash = tempBlk.hash()
01084             tempBlk.setState( None )
01085             blk.setNameLength( tempBlk.getNameLength() )
01086             return blk
01087 
01088         #.....................................................................
01089         # Setup the new template block (that really isn't templated, but just
01090         # a container for its children) and call the recursive _instantiate()
01091         # Note:  Must reset the state after instantiation to ensure there are
01092         #        no name conflicts when re-populating SymbolTables.
01093         #
01094         # Need to resolve object names if they contain wildcards.  This is
01095         # done by calling resolveSymbol() on each object in the list.
01096         #.....................................................................
01097         try:
01098             diff = len( self.template() ) - len( objects )
01099             _objects = list( copy.copy( objects ) )
01100             _objects.extend( [ '*' ] * diff )
01101             _objects = [self.resolveSymbol(o, mangle=False) for o in _objects]
01102         except Exception as inst:
01103             msg = inst.args
01104             print msg
01105             msg = '''Failed while trying to instantiate %s with parameters
01106             %s''' % ( self.name(), str( objects ) )
01107             raise Exception( msg )
01108 
01109         pairs = mergeTemplateAndObjects( self.template(), _objects )
01110         self.setState( pairs )
01111         subDict = {}
01112         subDict.update( self.getState() )
01113 
01114         #......................................................................
01115         # Check to see if there is a cached version of the template.
01116         # The dictionary must be gotten before setting the templates state so
01117         # that the hash is correct to get the dictionary.  Once you get
01118         # the dictionary, then set the state and use that hash as the key into
01119         # the dictionary.
01120         #......................................................................
01121         _inCache = False
01122         _blk = cache.fromCache( self )
01123 
01124         if _blk:
01125             print '-> Found cached instantiation...'
01126             _inCache = True
01127             _blk.fromCache = True
01128         else:
01129             _blk = type( self )([None], name=self.name())
01130             _blk.setNameLength( self.getNameLength() )
01131             _blk = _instantiate( self, _blk )
01132             _blk.fromCache = False
01133 
01134         self._parent.addChild( _blk, self.childString( self.baseName() ) )
01135 
01136         #...................................................................
01137         # Set the internal _hash value of the instantiated template to the
01138         # template it was generated from.
01139         #...................................................................
01140         self.setState( None )
01141         if not _inCache: cache.toCache( _blk )
01142 
01143         # globalSymbolMap.update( map( lambda x: ( x, _blk.symbolTuple( x ) ),
01144         #                              [ x for x in _blk.dependencies() ] ) )
01145         return _blk
01146 
01147 #................................................................................
01148 class Program(FortranBlock):
01149     #....................
01150     # pyparsing grammar
01151     #....................
01152     BLOCKTYPE = 'Program'
01153     KEYWORD = pp.CaselessKeyword(BLOCKTYPE)
01154     GRAMMAR = \
01155         pp.StringStart() +\
01156         KEYWORD +\
01157         VALIDNAME('name')
01158     #......................................
01159 
01160     @staticmethod
01161     def factory(s, lineNo, tokens):
01162         '''Factory method called by the BlockParser to produce an
01163         instance of the class.
01164 
01165         '''
01166         return Program([tokens.name])
01167 
01168     def __init__(self, parameters):
01169         '''A F90/95/2003 program block.
01170 
01171         program foo
01172         .
01173         .
01174         .
01175         end program foo
01176 
01177         Input
01178         -----
01179         parameters : the groups from the factory static method
01180 
01181         '''
01182         name = parameters[0]
01183         super(Program, self).__init__(self.BLOCKTYPE, parameters)
01184 
01185     def getState(self):
01186         return "Stub:  Program getState"
01187 
01188 #................................................................................
01189 class Module(FortranBlock):
01190     #.....................................................
01191     # pyparsing grammar
01192     #.....................................................
01193     BLOCKTYPE = 'Module'
01194     KEYWORD = pp.CaselessKeyword(BLOCKTYPE)
01195     GRAMMAR = \
01196         pp.StringStart() +\
01197         KEYWORD +\
01198         VALIDNAME('name') +\
01199         (pp.StringEnd()|pp.Literal(';'))
01200     #................................................................................
01201 
01202     @staticmethod
01203     def factory(s, lineNo, tokens):
01204         '''Factory method called by the BlockParser to produce an
01205         instance of the class.
01206 
01207         '''
01208         return Module([tokens.name])
01209 
01210     def __init__(self, parameters):
01211         '''A F90/95/2003 Module block:
01212 
01213         module foo
01214         .
01215         .
01216         .
01217         end module foo
01218 
01219         Input
01220         -----
01221         parameters : the groups from the factory static method
01222 
01223         '''
01224         super(Module, self).__init__(self.BLOCKTYPE, parameters)
01225 
01226 #................................................................................
01227 class Function(FortranBlock):
01228     #....................
01229     # pyparsing grammar
01230     #....................
01231     BLOCKTYPE = 'Function'
01232     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01233     RECURSIVE = pp.Suppress(pp.CaselessKeyword('recursive'))
01234     ELEMENTAL = pp.Suppress(pp.CaselessKeyword('elemental'))
01235     RESULT = \
01236         pp.Suppress(pp.CaselessKeyword('result')) +\
01237         pp.Suppress('(') +\
01238         VALIDNAME('resName') +\
01239         pp.Suppress(')')
01240     VALID_ASNAME = pp.Word(pp.alphas, pp.alphanums + '_./()=<>*+-')
01241     AS_CLAUSE = pp.Suppress('as')+VALID_ASNAME('asName')
01242     NO_MANGLE = pp.Word('no_mangle')('noMangle')
01243     GRAMMAR = \
01244         pp.StringStart() +\
01245         pp.Optional(RECURSIVE|ELEMENTAL) +\
01246         KEYWORD +\
01247         VALIDNAME('funcName') +\
01248         ARGLIST('args') +\
01249         pp.Optional(RESULT) +\
01250         pp.Optional(AS_CLAUSE)+\
01251         pp.Optional(NO_MANGLE)
01252     #................................................................................
01253 
01254     @staticmethod
01255     def factory(s, lineNo, tokens):
01256         '''Factory method called by the BlockParser to produce an
01257         instance of the class.
01258 
01259         '''
01260         asName = tokens.asName or None
01261         noMangle = tokens.noMangle or None
01262         resName = tokens.resName or None
01263         parameters = [tokens.funcName, tokens.args.asList(),
01264                       resName, asName, noMangle]
01265         return Function(parameters)
01266 
01267     def __init__(self, parameters):
01268         '''A F90/95/2003 subroutine block:
01269 
01270         function foo(x,y,z)  result(bar)
01271         .
01272         .
01273         .
01274         end function foo
01275 
01276         Input
01277         -----
01278         parameters : the groups from the factory static method
01279 
01280         '''
01281         self._argList, self._resName, self._asName = parameters[1:4]
01282         if parameters[4] is None:
01283             self._no_mangle = False
01284         else:
01285             self._no_mangle = True
01286         # self._asName = parameters[3]
01287         super(Function, self).__init__(self.BLOCKTYPE, parameters)
01288 
01289     def addLine(self, line, n=None):
01290         '''Overloaded addLine() method for a function block.  It strips out
01291         the 'as' and 'no_mangle' clauses prior to calling the base class
01292         addLine() method.
01293 
01294         '''
01295         if ' as ' in line:  line = line[0:line.index(' as ')]+os.linesep
01296         if ' no_mangle' in line:  line = line[0:line.index(' no_mangle')]+os.linesep
01297         super(Function, self).addLine(line)
01298 
01299     def name(self):
01300         '''Overloaded name method.  If "no_mangle" was specified, returns the
01301         original name with no template mangling.  Otherwise, it calls the base
01302         class name() method.
01303 
01304         '''
01305         if self._no_mangle: return self._name
01306         return super(Function, self).name()
01307 
01308 #     def asName( self ):
01309 #         return self._asName
01310 
01311 #     def argList( self ):
01312 #         return self._argList
01313 
01314     #......................................................................
01315     # Properties
01316     #......................................................................
01317     def __propAsName():
01318         doc = "The `as` attribute of the function"
01319         def fget( self ):
01320             return self._asName
01321         return locals()
01322 
01323     def __propArgList():
01324         doc = "The argument list of the function"
01325         def fget( self ):
01326             return self._argList
01327         return locals()
01328 
01329     asName = property( **__propAsName() )
01330     argList = property( **__propArgList() )
01331 
01332 #................................................................................
01333 class Subroutine(FortranBlock):
01334     #....................................................
01335     # pyparsing grammar
01336     #....................................................
01337     BLOCKTYPE = 'Subroutine'
01338     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01339     RECURSIVE = pp.Suppress(pp.CaselessKeyword('recursive'))
01340     ELEMENTAL = pp.Suppress(pp.CaselessKeyword('elemental'))
01341     VALID_ASNAME = pp.Word(pp.alphas, pp.alphanums+'_./()=<>+-*')
01342     AS_CLAUSE = pp.Suppress('as')+VALID_ASNAME('asName')
01343     NO_MANGLE = pp.Word('no_mangle')('noMangle')
01344     GRAMMAR = \
01345         pp.StringStart() +\
01346         pp.Optional(RECURSIVE|ELEMENTAL) +\
01347         KEYWORD +\
01348         VALIDNAME('subName') +\
01349         ARGLIST('args') +\
01350         pp.Optional(AS_CLAUSE) +\
01351         pp.Optional(NO_MANGLE)
01352     #................................................................................
01353 
01354     @staticmethod
01355     def factory(s, lineNo, tokens):
01356         '''Factory method called by the BlockParser to produce an
01357         instance of the class.
01358 
01359         '''
01360         asName = tokens.asName or None
01361         noMangle = tokens.noMangle or None
01362         parameters = [tokens.subName, tokens.args.asList(), asName, noMangle]
01363         return Subroutine(parameters)
01364 
01365     def __init__(self, parameters):
01366         '''A F90/95/2003 subroutine block:
01367 
01368         subroutine foo(x,y,z)
01369         .
01370         .
01371         .
01372         end subroutine foo
01373 
01374         Input
01375         -----
01376         parameters : the groups from the factory static method
01377 
01378         '''
01379         self._argList, self._asName = parameters[1:3]
01380         if parameters[3] is None:
01381             self._no_mangle = False
01382         else:
01383             self._no_mangle = True
01384         self._template = None
01385         super(Subroutine, self).__init__(self.BLOCKTYPE, parameters)
01386 
01387     def addLine(self, line, n=None):
01388         '''Overloaded addLine() method for a subroutine block.  It strips out
01389         the 'as' and 'no_mangle' clauses prior to calling the base class
01390         addLine() method.
01391 
01392         '''
01393         if ' as ' in line:  line = line[0:line.index(' as ')]+os.linesep
01394         if ' no_mangle' in line:  line = line[0:line.index(' no_mangle')]+os.linesep
01395         super(Subroutine, self).addLine(line)
01396 
01397     def name(self):
01398         '''Overloaded name method.  If "no_mangle" was specified, returns the
01399         original name with no template mangling.  Otherwise, it calls the base
01400         class name() method.
01401 
01402         '''
01403         if self._no_mangle: return self._name
01404         return super(Subroutine, self).name()
01405 
01406 #     def asName(self):
01407 #         return self._asName
01408 
01409     #......................................................................
01410     # Properties
01411     #......................................................................
01412     def __propAsName():
01413         doc = "The `as` attribute of the function"
01414         def fget( self ):
01415             return self._asName
01416         return locals()
01417 
01418     def __propArgList():
01419         doc = "The argument list of the function"
01420         def fget( self ):
01421             return self._argList
01422         return locals()
01423 
01424     asName = property( **__propAsName() )
01425     argList = property( **__propArgList() )
01426 
01427 #................................................................................
01428 class Interface(FortranBlock):
01429     #.......................................................
01430     # pyparsing grammar
01431     #.......................................................
01432     BLOCKTYPE='Interface'
01433     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01434     OPERATOR = \
01435         pp.Combine(
01436         pp.CaselessKeyword('operator')('type') +\
01437         pp.Literal('(') +\
01438         ( pp.Combine( pp.Literal('.') + pp.Word( pp.alphas ) + pp.Literal('.') ) |
01439           pp.Literal('<=') |
01440           pp.Literal('>=') |
01441           pp.Literal('==') |
01442           pp.Literal('<') |
01443           pp.Literal('>') )('op') +\
01444         pp.Literal(')'), adjacent=False)
01445     ASSIGNMENT = \
01446         pp.Combine(
01447         pp.CaselessKeyword('assignment')('type') +\
01448         pp.Literal('(') +\
01449         pp.Literal('=') +\
01450         pp.Literal(')'), adjacent=False)
01451     GRAMMAR = \
01452         pp.StringStart() +\
01453         KEYWORD +\
01454         (OPERATOR|ASSIGNMENT|pp.originalTextFor(TEMPLATE_DECL)|VALIDNAME)('name')
01455     SENTINEL = \
01456         pp.StringStart() +\
01457         pp.CaselessKeyword('end') +\
01458         KEYWORD
01459     #................................................................................
01460 
01461     @staticmethod
01462     def factory(s, lineNo, tokens):
01463         '''Factory method called by the BlockParser to produce an
01464         instance of the class.
01465 
01466         '''
01467         name = tokens.name or None
01468         parameters = [name]
01469         return Interface(parameters)
01470 
01471     def __init__(self, parameters):
01472         '''A F90/95/2003 interface block:
01473 
01474         interface foo
01475         .
01476         .
01477         .
01478         end interface
01479 
01480         Input
01481         -----
01482         parameters : the groups from the factory static method
01483 
01484         '''
01485         # Send blank line for name to create the proper sentinel
01486         super(Interface, self).__init__(self.BLOCKTYPE, [''])
01487         if parameters[0] is None:
01488             self._name = 'interface_'+str(id(self))
01489         else:
01490             self._name = pack( parameters[0] )
01491 
01492     def name(self):
01493         # No mangled names for interface names within templates
01494         return self._name
01495 
01496     def addLine( self, line, n=None ):
01497         if len( self._code ) == 0:
01498             self._code.append( line )
01499         else:
01500             super( Interface, self ).addLine( line, n )
01501 
01502 #................................................................................
01503 class InterfaceA(FortranBlock):
01504     #.....................................................
01505     # pyparsing grammar
01506     #.....................................................
01507     BLOCKTYPE = 'Interface'
01508     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01509     OPERATOR = \
01510         pp.Combine(
01511         pp.CaselessKeyword('operator')('type') +\
01512         pp.Literal('(') +\
01513         pp.Word(pp.alphas+'.', pp.alphanums+'_.<=>+/-')('op') +\
01514         pp.Literal(')'), adjacent=False)
01515     ASSIGNMENT = \
01516         pp.Combine(
01517         pp.CaselessKeyword('assignment')('type') +\
01518         pp.Literal('(') +\
01519         pp.Literal('=') +\
01520         pp.Literal(')'), adjacent=False)
01521     GRAMMAR = \
01522         pp.StringStart() +\
01523         KEYWORD +\
01524         (OPERATOR|ASSIGNMENT|VALIDNAME)('name')
01525     SENTINEL = \
01526         pp.StringStart() +\
01527         pp.CaselessKeyword('end') +\
01528         KEYWORD
01529     #................................................................................
01530 
01531     @staticmethod
01532     def factory(s, lineNo, tokens):
01533         '''Factory method called by the BlockParser to produce an
01534         instance of the class.
01535 
01536         '''
01537         name = tokens.name or None
01538         parameters = [name]
01539         return InterfaceA(parameters)
01540 
01541     def __init__(self, parameters):
01542         # Send blank line for name to create the proper sentinel
01543         super(Interface, self).__init__(self.BLOCKTYPE, [''])
01544         if parameters[0] is None:
01545             self._name = 'interface_'+str(id(self))
01546         else:
01547             self._name = parameters[0]
01548 
01549     def name(self):
01550         '''Overloaded name() method.  No name mangling for interface names.
01551 
01552         '''
01553         # No mangled names for interface names within templates
01554         return self._name
01555 
01556     def show(self, mangle=False):
01557         '''Overloaded show method.  For user defined interfaces within templates this
01558         finds the new names of the functions/subroutines due to name mangling
01559         and rebuilds the interface block to reflect the new names.
01560 
01561         '''
01562         parent = self._parent
01563         if parent is not None:
01564             funcsAndSubs = parent.children().filter(Or(isFunction,
01565                                                        isSubroutine))
01566         else:
01567             funcsAndSubs = None
01568 
01569         if funcsAndSubs is None: return ''.join(self._code)
01570         if len(funcsAndSubs) == 0: return ''.join(self._code)
01571 
01572         block = ''
01573         for line in self._code:
01574             ans = self.procedureNames.search(line)
01575             if ans:
01576                 names = ans.group(1).split(',')
01577                 newNames = []
01578                 for name in names:
01579                     name = name.rstrip()
01580                     fnc_sub = funcsAndSubs.filter(startsWith(name))
01581                     if len(fnc_sub) != 1:
01582                         raise Exception("Ambiguous or unresolved name in "+\
01583                                         "Interface block: %s%s"%(str(fnc_sub),os.linesep)+\
01584                                         "Name: %s"%(name,))
01585                     fnc_sub = fnc_sub.values()[0]
01586                     line = line.replace(name, fnc_sub.name())
01587             block += line
01588         return block
01589 
01590 #................................................................................
01591 class Type(FortranBlock):
01592     #..........................................................
01593     # pyparsing grammar
01594     #..........................................................
01595     BLOCKTYPE = 'Type'
01596     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01597     ABSTRACT = pp.CaselessKeyword('abstract')
01598     EXTENDS = pp.Suppress(pp.CaselessKeyword('extends')) +\
01599         pp.Suppress('(') + (VALIDNAME | TEMPLATE_DECL)('name') + pp.Suppress(')')
01600     BIND = pp.Suppress(pp.CaselessKeyword('bind')) +\
01601         pp.Suppress('(') + pp.Word(pp.alphas)('name') + pp.Suppress(')')
01602     TYPE_ATTR_SPEC = \
01603         pp.delimitedList(EXTENDS('extends') | ABSTRACT | BIND('bind') | PUBLIC |
01604                          PRIVATE)
01605     GRAMMAR = \
01606         pp.StringStart() +\
01607         KEYWORD +\
01608         pp.Optional(',' + TYPE_ATTR_SPEC )('type_attr_spec') +\
01609         pp.Suppress(pp.Optional('::')) +\
01610         (pp.NotAny(pp.CaselessKeyword('is'))) +\
01611         VALIDNAME('name') +\
01612         pp.Suppress(pp.Optional(ARGLIST)) +\
01613         pp.Optional(';') + pp.StringEnd()
01614     #................................................................................
01615 
01616     @staticmethod
01617     def factory(s, lineNo, tokens):
01618         '''Factory method called by the BlockParser to produce an
01619         instance of the class.
01620 
01621         '''
01622         return Type([tokens.name])
01623 
01624     def __init__(self, parameters):
01625         '''A F90/95/2003 type block:
01626 
01627         type foo
01628         .
01629         .
01630         .
01631         end type foo
01632 
01633         Input
01634         -----
01635         parameters : the groups from the factory static method
01636 
01637         '''
01638         super(Type, self).__init__(self.BLOCKTYPE, parameters)
01639 
01640         #......................................................................
01641         # Have to create the SENTINEL grammar because I'm using a different
01642         # KEYWORD than the name of the class since `type` is a Python keyword
01643         #......................................................................
01644         # self.SENTINEL = \
01645         #     pp.StringStart() +\
01646         #     pp.CaselessKeyword('end') +\
01647         #     pp.CaselessKeyword('type') +\
01648         #     pp.CaselessKeyword(self._name)
01649 
01650 #................................................................................
01651 class AutoInterface(FortranBlock):
01652     #...................................................................
01653     # pyparsing grammar
01654     #...................................................................
01655     BLOCKTYPE = 'AutoInterface'
01656     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01657     GRAMMAR =  KEYWORD
01658     SENTINEL = \
01659         pp.StringStart() +\
01660         pp.CaselessKeyword('end') +\
01661         KEYWORD
01662     #...................................................................
01663 
01664     @staticmethod
01665     def factory(s, lineNo, tokens):
01666         '''Factory method called by the BlockParser to produce an
01667         instance of the class.
01668 
01669         '''
01670         return AutoInterface(['autointerface'])
01671 
01672     def __init__(self, parameters):
01673         '''An autointerface block:
01674 
01675         autointerface
01676         end autointerface
01677 
01678         This block automatically generates interfaces for
01679         functions/subroutines with 'as' clauses.
01680 
01681         Input
01682         -----
01683         parameters : the groups from the factory static method
01684 
01685         '''
01686         # Send blank line for name to create the proper sentinel
01687         super(AutoInterface, self).__init__(self.BLOCKTYPE, [''])
01688         self._name = parameters[0]
01689 
01690     def show(self, mangle=False):
01691         '''Overloaded show() method.  Finds all functions/subroutines that
01692         have 'as' naming within its parent block and generates a generic
01693         interface for it using the as name.  For example:
01694 
01695         function foo(x,y,z)  result(x)  as  bar
01696         function foo2(x)     result(y)  as  bar
01697 
01698         would generate
01699 
01700         interface bar
01701            module procedure foo
01702            module procedure foo2
01703         end interface
01704 
01705         This is also very useful for templated functions so a consistent
01706         generic interface can be generated.
01707 
01708         '''
01709         parent = self._parent
01710         if parent is not None:
01711             #................................................................
01712             # Find all the functions/subroutines that have 'as' names.
01713             # ................................................................
01714             funcsAndSubs = parent.findAll((Function, Subroutine))
01715             funcsAndSubs = [x for x in funcsAndSubs
01716                             if (hasAsName(('',x)) and not x.isTemplated())]
01717         else:
01718             funcsAndSubs = []
01719 
01720         n = tab(self._code[0])
01721 
01722         _dots = '!'+'.'*(80-n)+os.linesep
01723         block = indent(n, _dots)
01724         block += indent(n, '! `AutoInterface` Generated Code Section'+os.linesep)
01725         block += indent(n, _dots)
01726         block += ''.join(self._code[1:-1])
01727 
01728         # Filter the asNames for funcs/subs that have the same asName
01729         _asNames = {}
01730         # for fnc in funcsAndSubs.values():
01731         for fnc in funcsAndSubs:
01732             if fnc.asName in _asNames:
01733                 _asNames[fnc.asName].append(fnc.name())
01734             else:
01735                 _asNames[fnc.asName] = [fnc.name()]
01736 
01737         for k,v in _asNames.items():
01738             line = indent(n, 'interface %s%s'%(k,os.linesep))
01739             for name in v:
01740                 line += indent(n+2, 'module procedure %s%s'%(name,os.linesep))
01741 
01742             line += indent(n, 'end interface'+2*os.linesep)
01743             block += line
01744 
01745         return block+indent(n, _dots)
01746 
01747 #................................................................................
01748 class AutoPrivate(FortranBlock):
01749     #.................................................................
01750     # pyparsing grammar
01751     #.................................................................
01752     BLOCKTYPE = 'AutoPrivate'
01753     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01754     GRAMMAR =  KEYWORD
01755     SENTINEL = \
01756         pp.StringStart() +\
01757         pp.CaselessKeyword('end') +\
01758         KEYWORD
01759     #.................................................................
01760 
01761     @staticmethod
01762     def factory(s, lineNo, tokens):
01763         '''Factory method called by the BlockParser to produce an
01764         instance of the class.
01765 
01766         '''
01767         return AutoPrivate(['autoprivate'])
01768 
01769     def __init__(self, parameters):
01770         '''An autoprivate block:
01771 
01772         autoprivate
01773         end autoprivate
01774 
01775         This block automatically generates private statements for
01776         functions/subroutines with 'as' clauses.
01777 
01778         Input
01779         -----
01780         parameters : the groups from the factory static method
01781 
01782         '''
01783         # Send blank line for name to create the proper sentinel
01784         super(AutoPrivate, self).__init__(self.BLOCKTYPE, [''])
01785         self._name = parameters[0]
01786 
01787     def show(self, mangle=False):
01788         '''Overloaded show that finds all 'as' name functions/subroutines and
01789         makes their original names private within the module.
01790 
01791         '''
01792         parent = self._parent
01793         if parent is not None:
01794             #................................................................
01795             # Find all the functions/subroutines that have 'as' names.
01796             #................................................................
01797             funcsAndSubs = parent.findAll((Function, Subroutine))
01798             funcsAndSubs = [x for x in funcsAndSubs
01799                             if (hasAsName(('',x)) and not x.isTemplated())]
01800         else:
01801             funcsAndSubs = []
01802 
01803         n = tab(self._code[0])
01804 
01805         _dots = '!'+'.'*(80-n)+os.linesep
01806         block = indent(n, _dots)
01807         block += indent(n, '! `AutoPrivate` Generated Code Section'+os.linesep)
01808         block += indent(n, _dots)
01809         block += ''.join(self._code[1:-1])
01810 
01811         # for fnc in funcsAndSubs.values():
01812         for fnc in funcsAndSubs:
01813             line = indent(n, 'private :: %s%s'%(fnc.name(),os.linesep))
01814             block += line
01815 
01816         return block+indent(n, _dots)
01817 
01818 #................................................................................
01819 class AutoUse(FortranBlock):
01820     #..........................................................
01821     # pyparsing grammar
01822     #..........................................................
01823     BLOCKTYPE = 'AutoUse'
01824     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01825     GRAMMAR = KEYWORD
01826     SENTINEL = \
01827         pp.StringStart() +\
01828         pp.CaselessKeyword('end') +\
01829         KEYWORD
01830     #..........................................................
01831 
01832     @staticmethod
01833     def factory(s, lineNo, tokens):
01834         '''Factory method called by the BlockParser to produce an
01835         instance of the class.
01836 
01837         '''
01838         return AutoUse(['autouse'])
01839 
01840     def __init__(self, parameters):
01841         '''An autouse block:
01842 
01843         autouse
01844         end autouse
01845 
01846         This block automatically generates use statements for
01847         modules that use types/functions/subroutines in other templated
01848         modules.
01849 
01850         Input
01851         -----
01852         parameters : the groups from the factory static method
01853 
01854         '''
01855         # Send blank line for name to create the proper sentinel
01856         super(AutoUse, self).__init__(self.BLOCKTYPE, [''])
01857         self._name = parameters[0]
01858 
01859     def show(self, mangle=False):
01860         '''Overloaded show method.  Look for dependencies and put proper name
01861         mangled use statements.
01862 
01863         '''
01864         jc = globalSymbolTable.joinCharacter()
01865         n = tab(self._code[0])
01866 
01867         _dots = '!'+'.'*(80-n)+os.linesep
01868         block = indent(n, _dots)
01869         block += indent(n, '! `AutoUse` Generated Code Section'+os.linesep)
01870         block += indent(n, _dots)
01871         for line in self._code[ 1:-1 ]:
01872             self._modLine = line
01873             ans = self.SHOW_GRAMMARS.searchString( line )
01874             block += self._modLine
01875         # block += ''.join( self._code[1:-1] )
01876 
01877         use_decl = map( self.resolveSymbol, self.useDeclarations() )
01878         use_decl.extend( map( self.resolveSymbol,
01879                               self._parent.useDeclarations() ) )
01880 
01881         modSet = ASet()
01882         depSet = ASet( map( self.resolveSymbol,
01883                             self._parent.dependencies().copy() ) )
01884         myMod = self.upTo( Module )
01885         for dep in depSet:
01886             if myMod is not None:
01887                 if myMod.contains( dep ): continue
01888             blk = globalSymbolTable.filter( startsWith( dep + jc ) ).values()
01889             if len( blk ) != 1:
01890                 if len( blk ) == 0 and dep in self._depIgnore: continue
01891                 msg = "WARNING:  Ambiguous or unresolved name in AutoUse block"
01892                 msg += os.linesep
01893                 msg += "Parent block: %s%s"%( self.parent().name(),
01894                                                                 os.linesep)
01895                 msg += "Looking for: %s%s"%(dep, os.linesep)
01896                 msg += "Found: %s%s"%(','.join([b.name() for b in blk]),
01897                                       os.linesep)
01898                 print msg
01899                 continue
01900                 # raise Exception(msg)
01901 
01902             #................................................................
01903             # If the block is an instance of a module, then skip it since it
01904             # was grabbed as a dependency from a use declaration in a child
01905             # block, i.e. a child block had a "use foo" or something in it
01906             # which resulted in a module dependency for that block.  These
01907             # are ignored because we are trying to create "use" declarations
01908             # for a module based on type/function/subroutine dependencies.
01909             #................................................................
01910             blk = blk[0]
01911             if isinstance( blk, Module ): continue
01912             mod = blk.upTo( Module )
01913             if mod is None:
01914                 msg = "Cannot find (%s) in a module for AutoUse" + os.linesep
01915                 msg += "In module (%s) for AutoUse (%s)"
01916                 msg = msg % ( blk.name(), self._parent.name(), str(self) )
01917                 print msg
01918                 continue
01919                 # raise Exception()
01920 
01921             if mod is myMod: continue
01922             modSet.add( mod.name() )
01923 
01924         modSet.difference_update( use_decl )
01925         while ( len( modSet ) > 0 ):
01926             mod = modSet.pop()
01927             block += indent(n, 'use %s%s'%( mod, os.linesep ) )
01928         return block+indent( n, _dots )
01929 
01930 #................................................................................
01931 class Enumeration(FortranBlock):
01932     #...................................................................
01933     # pyparsing grammar
01934     #...................................................................
01935     BLOCKTYPE = 'Enumeration'
01936     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
01937     GRAMMAR = pp.StringStart() + KEYWORD + pp.Optional(ARGLIST('args'))
01938     SENTINEL = \
01939         pp.StringStart() +\
01940         pp.CaselessKeyword('end') +\
01941         KEYWORD
01942     #...................................................................
01943 
01944     @staticmethod
01945     def factory(s, lineNo, tokens):
01946         '''Factory method called by the BlockParser to produce an
01947         instance of the class.
01948 
01949         '''
01950         startToken = 0
01951         increment = 1
01952         multiple = 1
01953         try:
01954             startToken = tokens.args[0]
01955             increment = tokens.args[1]
01956             multiple = tokens.args[2]
01957         except:
01958             pass
01959         return Enumeration([startToken,increment,multiple])
01960 
01961     def __init__(self, parameters):
01962         '''An enumeration block:
01963 
01964         enumeration(start,incr,mult)  |  enumeration start
01965           INT1 INT2 INT3
01966         end enumeration
01967 
01968         Results in:
01969 
01970         integer, parameter :: INT1=1*incr+start
01971         integer, parameter :: INT2=2*incr+start
01972         integer, parameter :: INT3=3*incr+start
01973 
01974         This block automatically generates integer parameter statements for
01975         functionals.
01976 
01977         Input
01978         -----
01979         parameters : the groups from the factory static method
01980 
01981         '''
01982         # Send blank line for name to create the proper sentinel
01983         super(Enumeration, self).__init__(self.BLOCKTYPE,
01984                                           ['enumeration_'+str(id(self))]+parameters)
01985         self._starttoken = parameters[0]
01986         self._increment = parameters[1]
01987         self._multiple = parameters[2]
01988 
01989     def show(self, mangle=False):
01990         '''Overloaded show method.  Look for templated dependencies and put
01991         proper name mangled use statements.
01992 
01993         '''
01994 
01995         block = ''
01996         try: start = int(self._starttoken)
01997         except: start = self._starttoken
01998         try: incr = int(self._increment)
01999         except: incr = self._increment
02000         try: mult = int(self._multiple)
02001         except: mult = self._multiple
02002         n = tab(self._code[0]) # Tab level of the beginning of the block
02003         if len(self._code) > 0:
02004             _n = max(0, tab(self._code[1])-n) # Tab difference for the internal code
02005 
02006         val = incr
02007         for line in self._code[1:-1]:
02008             ind = line.find('!')
02009             if ind < 0: ind = len(line)
02010             comment = line[ind:]
02011             for var in " ".join(line[0:ind].split(',')).split():
02012                 block += os.linesep
02013                 block += indent(n, 'integer, parameter :: %s=(%s)+(%d)'%(var,start,val))
02014                 if mult == 1:
02015                     val += incr
02016                 else:
02017                     val *= mult
02018             block += comment + os.linesep
02019         return block
02020 
02021 #................................................................................
02022 class Foreach(FortranBlock):
02023     #.............................................................
02024     # pyparsing grammar
02025     #.............................................................
02026     BLOCKTYPE = 'Foreach'
02027     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
02028     GRAMMAR = KEYWORD +\
02029         ARGLIST('args') + pp.Suppress(pp.CaselessKeyword('in')) + ARGLIST2
02030     SENTINEL = \
02031         pp.StringStart() +\
02032         pp.CaselessKeyword('end') +\
02033         KEYWORD
02034     #.............................................................
02035 
02036     @staticmethod
02037     def factory(s, lineNo, tokens):
02038         '''Factory method called by the BlockParser to produce an
02039         instance of the class.
02040 
02041         '''
02042         replacestrings = tokens.args
02043         arglist = tokens.args2
02044         return Foreach(['foreach',replacestrings,arglist])
02045 
02046     def __init__(self, parameters):
02047         '''A foreach construct:
02048 
02049         foreach(f) in(INT1, INT2, INT3)
02050           $f$ = $f$ + 1
02051         end foreach
02052 
02053         Results in:
02054 
02055         INT1 = INT1 + 1
02056         INT2 = INT2 + 1
02057         INT3 = INT3 + 1
02058 
02059         Input
02060         -----
02061         parameters : the groups from the factory static method
02062 
02063         '''
02064         # Send blank line for name to create the proper sentinel
02065         super(Foreach, self).__init__(self.BLOCKTYPE,
02066                                       [parameters[0]+'_'+str(id(self))]+parameters[1:])
02067         self._replacestring = parameters[1]
02068         self._argumentlists = parameters[2]
02069 
02070     def show(self, mangle=False):
02071         '''Overloaded show method.  Look for templated dependencies and put
02072         proper name mangled use statements.
02073 
02074         '''
02075 
02076         block = ''
02077 
02078         replist = list()
02079         for i in self._replacestrings: replist.append(i)
02080         ln = len(replist)
02081         nreps = len(self._argumentlists)/ln
02082 
02083         arglist = list()
02084         for j in xrange(0,nreps):
02085             tmpargs = list()
02086             for i in xrange(0,ln):
02087                 tmpargs.append(self._argumentlists[i+ln*j])
02088             arglist.append(tmpargs)
02089 
02090         print replist,arglist
02091 
02092         n = tab(self._code[0]) # Tab level of the beginning of the block
02093         if len(self._code) > 0:
02094             _n = max(0, tab(self._code[1])-n) # Tab difference for the internal code
02095 
02096         repl = self._replacestrings
02097         for arg in arglist:
02098             for line in self._code[1:-1]:
02099                 tmpline = line
02100                 for i in xrange(0,len(self._replacestrings)):
02101                     rs = self._replacestrings[i]
02102                     ostr = arg[i]
02103                     tmpline = tmpline.replace('$'+rs+'$', ostr)
02104                 block += indent(n, tmpline + os.linesep)
02105             block += os.linesep
02106 
02107         return block
02108 
02109 #................................................................................
02110 class TestRunner(FortranBlock):
02111     #.................................................................
02112     # pyparsing grammar
02113     #.................................................................
02114     BLOCKTYPE = 'TestRunner'
02115     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
02116     GRAMMAR = \
02117         pp.StringStart() +\
02118         KEYWORD +\
02119         ARGLIST('args')
02120     SENTINEL = \
02121         pp.StringStart() +\
02122         pp.CaselessKeyword('end') +\
02123         KEYWORD
02124     #...........................................................
02125 
02126     @staticmethod
02127     def factory(s, lineNo, tokens):
02128         '''Factory method called by the BlockParser to produce an
02129         instance of the class.
02130 
02131         '''
02132         args = tokens.args.asList()
02133         prefix = args[0]
02134         testArgList = ((len(args) > 1) and args[1:]) or []
02135         return TestRunner(['TestRunner', prefix, testArgList])
02136 
02137     def __init__(self, parameters):
02138         '''A test runner block:
02139 
02140         TestRunner(prefix)
02141         end TestRunner
02142 
02143         This block gathers all functions that start with `prefix` in the
02144         module found in the sole use statement of the enclosing block.  If no
02145         use statement is found in the enclosing block, it traverses up its
02146         block tree to find the enclosing module it is contained in and uses
02147         that for the function finding.  It then creates the following pattern
02148         in its show method for each function it finds:
02149 
02150         call setUp(<function name as string here>)
02151         if(.not. <function with call signature as found in module>) then
02152            print *,'Test <function name> : failed'
02153         else
02154            print *,'Test <function name> : passed'
02155         endif
02156         call tearDown(<function name as string here>)
02157 
02158         Input
02159         -----
02160         parameters : the groups from the factory static method
02161 
02162         '''
02163         self._name, self._prefix, self._argList = parameters
02164         super(TestRunner, self).__init__(self.BLOCKTYPE, parameters)
02165 
02166     def show(self, mangle=False):
02167         '''Create block of test running code.
02168 
02169         '''
02170         def _cmp(blkA, blkB):
02171             '''Comparison function for block names.  Maybe move into Block
02172             class ???
02173 
02174             '''
02175             if blkA.name() < blkB.name(): return -1
02176             if blkA.name() == blkB.name(): return 0
02177             if blkA.name() > blkB.name(): return 1
02178 
02179         jc = globalSymbolTable.joinCharacter()
02180         if self._parent:
02181             _use = self._parent.useDeclarations()
02182             #.................................................................
02183             # Three cases:
02184             # len(_use) > 1  : Enclosing block has more than one use
02185             #                  statement.  Only expect one in enclosing block.
02186             # len(_use) == 0 : No use statement in enclosing block.  Search
02187             #                  for module in family tree.
02188             # default        : One use statement in enclosing block.  Find it
02189             #                  in the symbol table and get the functions.
02190             #.................................................................
02191             if len(_use) > 1:
02192                 msg = "Enclosing block %s "%(self._parent.name(),)
02193                 msg += "of TestRunner block has more than "
02194                 msg += "one use declaration%s"%(os.linesep,)
02195                 raise Exception(msg)
02196             elif len(_use) == 0 :
02197                 _mod = self.upTo(Module)
02198             else:
02199                 _mod = globalSymbolTable.filter(And(startsWith(_use.pop() + jc),
02200                                               isModule))
02201                 if len(_mod) > 1:
02202                     msg = "Ambiguous module names found in TestRunner"+os.linesep
02203                     msg += "Found: %s"%(','.join([m for m in _mod]))
02204                     raise Exception(msg)
02205 
02206                 _mod = _mod.values()[0]
02207 
02208         #....................................................
02209         # Get the prefixed functions from the _use module
02210         #....................................................
02211         _funcs = _mod.children().filter(startsWith(self._prefix)).values()
02212         _funcs.sort(cmp=_cmp)
02213 
02214         #.....................................................
02215         # Build code block
02216         #.....................................................
02217         n = tab(self._code[0])
02218         code = ''
02219         argList = ','.join(self._argList)
02220         code += indent(n, ''+os.linesep)
02221         code += indent(n, 'call setUp("__MOD__")'+os.linesep)
02222         for fnc in _funcs:
02223             fname = fnc.name()
02224             code += indent(n, 'call setNTests(this, getNTests(this)+1)'+os.linesep)
02225             code += indent(n, 'ntest = ntest + 1'+os.linesep)
02226             code += indent(n, 'err = init(this%shared%messages)'+os.linesep)
02227             code += indent(n, 'call setUp("%s")%s'%(fname,os.linesep))
02228             code += indent(n, 'err = init(this%shared%failList_)'+os.linesep)
02229             code += indent(n, 'ret = %s(%s)%s'%(fname,argList,os.linesep))
02230             code += indent(n, 'do while(iterate(this%shared%failList_,retval))'+os.linesep)
02231             code += indent(n, '  if (retval /= 0) ret = .false.'+os.linesep)
02232             code += indent(n, 'end do'+os.linesep)
02233             code += indent(n, 'if (.not. ret) then'+os.linesep)
02234             code += indent(n+3, 'print *, "Test %s... failed"%s'%(fname,os.linesep))
02235             code += indent(n+3, 'call setNFails(this, getNFails(this)+1)'+os.linesep)
02236             code += indent(n+3, 'nfail = nfail + 1'+os.linesep)
02237             code += indent(n, 'else'+os.linesep)
02238             code += indent(n+3, 'print *, "Test %s... passed"%s'%(fname,os.linesep))
02239             code += indent(n, 'endif'+os.linesep)
02240             code += indent(n, 'err = delete(this%shared%failList_)'+os.linesep)
02241             code += indent(n, 'call tearDown("%s")%s'%(fname,os.linesep))
02242             code += indent(n, 'err = init(rep,"%s"'%(fname,)+',ret,this%shared%messages)'+os.linesep)
02243             code += indent(n, 'err = push(replist,rep)'+os.linesep)
02244             code += indent(n, 'do while (iterate(this%shared%messages,mess))'+os.linesep)
02245             code += indent(n, '  err = push(messagestack,mess)'+os.linesep)
02246             code += indent(n, 'end do'+os.linesep)
02247             code += os.linesep
02248 
02249         code += indent(n, 'call tearDown("__MOD__")'+os.linesep)
02250 
02251         return code
02252 
02253 #................................................................................
02254 class ParallelTestRunner(FortranBlock):
02255     #.............................................................
02256     # pyparsing grammar
02257     #.............................................................
02258     BLOCKTYPE = 'ParallelTestRunner'
02259     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
02260     GRAMMAR = \
02261         pp.StringStart() +\
02262         KEYWORD +\
02263         ARGLIST('args')
02264     SENTINEL = \
02265         pp.StringStart() +\
02266         pp.CaselessKeyword('end') +\
02267         KEYWORD
02268     #.............................................................
02269 
02270     @staticmethod
02271     def factory(s, lineNo, tokens):
02272         '''Factory method called by the BlockParser to produce an
02273         instance of the class.
02274 
02275         '''
02276         args = tokens.args.asList()
02277         prefix = args[0]
02278         testArgList = ((len(args) > 1) and args[1:]) or []
02279         return ParallelTestRunner(['ParallelTestRunner', prefix, testArgList])
02280 
02281     def __init__(self, parameters):
02282         '''A test runner block:
02283 
02284         ParallelTestRunner(prefix)
02285         end ParallelTestRunner
02286 
02287         This block gathers all functions that start with `prefix` in the
02288         module found in the sole use statement of the enclosing block.  If no
02289         use statement is found in the enclosing block, it traverses up its
02290         block tree to find the enclosing module it is contained in and uses
02291         that for the function finding.  It then creates the following pattern
02292         in its show method for each function it finds:
02293 
02294         call setUp(<function name as string here>)
02295         if(.not. <function with call signature as found in module>) then
02296            print *,'Test <function name> : failed'
02297         else
02298            print *,'Test <function name> : passed'
02299         endif
02300         call tearDown(<function name as string here>)
02301 
02302         Input
02303         -----
02304         parameters : the groups from the factory static method
02305 
02306         '''
02307         self._name, self._prefix, self._argList = parameters
02308         super(ParallelTestRunner, self).__init__(self.BLOCKTYPE, parameters)
02309 
02310     def show(self, mangle=False):
02311         '''Create block of test running code.
02312 
02313         '''
02314         def _cmp(blkA, blkB):
02315             '''Comparison function for block names.  Maybe move into Block
02316             class ???
02317 
02318             '''
02319             if blkA.name() < blkB.name(): return -1
02320             if blkA.name() == blkB.name(): return 0
02321             if blkA.name() > blkB.name(): return 1
02322 
02323         jc = globalSymbolTable.joinCharacter()
02324         if self._parent:
02325             _use = self._parent.useDeclarations()
02326             #.................................................................
02327             # Three cases:
02328             # len(_use) > 1  : Enclosing block has more than one use
02329             #                  statement.  Only expect one in enclosing block.
02330             # len(_use) == 0 : No use statement in enclosing block.  Search
02331             #                  for module in family tree.
02332             # default        : One use statement in enclosing block.  Find it
02333             #                  in the symbol table and get the functions.
02334             #.................................................................
02335             if len(_use) > 1:
02336                 msg = "Enclosing block %s "%(self._parent.name(),)
02337                 msg += "of ParallelTestRunner block has more than "
02338                 msg += "one use declaration%s"%(os.linesep,)
02339                 raise Exception(msg)
02340             elif len(_use) == 0 :
02341                 _mod = self.upTo(Module)
02342             else:
02343                 _mod = globalSymbolTable.filter(And(startsWith(_use.pop() + jc),
02344                                               isModule))
02345                 if len(_mod) > 1:
02346                     msg = "Ambiguous module names found in ParallelTestRunner"+os.linesep
02347                     msg += "Found: %s"%(','.join([m for m in _mod]))
02348                     raise Exception(msg)
02349 
02350                 _mod = _mod.values()[0]
02351 
02352         #........................................................
02353         # Get the prefixed functions from the _use module
02354         #........................................................
02355         _funcs = _mod.children().filter(startsWith(self._prefix)).values()
02356         _funcs.sort(cmp=_cmp)
02357 
02358         #...................................................................
02359         # Build code block
02360         #...................................................................
02361         n = tab(self._code[0])
02362         code = ''
02363         argList = ','.join(self._argList)
02364         code += indent(n, ''+os.linesep)
02365         code += indent(n, 'call setUp("__MOD__")'+os.linesep+os.linesep)
02366         for fnc in _funcs:
02367             fname = fnc.name()
02368             code += indent(n, 'call setNTests(this, getNTests(this)+1)'+os.linesep)
02369             code += indent(n, 'ntest = ntest + 1'+os.linesep)
02370             code += indent(n, 'err = init(this%shared%messages)'+os.linesep)
02371             code += indent(n, 'err = init(this%shared%failList_)'+os.linesep)
02372             code += indent(n, os.linesep)
02373             code += indent(n, 'call setUp("%s")%s'%(fname,os.linesep))
02374             code += indent(n, 'ret = %s(%s)%s'%(fname,argList,os.linesep))
02375             code += indent(n, 'call tearDown("%s")%s'%(fname,os.linesep))
02376             code += indent(n, 'do while(iterate(this%shared%failList_,retval))'+os.linesep)
02377             code += indent(n+3, 'if (retval /= 0) ret = .false.'+os.linesep)
02378             code += indent(n, 'end do'+os.linesep)
02379             code += indent(n, 'localfails = merge(0, 1, ret)'+os.linesep)
02380             code += indent(n, 'globalfails = 0'+os.linesep)
02381             code += indent(n, 'call mpi_barrier( testcomm, ierror )'+os.linesep)
02382             code += indent(n, 'call mpi_reduce( localfails, globalfails, 1, MPI_INTEGER, MPI_SUM, 0, testcomm, ierror )'+os.linesep)
02383             #
02384             # Reporting section
02385             #
02386             code += indent(n, 'if ( rnk == 0 ) then'+os.linesep)
02387             # If this proc is the master of the test communicator, it should handle printing
02388             # messages and also should create the suite report.
02389             # There should also be a loop over the other processors to collect messages...
02390             code += indent(n+3, 'if (globalfails > 0) then'+os.linesep)
02391             code += indent(n+6, 'print *, "Test %s... failed"%s'%(fname,os.linesep))
02392             code += indent(n+6, 'call setNFails(this, getNFails(this)+1)'+os.linesep)
02393             code += indent(n+6, 'nfail = nfail + 1'+os.linesep)
02394             code += indent(n+3, 'else'+os.linesep)
02395             code += indent(n+6, 'print *, "Test %s... passed"%s'%(fname,os.linesep))
02396             code += indent(n+3, 'end if'+os.linesep)
02397             code += indent(n+3, 'err = init(rep,"%s"'%(fname,)+',ret,this%shared%messages)'+os.linesep)
02398             code += indent(n+3, 'err = push(replist,rep)'+os.linesep)
02399             code += indent(n+3, 'do while (iterate(this%shared%messages,mess))'+os.linesep)
02400             code += indent(n+6, 'err = push(messagestack,mess)'+os.linesep)
02401             code += indent(n+3, 'end do'+os.linesep)
02402             code += indent(n+3, 'do i = 1, sz-1, 1'+os.linesep)
02403             code += indent(n+6, '!call mpi_recv(msize, 1, MPI_INTEGER, i, 42, testcomm, stat, ierror)'+os.linesep)
02404             code += indent(n+6, '!if (msize > 0) then'+os.linesep)
02405             code += indent(n+6, 'call mpi_probe(i, 43, testcomm, stat, ierror)'+os.linesep)
02406             code += indent(n+6, 'call mpi_get_count(stat, MPI_CHARACTER, msize, ierror)'+os.linesep)
02407             code += indent(n+6, 'allocate(cbuff(msize))'+os.linesep)
02408             code += indent(n+6, 'call mpi_recv(cbuff, msize, MPI_CHARACTER, i, 43, testcomm, stat, ierror)'+os.linesep)
02409             code += indent(n+6, 'err = init(comb,cbuff)'+os.linesep)
02410             code += indent(n+6, 'deallocate(cbuff)'+os.linesep)
02411             code += indent(n+6, 'if (msize > 0) then'+os.linesep)
02412             code += indent(n+6, 'tmplist = split(comb, sepstr)'+os.linesep)
02413             code += indent(n+6, 'do while (iterate(tmplist,mess))'+os.linesep)
02414             code += indent(n+9, 'newmess = ": " + mess'+os.linesep)
02415             code += indent(n+9, 'mess = stoi(i)'+os.linesep)
02416             code += indent(n+9, 'mess = mess + newmess'+os.linesep)
02417             code += indent(n+9, 'newmess = "processor " + mess'+os.linesep)
02418             code += indent(n+9, 'err = push(messagestack,newmess)'+os.linesep)
02419             code += indent(n+9, 'err = push(this%shared%messages,newmess)'+os.linesep)
02420             code += indent(n+6, 'end do'+os.linesep)
02421             code += indent(n+6, 'end if !(msize > 0)'+os.linesep)
02422             code += indent(n+3, 'end do'+os.linesep)
02423             code += indent(n, 'else'+os.linesep)
02424             # If this proc is not the master of the test communicator, it should send
02425             # all messages to the master.
02426             code += indent(n+3, 'comb = join(sep, this%shared%messages)'+os.linesep)
02427             code += indent(n+3, 'msize = length(comb)'+os.linesep)
02428             code += indent(n+3, '!call mpi_send(msize, 1, MPI_INTEGER, 0, 42, testcomm, ierror)'+os.linesep)
02429             code += indent(n+3, 'if (msize > 0) then'+os.linesep)
02430             code += indent(n+6, 'cbuff => getText(comb)'+os.linesep)
02431             code += indent(n+6, 'call mpi_send(cbuff, msize, MPI_CHARACTER, 0, 43, testcomm, ierror)'+os.linesep)
02432             code += indent(n+6, 'nullify(cbuff)'+os.linesep)
02433             code += indent(n+3, 'else'+os.linesep)
02434             code += indent(n+6, 'call mpi_send(\' \', 0, MPI_CHARACTER, 0, 43, testcomm, ierror)'+os.linesep)
02435             code += indent(n+3, 'end if !(msize > 0)'+os.linesep)
02436             code += indent(n, 'end if'+os.linesep)
02437             code += indent(n, 'err = delete(this%shared%failList_)'+os.linesep)
02438             code += os.linesep
02439 
02440         code += indent(n, 'call tearDown("__MOD__")'+os.linesep)
02441 
02442         return code
02443 
02444 #................................................................................
02445 class AutoDelete(FortranBlock):
02446     #..............................................................
02447     # pyparsing grammar
02448     #..............................................................
02449     BLOCKTYPE = 'AutoDelete'
02450     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
02451     GRAMMAR = \
02452         KEYWORD +\
02453         ARGLIST('args')
02454     SENTINEL = \
02455         pp.StringStart() +\
02456         pp.CaselessKeyword('end') +\
02457         KEYWORD
02458     #..............................................................
02459     @staticmethod
02460     def factory(s, lineNo, tokens):
02461         '''Factory method called by the BlockParser to produce an
02462         instance of the class.
02463 
02464         '''
02465         name = tokens.args[0]
02466         return AutoDelete(['autodelete_'+name])
02467 
02468     def __init__(self, parameters):
02469         '''An autodelete block:
02470 
02471         AutoDelete(name)
02472           type (DLList<integer>) :: a,b,c
02473         end AutoDelete
02474 
02475         This block will store all the symbols of the type() declarations,
02476         i.e. a,b,c above for later use with the GarbageCollect block.
02477 
02478         Input
02479         -----
02480         parameters : the groups from the factory static method
02481 
02482         '''
02483         super(AutoDelete, self).__init__(self.BLOCKTYPE, parameters)
02484 #         self._symbols = []
02485 
02486     def show(self, mangle=False):
02487         '''Find all the derived type names in the block and store them for the
02488         GarbageCollect block.
02489 
02490         '''
02491 
02492         block = ''
02493         n = tab(self._code[0])
02494         untab = tab(self._code[1])-n
02495         parent = self.parent()
02496 
02497         for line in self._code[1:-1]:
02498             ans = TEMPLATE_TYPE.searchString(line)
02499             try:
02500                 name = ans[0].name
02501                 newName = self.resolveSymbol(name)
02502                 block += line.replace(name, newName)[untab:]
02503             except:
02504                 raise Exception('''Could not figure out line: %s %sIn AutoDelete%s'''%(line,os.linesep,os.linesep))
02505 
02506 #         for line in self._code[1:-1]:
02507 #             ans = TEMPLATE_TYPE.searchString(line)
02508 #             try:
02509 #                 name = ans[0].name
02510 #                 symbols.extend(ans[0].symbolList.asList())
02511 #                 newName = self.resolveSymbol(name)
02512 #                 block += line.replace(name, newName)[untab:]
02513 #             except:
02514 #                 raise Exception('''Could not figure out line: %s %sIn
02515 #             AutoUse%s'''%(line,os.linesep,os.linesep))
02516 
02517         return block
02518 
02519 #     def getSymbols(self):
02520 #         return self._symbols
02521 
02522 #................................................................................
02523 class GarbageCollect(FortranBlock):
02524     #............................................................
02525     # pyparsing grammar
02526     #............................................................
02527     BLOCKTYPE = 'GarbageCollect'
02528     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
02529     GRAMMAR = \
02530         KEYWORD +\
02531         ARGLIST('args')
02532     SENTINEL = \
02533         pp.StringStart() +\
02534         pp.CaselessKeyword('end') +\
02535         KEYWORD
02536     #.............................................................
02537 
02538     @staticmethod
02539     def factory(s, lineNo, tokens):
02540         '''Factory method called by the BlockParser to produce an
02541         instance of the class.
02542 
02543         '''
02544         name = tokens.args[0]
02545         errorToken = tokens.args[1]
02546         return GarbageCollect(['gc_'+name, name, errorToken])
02547 
02548     def __init__(self, parameters):
02549         '''An GarbageCollect that works in concert with the AutoDelete block.
02550         It autogenerates calls to the delete(...) function for symbols found
02551         in the AutoDelete block.  For instance, if you have the following auto
02552         delete block:
02553 
02554         AutoDelete(locals)
02555           type (DLList<integer>) :: a,b,c
02556         end AutoDelete
02557 
02558         Then, a GarbageCollect block like this:
02559 
02560         GarbageCollect(locals, lerr)
02561         end GarbageCollect
02562 
02563         will generate:
02564 
02565         lerr = catch(delete(a), .true.)
02566         lerr = catch(delete(b), .true.)
02567         lerr = catch(delete(c), .true.)
02568 
02569 
02570         Input
02571         -----
02572         parameters : the groups from the factory static method
02573 
02574         '''
02575         super(GarbageCollect, self).__init__(self.BLOCKTYPE, parameters)
02576         self._autoDelName = parameters[1]
02577         self._err = parameters[2]
02578 
02579     def show(self, mangle=False):
02580         '''Work with the AutoDelete block to auto create delete calls.
02581 
02582         '''
02583 
02584         block = ''
02585         name = self._autoDelName
02586         err = self._err
02587         n = tab(self._code[0])
02588         if len(self._code) > 0:
02589             _n = max(0, tab(self._code[1])-n)
02590 
02591         autoDelBlocks = self._parent.findAll((AutoDelete,), depth=1)
02592         auto = [x for x in autoDelBlocks
02593                 if x.name().startswith('autodelete_'+name)]
02594 
02595         # A Specialization block may contain an AutoDelete
02596         specBlocks = self._parent.findAll((Specialization,), depth=1)
02597         for x in specBlocks:
02598             auto.extend([y for y in x.findAll((AutoDelete,), depth=1)
02599                          if y.name().startswith('autodelete_'+name)])
02600 
02601         # auto = self.parent().children().filter(startsWith('autodelete_'+name)).values()
02602         if len(auto) > 1:
02603             print 'Found multiple autodelete blocks:'
02604             for x in auto:
02605                 print '-> %s'%(x.name(),)
02606             raise Exception('More than one autodelete block found in parent!')
02607         elif len(auto) == 0:
02608             return block
02609 
02610         auto = auto[0]
02611 #         for sym in auto.getSymbols():
02612 #             block += indent(n, err+' = catch(delete(%s), .true.)%s'%(sym,os.linesep))
02613 
02614         for sym in auto.symbols:
02615             block += indent(n, err+' = catch(delete(%s), .true.)%s'%(sym,os.linesep))
02616 
02617         for line in self._code[1:-1]:
02618             block += line[_n:]
02619 
02620         return block
02621 
02622 #................................................................................
02623 class Specialization(FortranBlock):
02624     BLOCKTYPE = 'Specialization'
02625     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
02626     GRAMMAR = KEYWORD+pp.LineEnd()
02627     SENTINEL = \
02628         pp.CaselessKeyword('end') +\
02629         KEYWORD
02630     SPECIAL = \
02631         pp.Suppress(pp.CaselessKeyword('template')) +\
02632         pp.Suppress(LEFT_TEMPTOK) + \
02633         pp.Optional(pp.Combine(TEMPLATE_PARAM_LIST, adjacent=False))('spec') +\
02634         pp.Suppress(RIGHT_TEMPTOK)
02635 
02636     @staticmethod
02637     def factory(s, lineNo, tokens):
02638         '''Factory method called by the BlockParser to produce an
02639         instance of the class.
02640 
02641         '''
02642         return Specialization(['special'])
02643 
02644     def __init__(self, parameters):
02645         '''This is a specialization block which should only appear inside a
02646         template block.  It is used to produce code that appears within it for
02647         certain template parameters.  This is an example:
02648 
02649         template <class Object, class Element>
02650 
02651         Specialization
02652         template <real, real>
02653         function foo(bar, a,b,c) result(blah)
02654            type (myType<real, real>) :: bar
02655            real :: a,b
02656            integer :: c
02657 
02658            bar%a = bar%a * a
02659            bar%b = bar%b * b
02660         end function foo
02661 
02662         template <>
02663         function foo(bar, a,b,c) result(blah)
02664            type (myType<Object, Element>) :: bar
02665            real :: a,b
02666            integer :: c
02667            print *, 'This is the default'
02668         end function foo
02669         end Specialization
02670 
02671         end template
02672 
02673         The above Specialization block will only show the first function if Object is
02674         real and Element is real, but shows the second function default
02675         (i.e. Using <>) otherwise.
02676 
02677         Input
02678         -----
02679         parameters : the groups from the factory static method
02680 
02681         '''
02682 
02683         super(Specialization, self).__init__(self.BLOCKTYPE, parameters)
02684         self._name = parameters[0]+'_'+str(id(self))
02685         self._specLists = []
02686         self._codeBuff = ''
02687         self._default = None
02688         self._SPECIAL = self.SPECIAL + pp.SkipTo(self.SPECIAL|self.SENTINEL)
02689         self._SPECIAL.setParseAction(self._addSpec)
02690 
02691     def _addSpec(self, s, l, tokens):
02692         '''Create specialization list where each entry is a tuple of template
02693         parameters tuple and corresponding code.
02694 
02695         '''
02696         end = pp.getTokensEndLoc()
02697         code = s[l:end].splitlines(True)
02698         code.append(os.linesep) # append carriage return
02699         if tokens.spec == '':
02700             self._default = code[1:]
02701             spec = ('',)
02702         else:
02703             spec = templateSpecToTuple(tokens.spec)
02704         self._specLists.append((spec, code[1:]))
02705 
02706     def setState(self, state):
02707         '''Overloaded to select which code block to use.  After the fist call,
02708         a code block is selected.  Subsequent calls can be used to switch to
02709         others.
02710 
02711         '''
02712         self._state = state
02713         if state is None:
02714             self._state = None
02715 #             if len(self._specLists) == 0:
02716 #                 self._code = self._codeBuff.splitLines()
02717             return
02718 
02719         self._code = []
02720         for spec,code  in self._specLists:
02721             if all([(x[1] == y) for x,y in zip(self._state, spec) if y != '*']):
02722                 self._code = code
02723                 return
02724 
02725         if self._default:
02726             self._code = self._default
02727 
02728     def addLine(self, line, n=None):
02729         '''Overloaded addLine to add to code buffer and parse later in
02730         sentinel method.
02731 
02732         '''
02733         self._codeBuff += line
02734 
02735     def show(self, mangle=False):
02736         '''Overloaded show to break _codeBuff into code lines.
02737 
02738         '''
02739         self._code = self._codeBuff.splitlines(True)
02740         return super(Specialization, self).show(mangle)
02741 
02742     def sentinel(self, line):
02743         '''Overloaded sentinel method to grab all the specialization blocks.
02744 
02745         '''
02746         if self.SENTINEL.searchString(line):
02747             self.addLine(line)
02748             self._SPECIAL.searchString(self._codeBuff)
02749             return True
02750 
02751     def __getstate__(self):
02752         '''Builds a dictionary tree of the block.
02753 
02754         '''
02755         _dd = self.__dict__.copy()
02756         _dd.pop('_SPECIAL', None)
02757         return super( Specialization, self ).__getstate__( _dd )
02758 
02759     def serialize( self ):
02760         return self.__getstate__()
02761 
02762 #................................................................................
02763 class Instantiate( FortranBlock ):
02764     BLOCKTYPE = 'Instantiate'
02765     KEYWORD = pp.Suppress(pp.CaselessKeyword(BLOCKTYPE))
02766     OPTNAME = pp.Optional(VALIDNAME)('name')
02767     GRAMMAR = KEYWORD + OPTNAME + pp.LineEnd()
02768     SENTINEL = \
02769         pp.CaselessKeyword('end') +\
02770         KEYWORD
02771 
02772     @staticmethod
02773     def factory(s, lineNo, tokens):
02774         '''Factory method called by the BlockParser to produce an
02775         instance of the class.
02776 
02777         '''
02778         name = tokens.name
02779         return Instantiate( [ 'instantiate_' + tokens.name ] )
02780 
02781     def __init__(self, parameters):
02782         '''An instantiate block is simply used to instantiate template symbols
02783         listed in the block.  This can be used to separate the build cycle
02784         into phases.  Phase I would be to have instantiate blocks build all
02785         the templates for you and put them somewhere.  Phase II would be to
02786         read the sources and mangle the template names.  Phase III would be to
02787         parse the headers of all the files and build the dependencies for a
02788         Makefile. Also, these blocks can be placed in a file directly to
02789         trigger instantiations.  This can be useful if you place functions
02790         within a templated module and use an `as` name to wrap them in generic
02791         interfaces.  You can the simply use the `as` name in your code instead
02792         of a template call.
02793         '''
02794 
02795         super(Instantiate, self).__init__(self.BLOCKTYPE, parameters)
02796         self._name = parameters[0]+'_'+str(id(self))
02797 
02798     def show( self, mangle ):
02799         return ''
02800 
02801 #................................................................................
02802 class Pod(FortranBlock):
02803     BLOCKTYPE = 'Pod'
02804     podWord = BLOCKTYPE
02805     #.....................................................................
02806     # pyparsing grammar
02807     #.....................................................................
02808     KEYWORD = pp.CaselessKeyword(BLOCKTYPE)
02809     VALIDNAME = pp.Word(pp.alphas, pp.alphanums+'_')
02810     FROM = pp.CaselessKeyword('from')
02811 
02812     GRAMMAR = \
02813         pp.Suppress(KEYWORD) +\
02814         VALIDNAME('podName') +\
02815         pp.Suppress(FROM) +\
02816         VALIDNAME('modName')
02817     #.....................................................................
02818 
02819     @staticmethod
02820     def factory(s, lineNo, tokens):
02821         '''Factory method called by the BlockParser to produce an
02822         instance of the class.
02823 
02824         '''
02825         return Pod(tokens.asList())
02826 
02827     def __init__(self, parameters):
02828         '''A (P)ython (O)n (D)emand block:
02829 
02830         pod foo from barmodule
02831         .
02832         .
02833         .
02834         end pod foo
02835 
02836         The code contained within the block is processed AT SHOW TIME.  Use
02837         the Prepod block for processing at parse time.
02838 
02839         Input
02840         -----
02841         parameters : the groups from the class regex
02842         table : the global symbol table to store the block in
02843 
02844         '''
02845         super(Pod, self).__init__(self.BLOCKTYPE, parameters)
02846         self._name = parameters[0].partition('~')[0]+'~'+str(id(self))
02847 
02848         # Local parser and scanner
02849         self._scan = LineScanner()
02850         self._module = None
02851         self._fnc = None
02852         self._parser = FortranBlockParser(self._scan)
02853         self._parser.register([Template,
02854                                Module,
02855                                Function,
02856                                Subroutine,
02857                                Type,
02858                                Interface,
02859                                Pod,
02860                                Program,
02861                                AutoInterface,
02862                                AutoPrivate,
02863                                AutoUse])
02864 
02865     def name(self):
02866         # No mangled names for interface names within templates
02867         return self._name
02868 
02869     def show(self, mangle):
02870         '''Overloaded show() for Pods.
02871 
02872         '''
02873         self._module = __import__(self._parameters[1])
02874         self._fnc = getattr(self._module, self._parameters[0].partition('~')[0], None)
02875         if self._fnc is None:
02876             raise Exception("Could not find pod: %s"%(str(self._parameters[0]),))
02877 
02878         # Strip all leading spaces from code block and indent to proper Python
02879         # Strip the 'pod foo ...' and 'end pod' lines
02880         code = ''
02881         for line in self._code[1:]:
02882             if line.rstrip() != '':
02883                 nspace = tab(line) # indentation of Python code
02884                 break
02885         code = ''.join(map(lambda s: s[nspace:], self._code[1:-1]))
02886 
02887         #......................................................................
02888         # Use the local line scanner, symbol table as a copy of
02889         # globalSymbolTable and block parser on the returned code string.
02890         # Parse it and call show on the returned block from the parser.  This
02891         # will perform all the necessary substitutions and name mangling.
02892         #......................................................................
02893         nspace = tab(self._code[0])
02894         code = self._fnc(code, globals())
02895         code = (os.linesep).join(map(lambda x: indentNoStrip(nspace, x),
02896                                      code.splitlines()))
02897         code += os.linesep
02898         self._scan.loadString(code, self.name())
02899         blk = self._parser.parse(AFile(parameters=[self._scan.name(),
02900                                                    self._scan.hash()]))
02901 
02902         _dots = indent(nspace, '!'+'.'*(80-nspace)+os.linesep)
02903         _banner = _dots
02904         _banner += indent(nspace,'! Pod %s Generated Code Block%s'%(self._name,os.linesep))
02905         _banner += _dots
02906         return _banner + blk.show(mangle) + _dots
02907 
02908     def __getstate__(self):
02909         _dd = self.__dict__.copy()
02910         _dd.pop('_fnc', None)
02911         _dd.pop('_module', None)
02912         _dd.pop('_parser', None)
02913         _dd.pop('_scan', None)
02914         return super( Pod, self ).__getstate__( _dd )
02915 
02916     def serialize(self):
02917         '''Builds a dictionary tree of the block.
02918 
02919         '''
02920         return self.__getstate__()
02921 
02922 #................................................................................
02923 class Prepod(FortranBlock):
02924     BLOCKTYPE = 'Prepod'
02925     podWord = BLOCKTYPE
02926     #.....................................................................
02927     # pyparsing grammar
02928     #.....................................................................
02929     KEYWORD = pp.CaselessKeyword(BLOCKTYPE)
02930     VALIDNAME = pp.Word(pp.alphas, pp.alphanums+'_')
02931     FROM = pp.CaselessKeyword('from')
02932 
02933     GRAMMAR = \
02934         pp.Suppress(KEYWORD) +\
02935         VALIDNAME('podName') +\
02936         pp.Suppress(FROM) +\
02937         VALIDNAME('modName')
02938     #.....................................................................
02939 
02940     @staticmethod
02941     def factory(s, lineNo, tokens):
02942         '''Factory method called by the BlockParser to produce an
02943         instance of the class.
02944 
02945         '''
02946         return Prepod(tokens.asList())
02947 
02948     def __init__(self, parameters):
02949         '''A (P)ython (O)n (D)emand block:
02950 
02951         prepod foo from barmodule
02952         .
02953         .
02954         .
02955         end prepod foo
02956 
02957         The code contained within the block is processed AT PARSING TIME.
02958 
02959         Input
02960         -----
02961         parameters : the groups from the class regex
02962         table : the global symbol table to store the block in
02963 
02964         '''
02965         super(Prepod, self).__init__(self.BLOCKTYPE, parameters)
02966         self._name = parameters[0]+'_'+str(id(self))
02967         self._module = __import__(self._parameters[1])
02968         self._fnc = getattr(self._module, parameters[0], None)
02969 
02970         # Local parser and scanner
02971         self._scan = LineScanner()
02972         self._parser = FortranBlockParser(self._scan)
02973         self._parser.register([Template,
02974                                Module,
02975                                Function,
02976                                Subroutine,
02977                                Type,
02978                                Interface,
02979                                Program,
02980                                AutoInterface,
02981                                AutoPrivate,
02982                                AutoUse])
02983 
02984     def name(self):
02985         # No mangled names for interface names within templates
02986         return self._name
02987 
02988     def sentinel(self, line):
02989         '''Overloaded sentinel() for prepods.
02990 
02991         '''
02992         if not self.SENTINEL.searchString(line): return False
02993         if self._fnc is None:
02994             raise Exception("Could not find prepod: %s"%(str(self._fnc,)))
02995 
02996         # Strip all leading spaces from code block and indent to proper Python
02997         # Strip the 'prepod ...' and 'end prepod' lines
02998         code = ''
02999         _n = tab(self._code[0])           # indentation of Prepod block
03000         for line in self._code[1:]:
03001             if line.rstrip() != '!':
03002                 nspace = tab(line[1:]) + 1 # indentation of Python code
03003                 break
03004         # nspace = tab(self._code[1][1:])+1 # indentation of Python code
03005         code = ''.join(map(lambda s: s[nspace:], self._code[1:]))
03006 
03007         #......................................................................
03008         # Logic:
03009         #
03010         # Use the local line scanner, a new symbol table and local block
03011         # parser on the returned code string.  Load the code string into the
03012         # scanner, clear the prepod code string and call the block parser with
03013         # self as the starting block.
03014         #......................................................................
03015         self._scan.loadString(self._fnc(code, globals()), self.name())
03016         self._code = []
03017         self._parser.parse(self)
03018         self._code = map(lambda s: indent(_n, s), self._code)
03019 
03020         _dots = indent(_n, '!'+'.'*(80-nspace)+os.linesep)
03021         _banner = _dots
03022         _banner += indent(_n,'! Prepod %s Generated Code Block%s'%(self._name,os.linesep))
03023         _banner += _dots
03024         self._code.insert(0,_banner)
03025         self._code.append(os.linesep+_dots)
03026 
03027         return True
03028 
03029     def __getstate__(self):
03030         _dd = self.__dict__.copy()
03031         _dd.pop('_fnc', None)
03032         _dd.pop('_module', None)
03033         _dd.pop('_parser', None)
03034         _dd.pop('_scan', None)
03035         return super( Prepod, self ).__getstate__(_dd)
03036 
03037     def serialize(self):
03038         '''Builds a dictionary tree of the block.
03039 
03040         '''
03041         return self.__getstate__()
03042 
03043 #................................................................................
03044 class PyF95Directives( FortranBlock ):
03045     BLOCKTYPE = 'PyF95Directives'
03046     #.....................................................................
03047     # pyparsing grammar
03048     #.....................................................................
03049     GRAMMAR = PYF95_START
03050     SENTINEL = PYF95_END
03051 
03052     @staticmethod
03053     def factory( s, l, t):
03054         blk = PyF95Directives( [ PyF95Directives.BLOCKTYPE ] )
03055         return blk
03056 
03057     def __init__( self, parameters ):
03058         super( PyF95Directives, self ).__init__( self.BLOCKTYPE, parameters )
03059         self._name = parameters[ 0 ]
03060         self.external = []
03061         self.alwayswrite = False
03062 
03063         # Override the DEP_GRAMMAR set in FortranBlock
03064         self.DEP_GRAMMAR = \
03065             ( PYF95_EXTERNAL.copy() ).setParseAction( self.__external ) | \
03066             ( PYF95_ALWAYSWRITE.copy() ).setParseAction( self.__alwayswrite )
03067 
03068     def sentinel( self, line ):
03069         if self.SENTINEL.searchString( line ):
03070             self.addLine( line )
03071             return True
03072         return False
03073 
03074     def __external( self, s, l, t ):
03075         self.external.append( t.symbol )
03076 
03077     def __alwayswrite( self, s, l, t ):
03078         setattr( self, 'alwayswrite', ( t.bool.lower() == "true" ) )
03079 
03080     def show( self, mangle=False ):
03081         return ''.join( self._code ).replace( '#', '!' )
03082 
 All Classes Namespaces Files Functions Variables Properties