BlockIt
blocks.py
Go to the documentation of this file.
00001 ##
00002 # @file blocks.py
00003 # @brief Contains the basic block class.
00004 #
00005 #............................................................................
00006 # Copyright (c) 2009,2010,2011 David Car, david.car7@gmail.com
00007 #
00008 # This program is free software; you can redistribute it and/or modify it under
00009 # the terms of the GNU General Public License as published by the Free Software
00010 # Foundation; either version 2 of the License, or (at your option) any later
00011 # version.
00012 #
00013 # This program is distributed in the hope that it will be useful, but WITHOUT
00014 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00015 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00016 #
00017 # You should have received a copy of the GNU General Public License along with
00018 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
00019 # Place, Suite 330, Boston, MA 02111-1307 USA
00020 #..........................................................................
00021 from funcs import *
00022 from tables import SymbolTable
00023 from sets import ASet
00024 from parsers import pyp
00025 from time import time as sys_time
00026 import os
00027 import re
00028 import hashlib
00029 import copy
00030 from math import log
00031 from math import ceil
00032 
00033 class Block(object):
00034     """This is the base block class.
00035     """
00036 
00037     #................................................................................
00038     # pyparsing grammars
00039     #................................................................................
00040     GRAMMAR = None
00041     SENTINEL = None
00042     CHILDTOKEN = None
00043     BLOCKTYPE='Block'
00044     #................................................................................
00045 
00046     def __init__(self, blockType, name, parent=None, childToken=None):
00047         self._parent = parent
00048         self._code = []
00049         self._children = SymbolTable()
00050         self._name = name
00051         self._blockType = blockType
00052         self._childDeps = ASet()
00053         self._ownDeps = ASet()
00054         self._childToken = childToken
00055         self._depList = []
00056         self._depIgnore = []
00057         self._state = None
00058         self._fromCache = False
00059         self._timeStamp = sys_time()
00060 
00061         #................................................................................
00062         # pyparsing grammars
00063         #................................................................................
00064         jc = self._children.joinCharacter()
00065         if self.SENTINEL is None:
00066             self.SENTINEL = \
00067                 pyp.StringStart() +\
00068                 pyp.CaselessKeyword('end') +\
00069                 pyp.CaselessKeyword(self._blockType) +\
00070                 pyp.CaselessKeyword(self._name)
00071 
00072         if childToken is None:
00073             self.CHILDTOKEN = \
00074                 pyp.StringStart() +\
00075                 pyp.Suppress(pyp.Literal('#')) +\
00076                 pyp.Suppress(pyp.CaselessKeyword('Child:')) +\
00077                 pyp.Word(pyp.alphas, pyp.alphanums+'_~<>=()'+jc)('symbol')
00078 
00079     def getTimeStamp( self ):
00080         """ Return the time stamp of the block.
00081         """
00082         return self._timeStamp
00083     
00084     def addLine( self, line, n=None ):
00085         '''Rudimentary method to add a line to the code listing of the block.
00086 
00087         '''
00088         self._code.append( line )
00089 
00090     def addDeps( self, sym, propagate=True ):
00091         '''Add an internal dependency and propagate up the tree.  Do not add
00092         dependencies that are resolved by any of your children.  This is
00093         checked by consulting the child nodes to see if they contain the
00094         dependency.  Call the addChildDeps() on the parent.  The dependency is
00095         given as a symbol name, not a set.
00096 
00097         '''
00098         if sym in self._depIgnore: return
00099         if self.contains(sym): return
00100         self._ownDeps.add(sym)
00101         if propagate and self._parent: self._parent.addChildDeps(sym)
00102 
00103     def addChildDeps( self, sym, propagate=True, skipList=None ):
00104         '''Add the dependencies from a child block to the _childDeps set.
00105 
00106         '''
00107         if sym in self._depIgnore: return
00108         if self.contains( sym, skipList ): return
00109         self._childDeps.add( sym )
00110         if propagate and self._parent: 
00111             self._parent.addChildDeps( sym, propagate, skipList )
00112 
00113     def shareDeps( self, propagate=True ):
00114         '''Propagate the dependencies to the parent.  If propagate is True,
00115         then continue up the tree.
00116 
00117         '''
00118         parent = self._parent
00119         if parent:
00120             for sym in self.dependencies():
00121                 parent.addChildDeps( sym, propagate, skipList=( self, ) )
00122         
00123     def contains( self, sym, skipList=None ):
00124         '''Search to see if block has symbol.
00125 
00126         '''
00127         if not self.isTemplated():
00128             # if self.resolveSymbol( sym ) in self._children:  return True
00129             if sym in self._children:  return True
00130             if skipList is not None:
00131                 _children = [ x for x in self._children.values()
00132                               if x not in skipList ]
00133             else:
00134                 _children = self._children.values()
00135             for child in _children:
00136                 if child.contains( sym, skipList ): return True
00137         return False
00138             
00139     def addChild( self, blk, token=None, before=False, regen=True ):
00140         '''Add a block as a child of this block.  If a token string is given, then add
00141         this child as a token before/after this string in the code, else,
00142         just append it to the code at the end.
00143 
00144         Inputs
00145         ------
00146         blk : child block to add
00147         token : token string to use for adding line after/before
00148         before : boolean.  If true, add child line before given token; else after.
00149 
00150         '''
00151         addChildDeps = self.addChildDeps
00152 
00153         self._children.register( blk )
00154         blk.setParent( self, regen )
00155         blk.ignoreDependencies( self._depIgnore )
00156         blk.removeDependencies( self._depIgnore )
00157 
00158         #.................................................................
00159         # Check if this new child resolves any dependencies
00160         #.................................................................
00161         for sym in list( self._ownDeps ):
00162             if blk.contains( sym ):
00163                 self._ownDeps.discard( sym )
00164 
00165         for sym in list( self._childDeps ):
00166             if blk.contains( sym ):
00167                 self._childDeps.discard( sym )
00168 
00169         #............................................................
00170         # Now add the child dependencies to the parent
00171         #............................................................
00172         for sym in blk.dependencies():
00173             addChildDeps( sym )
00174 
00175         #............................................................
00176         # Add the child line after/before token
00177         #............................................................
00178         if token:
00179             try:
00180                 n = self._code.index( token ) + 1
00181             except:
00182                 n = None
00183         else:
00184             n = None
00185 
00186         self.addLine( self.childString( blk.name() ), n )
00187 
00188     def mergeChildren( self, tbl ):
00189         '''Merge a symbol table with children into the block and add their
00190         dependencies.  Does nothing to the underlying code base though.
00191         Useful only in certain circumstances.
00192 
00193         '''
00194         addChildDeps = self.addChildDeps
00195         for blk in tbl.values():
00196             self._children.register( blk )
00197             for sym in blk.dependencies():
00198                 addChildDeps( dep )
00199 
00200     def setParent(self, blk, regen=True):
00201         '''Set the parent block for this block.  If this block has a parent,
00202         remove it from that blocks children.
00203 
00204         '''
00205         if self._parent is blk:  return
00206 
00207         if self._parent:
00208             self._parent.popChild( self.name(), regen )
00209 
00210         self._parent = blk
00211 
00212 #     def setChild(self, blk):
00213 #         '''Add a child block to this block, but does not touch the code like the
00214 #         addChild(...) method does.  It simply puts the child into this blocks
00215 #         local symbolTable (_children).
00216 
00217 #         '''
00218 #         self._children.register(blk)
00219 
00220     def popChild(self, name, regen=True):
00221         '''Remove a child from this block.
00222 
00223         '''
00224         self._code.remove( self.childString( name ) )
00225         child = self._children.pop( name )
00226         if regen: self.regenChildDeps()
00227         return child
00228 
00229     def regenChildDeps( self ):
00230         '''Clear the child dependency list and regenerate it from the children.
00231 
00232         '''
00233         self._childDeps.clear()
00234         for blk in self._children.values():
00235             for dep in blk.dependencies():
00236                 self.addChildDeps( dep, propagate=False )
00237         
00238         if self._parent: self._parent.regenChildDeps()
00239 
00240     def baseName( self ):
00241         '''Return the base, i.e. non-instantiated, non-mangled, name of the block.
00242 
00243         '''
00244         return self._name
00245 
00246     @staticmethod
00247     def childString( name ):
00248         '''Create a child string token.
00249 
00250         '''
00251         return '# Child: %s%s' %( name, os.linesep )
00252 
00253     def name( self ):
00254         '''Return the name of the block.
00255 
00256         '''
00257         return self._name
00258 
00259     def blockType(self):
00260         return self._blockType
00261 
00262     def sentinel(self, line):
00263         if self.SENTINEL is None: return False
00264         if self.SENTINEL.searchString(line):
00265             self.addLine(line)
00266             return True
00267         return False
00268 
00269     def children(self):
00270         return self._children
00271 
00272     def dependencies(self):
00273         return self._ownDeps.union(self._childDeps)
00274 
00275     def clearChildDependencies( self ):
00276         self._childDeps.clear()
00277 
00278     def clearOwnDependencies( self ):
00279         self._ownDeps.clear()
00280 
00281     def clearAllDependencies( self ):
00282         self.clearChildDependencies()
00283         self.clearOwnDependencies()
00284 
00285     def renameDependencies( self, nameMap ):
00286         '''Rename an existing dependency in the dependency list and propagate
00287         to the children.
00288 
00289         '''
00290         if isinstance( nameMap, list ):
00291             for oldName,newName in nameMap:
00292                 self._ownDeps.discard( oldName )
00293                 self._ownDeps.add( newName )
00294                 if oldName in self._childDeps:
00295                     self._childDeps.discard( oldName )
00296                     self._childDeps.add( newName )
00297 
00298             for child in self._children:
00299                 child.renameDependency( nameMap )
00300         else:
00301             oldName, newName = nameMap
00302             self._ownDeps.discard( oldName )
00303             self._ownDeps.add( newName )
00304             if oldName in self._childDeps:
00305                 self._childDeps.discard( oldName )
00306                 self._childDeps.add( newName )
00307 
00308             for child in self._children:
00309                 child.renameDependency( oldName, newName )
00310             
00311     def removeDependencies( self, depList ):
00312         '''Remove a list of token dependencies from both dependencies lists
00313         and traverse down the tree recursively to the children and do the
00314         same.
00315         '''
00316         for dep in depList:
00317             self._ownDeps.discard( dep )
00318             self._childDeps.discard( dep )
00319         for child in self._children.values():
00320             child.removeDependencies( depList )
00321 
00322     def ignoreDependencies( self, depList ):
00323         '''A list of dependencies that should be ignored.
00324 
00325         '''
00326         self._depIgnore = copy.copy( depList )
00327         for child in self._children.values():
00328             child.ignoreDependencies( depList )
00329 
00330     def getIgnoreDependencies( self ):
00331         return copy.copy( self._depIgnore )
00332 
00333     def parent(self):
00334         return self._parent
00335     
00336     def hasChild(self, blk):
00337         return (blk.name() in self._children)
00338 
00339     def root(self):
00340         '''Return the root of the block tree starting at this block.
00341 
00342         '''
00343         if self._parent:
00344             return self._parent.root()
00345         return self
00346 
00347     def upTo(self, blkType):
00348         '''Travel up the tree and return the first blkType found in this
00349         blocks lineage.
00350 
00351         '''
00352         if isinstance(self, blkType): return self
00353         if self._parent:
00354             return self._parent.upTo(blkType)
00355         return None
00356         
00357     def objects(self):
00358         return self._objects
00359 
00360     def code(self, lineNumbers=False):
00361         '''Show the code strings with or without line numbers.  The default is
00362         no line numbers.
00363 
00364         '''
00365         block = ''
00366         _srcLength = int(ceil(log(len(self._code)+1)/log(10)))
00367         if lineNumbers:
00368             for n,line in enumerate(self._code):
00369                 block += "%s %s%s"%(str(n).zfill(_srcLength+1),line.rstrip(), os.linesep)
00370         else:
00371             for line in self._code:
00372                 block += line.rstrip()+os.linesep
00373 
00374         return block
00375 
00376     def codeList(self):
00377         return self._code
00378 
00379     def show(self):
00380         '''Show the block unrolled.
00381 
00382         '''
00383         block = ''
00384         for line in self._code:
00385             child = self.CHILDTOKEN.searchString(line)
00386             if child:
00387                 sym = child[0].symbol
00388                 _blk = self._children.get(sym, None)
00389                 if _blk is None:
00390                     print 'Child %s has disappeared from block %s'%(sym, self.name())
00391                     line = ''
00392                 else:
00393                     line = _blk.show()
00394             block += line
00395         return block
00396 
00397     def hash(self):
00398         '''Return a hashed value of this block if it does not already have a
00399         _hash internal value.  Uses the mangled version for the hash.
00400 
00401         '''
00402         hsh = getattr(self, '_hash', None)
00403         if hsh is None:
00404             self._hash = hashlib.sha256(self.show(True)).hexdigest()
00405         return self._hash
00406     
00407     def __getstate__(self, _dd=None):
00408         '''Builds a dictionary tree of the block for pickling.
00409 
00410         '''
00411         if _dd is None: _dd = self.__dict__.copy()
00412         _dd.pop('_children', None)
00413         _dd.pop('_parent', None)
00414         _dd.pop('SENTINEL', None)
00415         _dd.pop('CHILDTOKEN', None)
00416         _dd['_children'] = []
00417 
00418         for child in self.children().values():
00419             _dd['_children'].append( child.__getstate__() )
00420         return _dd
00421 
00422     def __setstate__(self, tree):
00423         '''Unpack the dictionary tree.
00424 
00425         '''
00426         g = globals()
00427         self.__dict__.update( tree )
00428         _children = self._children
00429         self._children = SymbolTable()
00430 
00431         for node in _children:
00432             _blockType = node[ '_blockType' ]
00433             _params = node[ '_parameters' ]
00434             child = g[ _blockType ]( _params )
00435             child.__setstate__( node )
00436             child.setParent( self )
00437             self._children.register( child )
00438 
00439     def unserialize(self, tree):
00440         self.__setstate__(tree)
00441 
00442     def serialize(self, _dd=None):
00443         '''Builds a dictionary tree of the block.
00444 
00445         '''
00446         return self.__getattr__(_dd)
00447 
00448     def toFile(self, name):
00449         '''Write the block to a special file.  This will write everything
00450         necessary to recreate the block and all its children with the fromFile
00451         class method.
00452 
00453         Input
00454         -----
00455         name : block fileName to write it to
00456 
00457         '''
00458         
00459         # Save the internal dictionary
00460         import pickle
00461 
00462         tree = self.getDictTree()
00463         hsh = getattr('_hash',None)
00464 
00465         output = open(name+'.pkl','wb')
00466         
00467         # Dump the hash value and the dictionary tree
00468         pickle.dump((self.hash(), tree), output)
00469         output.close()
00470 
00471     def lineArtGraph(self, tree=None):
00472         '''Display a line art graph for this block or the provided block tree.
00473 
00474         '''
00475         def _lineArtGraph(dt, tb=0):
00476             parent = dt
00477             if tb == 0:
00478                 print parent['_blockType']+': '+parent['_name']
00479             else:
00480                 print (tb-1)*(5*' ')+'|___ '+parent['_blockType']+': '+parent['_name']
00481 
00482             for child in parent[ '_children' ]:
00483                 # print tb*'  '+'|'
00484                 _lineArtGraph(child, tb+1)
00485 
00486         _tree = tree or self.__getstate__()
00487         _lineArtGraph(_tree)
00488 
00489     def findAll(self, blks, depth=-1):
00490         '''Find all types given in the blks tuple in your descendants to the
00491         given depth.  If depth is < 0 , then recurse all the way to the
00492         leaves.
00493 
00494         '''
00495         #............................................................
00496         def _findAll(myChildren, blks, matches, depth):
00497 
00498             if depth == 0: return
00499             _depth = depth-1
00500             
00501             for child in myChildren.values():
00502                 if isinstance(child, blks):
00503                     matches.append(child)
00504                 _findAll(child.children(), blks, matches, _depth)
00505         #............................................................
00506 
00507         matches = []
00508         _findAll(self.children(), blks, matches, depth)
00509         return matches
00510 
00511     def getFromCache(self):
00512         return self._fromCache
00513 
00514     def setFromCache(self, value):
00515         if isinstance(value, bool):
00516             self._fromCache = value
00517             if not value and self._parent: self._parent.fromCache = value
00518         else:
00519             print "fromCache must be a boolean type"
00520 
00521     #................................................................................
00522     # Virtual Methods
00523     #................................................................................
00524     def resolveSymbol(self, sym):
00525         raise NotImplementedError('''Virtual method 'resolveSymbol' in Block
00526         class''')
00527 
00528     def instantiate(self, obj):
00529         raise NotImplementedError("Virtual method 'instantiate' in Block class ")
00530 
00531     def getState(self):
00532         raise NotImplementedError("Virtual method 'getState' Block class")
00533 
00534     def setState(self, subs):
00535         raise NotImplementedError("Virtual function 'setState' in Block class")
00536 
00537     fromCache = property(getFromCache, setFromCache)
00538     
 All Classes Namespaces Files Functions Variables Properties