00001
00002
00003
00004
00005 from _Setup import *
00006 from PyUtil.fval import Fval
00007
00008 def _noop(s,*a,**kw):
00009 'mutator _noop primitive'
00010 pass
00011
00012 def _prim_m(s,*a,**kw):
00013 'non-node noop'
00014 return s
00015
00016 class LexiMutator(object):
00017 'mutator defined by lexicon (ie by cases)'
00018 def __init__(self,lexi,prim=_prim_m):
00019 self.lexi = lexi
00020 self.prim = Fval(prim)
00021
00022 def setup(self):
00023 for (cls,fn) in self.lexi:
00024 cls.mutator = fn
00025 _Mutable_T.prim_mutate = self.prim
00026
00027 def restore(self):
00028 for (cls,dc) in self.lexi:
00029 del cls.mutator
00030 _Mutable_T.mutator = _noop
00031 _Mutable_T.prim_mutate = Fval(_prim_m)
00032
00033 clr = restore
00034
00035 class FnMutator(object):
00036 'mutator defined as a single function'
00037 def __init__(self,fn,prim=_prim_m):
00038 self.fn = fn
00039 self.prim = Fval(prim)
00040
00041 def setup(self):
00042 _Mutable_T.mutator = self.fn
00043 _Mutable_T.prim_mutate = self.prim
00044
00045 def restore(self):
00046 _Mutable_T.mutator = _noop
00047 _Mutable_T.prim_mutate = Fval(_prim_m)
00048
00049
00050
00051 class __Path(object):
00052 '''abstract path class:
00053 when a value is mutated to a new node class(!), then
00054 the path must be reassigned the new value.
00055 '''
00056 class _Spath(object):
00057 '''a scalar path: base object + attribute name are sufficient
00058 '''
00059 def __init__(self,base,attr):
00060 self.base = base
00061 self.attr = attr
00062
00063 def set(self,val):
00064 'set a value for a scalar path'
00065 setattr(self.base,self.attr,val)
00066
00067 class _Lpath(object):
00068 '''a list path object: base object + attribute + index
00069 '''
00070 def __init__(self,base,attr,idx):
00071 self.base = base
00072 self.attr = attr
00073 self.idx = idx
00074
00075 def set(self,val):
00076 'set a value for an element of a list'
00077 m = self.base.sonv(self.attr)
00078 m[self.idx] = val
00079
00080
00081
00082 def is_leaf(v):
00083 '''a node is a leaf if:
00084 1) it is a token OR
00085 2) it has an empty sons list
00086 '''
00087 return isinstance(v,str) or (hasattr(v,'sons') and not v.sons)
00088
00089 def _prim(n):
00090 'if "node" n has no mutate method, then it is primitive'
00091 return not hasattr(n,'mutate')
00092
00093 class _Mutable_T(object):
00094 '''Support mutable tree data structures:
00095 expect a sons element in the class that describes what
00096 attributes hold subtrees.
00097 if one of the subtree attributes is list valued, then
00098 list
00099 '''
00100
00101 def putpth(self,pth):
00102 self._pth = pth
00103 return self
00104
00105 def clrpth(self):
00106 del self._pth
00107 return self
00108
00109 def prune(self,v=True):
00110 self._prune = v
00111 return self._prune
00112
00113 def pruneq(self):
00114 return hasattr(self,'_prune') and self._prune
00115
00116 def repl(self,val):
00117 'replace self with val: use pth to link to new val'
00118 self._pth.set(val)
00119
00120 def sonv(self,son):
00121 '''synonym for getattr'''
00122 return getattr(self,son)
00123
00124 def idx_son_val(self,son,idx):
00125 '''get an element from a list-valued son'''
00126 tmp = getattr(self,son)
00127 return tmp[idx]
00128
00129 def subtrees(self):
00130 'create a list of (path,subtree) pairs for a given node'
00131 rv = []
00132 for a in self._sons:
00133 son_val = self.sonv(a)
00134 if isinstance(son_val,list):
00135 rv.extend([(_Lpath(self,a,i),self.idx_son_val(a,i))
00136 for i in range(len(son_val))])
00137 else:
00138 rv.append((_Spath(self,a),son_val))
00139 return rv
00140
00141 mutator = _noop
00142 prim_mutate = Fval(_prim_m)
00143
00144 def mapp(self,*arg):
00145 'like mapc, but the mappor may be deleted'
00146 self.mappor(*arg)
00147
00148 def visit(self,*arg):
00149 '''primarily operate on arg'''
00150 rv = self.visitor(*arg)
00151 if not (self.pruneq() or is_leaf(self)):
00152 for (pth,n) in self.subtrees():
00153 if _prim(n):
00154 self.prim_visit(n,pth,*arg)
00155 else:
00156 n.putpth(pth).visit(*arg)
00157 n.clrpth()
00158
00159 return rv
00160
00161 def mutate(self,oth=None):
00162 '''The mutation walker:
00163 apply the mutator to self, THEN
00164 mutate all subtrees
00165 '''
00166 rv = self.mutator(oth)
00167 if not (self.pruneq() or is_leaf(self)):
00168 for (pth,n) in self.subtrees():
00169 if _prim(n):
00170 self.prim_mutate(n,pth,oth)
00171 else:
00172 n.putpth(pth).mutate(oth)
00173 n.clrpth()
00174
00175 def mutate(top,mutator,oth=None):
00176 '''mutate the substructure of the top level object (top).
00177 Use the mutator function to do it.
00178 !!! NOTE: This is not quite the same as the method. The
00179 mutator is NOT applied to the toplevel object
00180 '''
00181 mutator.setup()
00182 for (pth,n) in top.subtrees():
00183 if _prim(n):
00184 top.prim_mutate(n,pth,oth)
00185 else:
00186 n.putpth(pth).mutate(oth)
00187 n.clrpth()
00188 mutator.restore()
00189 return top