1 #!/usr/bin/env python 2 3 """ 4 Fix name-related operations. The code in this module operates upon simplified 5 program node trees. 6 7 Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk> 8 9 This software is free software; you can redistribute it and/or 10 modify it under the terms of the GNU General Public License as 11 published by the Free Software Foundation; either version 2 of 12 the License, or (at your option) any later version. 13 14 This software is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public 20 License along with this library; see the file LICENCE.txt 21 If not, write to the Free Software Foundation, Inc., 22 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 23 24 -------- 25 26 To use this module, first instantiate a Fixer object: 27 28 fixer = Fixer() 29 30 Then, apply the fixer to an existing Simplifier object: 31 32 simplifier = ... 33 fixer.process(simplifier) 34 35 If an existing simplifier has been used to process a module containing built-in 36 classes and functions, apply the fixer as follows: 37 38 fixer.process(simplifier, builtins_simplifier) 39 """ 40 41 from simplified import * 42 43 # Fixing of name-related operations. 44 45 class Fixer(Visitor): 46 47 """ 48 The name fixer which traverses the program nodes in a module, typically 49 depth-first, and maintains a record of name usage in the different 50 namespaces. As a consequence of various observations, some parts of the 51 program node tree are modified with different operations employed to those 52 originally defined. 53 54 There are two kinds of subprograms in modules: functions/methods and 55 internal subprograms which support things like loops. The latter kind of 56 subprogram may acquire the locals from their callers and must therefore be 57 traversed with information from such callers. Thus, we choose the top-level 58 code and all functions/methods as roots for processing, following 59 invocations of internal subprograms in order to reach all subprograms that 60 are defined in each module. 61 62 top-level 63 ... 64 invoke function 65 ... 66 invoke loop -> subprogram (internal) 67 ... 68 69 subprogram (function) 70 ... 71 invoke loop -> subprogram (internal) 72 ... 73 74 ... 75 76 The above approach should guarantee that all subprograms are traversed and 77 that all name lookups are correctly categorised. 78 """ 79 80 def __init__(self): 81 82 "Initialise the name fixer." 83 84 Visitor.__init__(self) 85 86 # Satisfy visitor issues. 87 88 self.visitor = self 89 90 def process(self, visitor, builtins_visitor=None): 91 92 """ 93 Process the resources of the given 'visitor' optionally using a 94 'builtins_visitor' to reference built-in objects. 95 """ 96 97 # The fixer maintains a list of transformed subprograms (added for each 98 # of the processing "roots" and also for each invoked internal 99 # subprogram), along with a list of current subprograms (used to avoid 100 # recursion issues) and a list of current namespaces (used to recall 101 # namespaces upon invoking internal subprograms). 102 103 self.subprograms = [] 104 self.current_subprograms = [] 105 self.current_namespaces = [] 106 107 # First, process the top-level code, finding out which names are 108 # defined at that level. 109 110 self.global_namespace = None 111 self.module = visitor.result 112 113 if builtins_visitor is not None: 114 self.builtins_module = builtins_visitor.result 115 else: 116 self.builtins_module = None 117 118 self.process_node(visitor.result) 119 120 # Then, process all functions and methods, providing a global namespace. 121 # By setting a global namespace, we influence the resolution of names: 122 # those which are global to the top-level module (processed above) are 123 # considered as built-in names, whereas those which are global to a 124 # function or method are searched for in the global namespace. 125 126 self.global_namespace = self.namespace 127 128 for subprogram in visitor.subprograms: 129 130 # Internal subprograms are skipped here and processed specially via 131 # Invoke nodes. 132 133 if not getattr(subprogram, "acquire_locals", 0): 134 self.subprograms.append(self.process_node(subprogram)) 135 136 # Ultimately, we redefine the list of subprograms on the visitor. 137 138 visitor.subprograms = self.subprograms 139 return visitor 140 141 def process_node(self, node, namespace=None): 142 143 """ 144 Process a subprogram or module 'node', discovering from attributes on 145 'node' any initial locals. Return a modified subprogram or module. 146 """ 147 148 # Do not process subprograms already being processed. 149 150 if node in self.current_subprograms: 151 return None 152 153 # Obtain a namespace either based on locals or on a structure. 154 155 structure = structure=getattr(node, "structure", None) 156 self.namespace = NameOrganiser(structure) 157 158 # Record the current subprogram and namespace. 159 160 self.current_subprograms.append(node) 161 self.current_namespaces.append(self.namespace) 162 163 # If passed some namespace, merge its contents into this namespace. 164 165 if namespace is not None: 166 self.namespace.merge_namespace(namespace) 167 168 # Register the names of parameters in the namespace. 169 170 if hasattr(node, "params"): 171 for param, default in node.params: 172 self.namespace.store(param) 173 if getattr(node, "star", None): 174 param, default = node.star 175 self.namespace.store(param) 176 if getattr(node, "dstar", None): 177 param, default = node.dstar 178 self.namespace.store(param) 179 180 # Add namespace details to any structure involved. 181 182 if hasattr(node, "structure") and node.structure is not None: 183 184 # Initialise bases where appropriate. 185 186 if hasattr(node.structure, "bases"): 187 bases = [] 188 for base in node.structure.bases: 189 bases.append(self.dispatch(base)) 190 node.structure.bases = bases 191 192 # Dispatch to the code itself. 193 194 result = self.dispatch(node) 195 result.organiser = self.namespace 196 197 # Restore the previous subprogram and namespace. 198 199 self.current_namespaces.pop() 200 if self.current_namespaces: 201 self.namespace = self.current_namespaces[-1] 202 self.current_subprograms.pop() 203 204 return result 205 206 # Visitor methods. 207 208 def default(self, node): 209 210 """ 211 Process the given 'node', given that it does not have a specific 212 handler. 213 """ 214 215 for attr in ("args",): 216 value = getattr(node, attr, None) 217 if value is not None: 218 setattr(node, attr, self.dispatches(value)) 219 for attr in ("expr", "lvalue", "test", "star", "dstar"): 220 value = getattr(node, attr, None) 221 if value is not None: 222 setattr(node, attr, self.dispatch(value)) 223 for attr in ("body", "else_", "handler", "finally_", "code", "choices"): 224 value = getattr(node, attr, None) 225 if value is not None: 226 setattr(node, attr, self.dispatches(value)) 227 return node 228 229 def dispatch(self, node, *args): 230 return Visitor.dispatch(self, node, *args) 231 232 def visitGlobal(self, global_): 233 for name in global_.names: 234 self.namespace.make_global(name) 235 return global_ 236 237 def visitLoadName(self, loadname): 238 239 "Transform the 'loadname' node to a specific, scope-sensitive node." 240 241 scope = self.namespace.find_for_load(loadname.name) 242 243 # For structure namespaces, load an attribute. 244 245 if scope == "structure": 246 result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.namespace.structure), name=loadname.name, nstype="structure")) 247 248 # For global accesses (ie. those outside the local namespace)... 249 250 elif scope == "global": 251 252 # Where a distinct global namespace exists, examine it. 253 254 if self.global_namespace is not None: 255 scope = self.global_namespace.find_for_load(loadname.name) 256 257 # Where the name is outside the global namespace, it must be a 258 # built-in. 259 260 if scope == "global": 261 result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name, nstype="module")) 262 263 # Otherwise, it is within the global namespace and must be a 264 # global. 265 266 else: 267 result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name, nstype="module")) 268 269 # Where no global namespace exists, we are at the module level and 270 # must be accessing a built-in. 271 272 else: 273 result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name, nstype="module")) 274 275 # For local accesses... 276 277 else: 278 279 # Where a distinct global namespace exists, it must be a local. 280 281 if self.global_namespace is not None: 282 result = loadname 283 284 # Otherwise, we must be accessing a global (which is local at the 285 # module level). 286 287 else: 288 result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name, nstype="module")) 289 290 return result 291 292 def visitStoreName(self, storename): 293 294 "Transform the 'storename' node to a specific, scope-sensitive node." 295 296 scope = self.namespace.find_for_store(storename.name) 297 298 # For structure namespaces, store an attribute. 299 300 if scope == "structure": 301 return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.namespace.structure), name=storename.name, expr=storename.expr, nstype="structure")) 302 303 # Where the name is outside the local namespace, disallow any built-in 304 # assignment and store the name globally. 305 306 elif scope == "global": 307 return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr, nstype="module")) 308 309 # For local namespace accesses... 310 311 else: 312 self.namespace.store(storename.name) 313 314 # If a distinct global namespace exists, it must be a local access. 315 316 if self.global_namespace is not None: 317 return storename 318 319 # Otherwise, the name is being set at the module level and is 320 # considered global. 321 322 else: 323 return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr, nstype="module")) 324 325 def visitInvoke(self, invoke): 326 327 "Transform the 'invoke' node, performing processing on subprograms." 328 329 # The special case of internal subprogram invocation is addressed by 330 # propagating namespace information to the subprogram and processing it. 331 332 if getattr(invoke, "same_frame", 0): 333 subprogram = self.process_node(invoke.expr.ref, self.namespace) 334 if subprogram is not None: 335 self.subprograms.append(subprogram) 336 return invoke 337 else: 338 return self.default(invoke) 339 340 class ScopeMismatch(Exception): 341 pass 342 343 class NameOrganiser: 344 345 """ 346 A local namespace which may either relate to a genuine set of function 347 locals or the initialisation of a structure. 348 """ 349 350 def __init__(self, structure=None): 351 352 "Initialise the namespace with an optional 'structure'." 353 354 self.structure = structure 355 if structure is not None: 356 self.local = "structure" 357 else: 358 self.local = "local" 359 360 # Names may be self.local or "global". 361 362 self.names = {} 363 364 def make_global(self, name): 365 if not self.names.has_key(name): 366 self.names[name] = "global" 367 elif self.names[name] == self.local: 368 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local) 369 370 def find_for_load(self, name): 371 return self.names.get(name, "global") 372 373 def find_for_store(self, name): 374 return self.names.get(name, self.local) 375 376 def store(self, name): 377 if self.names.get(name) != "global": 378 self.names[name] = self.local 379 else: 380 raise ScopeMismatch, "Name '%s' already considered as global." % name 381 382 def merge(self, name, scope): 383 if self.names.get(name) in (None, scope): 384 self.names[name] = scope 385 else: 386 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.names[name]) 387 388 def merge_namespace(self, namespace): 389 self.merge_items(namespace.names.items()) 390 391 def merge_items(self, items): 392 for name, scope in items: 393 self.merge(name, scope) 394 395 def __repr__(self): 396 return repr(self.names) 397 398 # vim: tabstop=4 expandtab shiftwidth=4