BlockIt
|
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