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, the easiest approach is to use the fix function: 27 28 fix(module) 29 30 The more complicated approach involves instantiating a Fixer object: 31 32 fixer = Fixer() 33 34 Then, applying the fixer to an existing module: 35 36 fixer.process(module) 37 38 If a module containing built-in classes and functions exists, apply the fixer as 39 follows: 40 41 fixer.process(module, builtins) 42 """ 43 44 from simplified import * 45 46 # Fixing of name-related operations. 47 48 class Fixer(Visitor): 49 50 """ 51 The name fixer which traverses the program nodes in a module, typically 52 depth-first, and maintains a record of name usage in the different 53 namespaces. As a consequence of various observations, some parts of the 54 program node tree are modified with different operations employed to those 55 originally defined. 56 57 There are two kinds of subprograms in modules: functions/methods and 58 internal subprograms which support things like loops. The latter kind of 59 subprogram may acquire the locals from their callers and must therefore be 60 traversed with information from such callers. Thus, we choose the top-level 61 code and all functions/methods as roots for processing, following 62 invocations of internal subprograms in order to reach all subprograms that 63 are defined in each module. 64 65 top-level 66 ... 67 invoke function 68 ... 69 invoke loop -> subprogram (internal) 70 ... 71 72 subprogram (function) 73 ... 74 invoke loop -> subprogram (internal) 75 ... 76 77 ... 78 79 The above approach should guarantee that all subprograms are traversed and 80 that all name lookups are correctly categorised. 81 """ 82 83 def __init__(self): 84 85 "Initialise the name fixer." 86 87 Visitor.__init__(self) 88 89 # Satisfy visitor issues. 90 91 self.visitor = self 92 93 def process(self, module, builtins=None): 94 95 """ 96 Process the given 'module' optionally using some 'builtins' to reference 97 built-in objects. 98 """ 99 100 # The fixer maintains a list of transformed subprograms (added for each 101 # of the processing "roots" and also for each invoked internal 102 # subprogram), along with a list of current subprograms (used to avoid 103 # recursion issues) and a list of current namespaces (used to recall 104 # namespaces upon invoking internal subprograms). 105 106 self.subprograms = [] 107 self.current_subprograms = [] 108 self.current_namespaces = [] 109 110 # First, process the top-level code, finding out which names are 111 # defined at that level. 112 113 self.global_namespace = None 114 self.module = module 115 self.builtins = builtins or module 116 117 self.process_node(self.module) 118 119 # Then, process all functions and methods, providing a global namespace. 120 # By setting a global namespace, we influence the resolution of names: 121 # those which are global to the top-level module (processed above) are 122 # considered as built-in names, whereas those which are global to a 123 # function or method are searched for in the global namespace. 124 125 self.global_namespace = self.namespace 126 127 for subprogram in self.module.simplifier.subprograms: 128 129 # Internal subprograms are skipped here and processed specially via 130 # Invoke nodes. 131 132 if not getattr(subprogram, "internal", 0): 133 self.subprograms.append(self.process_node(subprogram)) 134 135 # Ultimately, we redefine the list of subprograms on the visitor. 136 137 self.module.simplifier.subprograms = self.subprograms 138 return self.module 139 140 def process_node(self, node, namespace=None): 141 142 """ 143 Process a subprogram or module 'node', discovering from attributes on 144 'node' any initial locals. Return a modified subprogram or module. 145 """ 146 147 # Do not process subprograms already being processed. 148 149 if node in self.current_subprograms: 150 return None 151 152 # Obtain a namespace either based on locals or on a structure. 153 154 structure = structure=getattr(node, "structure", None) 155 self.namespace = NameOrganiser(structure) 156 157 # Record the current subprogram and namespace. 158 159 self.current_subprograms.append(node) 160 self.current_namespaces.append(self.namespace) 161 162 # If passed some namespace, merge its contents into this namespace. 163 164 if namespace is not None: 165 self.namespace.merge_namespace(namespace) 166 167 # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a 168 # NOTE: subprogram within itself. Do not define the name of the function 169 # NOTE: within a method definition. 170 171 if isinstance(node, Subprogram) and getattr(node, "name", None) is not None and not getattr(node, "is_method", 0): 172 self.namespace.store(node.name) 173 174 # Register the names of parameters in the namespace. 175 176 if hasattr(node, "params"): 177 new_params = [] 178 for param, default in node.params: 179 new_params.append((param, self.dispatch(default))) 180 self.namespace.store(param) 181 node.params = new_params 182 if getattr(node, "star", None): 183 param, default = node.star 184 self.namespace.store(param) 185 node.star = param, self.dispatch(default) 186 if getattr(node, "dstar", None): 187 param, default = node.dstar 188 self.namespace.store(param) 189 node.dstar = param, self.dispatch(default) 190 191 # Add namespace details to any structure involved. 192 193 if hasattr(node, "structure") and node.structure is not None: 194 195 # Initialise bases where appropriate. 196 197 if hasattr(node.structure, "bases"): 198 bases = [] 199 for base in node.structure.bases: 200 bases.append(self.dispatch(base)) 201 node.structure.bases = bases 202 203 # Dispatch to the code itself. 204 205 result = self.dispatch(node) 206 result.organiser = self.namespace 207 208 # Restore the previous subprogram and namespace. 209 210 self.current_namespaces.pop() 211 if self.current_namespaces: 212 self.namespace = self.current_namespaces[-1] 213 self.current_subprograms.pop() 214 215 return result 216 217 # Visitor methods. 218 219 def default(self, node): 220 221 """ 222 Process the given 'node', given that it does not have a specific 223 handler. 224 """ 225 226 for attr in ("pos_args",): 227 value = getattr(node, attr, None) 228 if value is not None: 229 setattr(node, attr, self.dispatches(value)) 230 for attr in ("kw_args",): 231 value = getattr(node, attr, None) 232 if value is not None: 233 setattr(node, attr, self.dispatch_dict(value)) 234 for attr in ("expr", "lvalue", "test", "star", "dstar"): 235 value = getattr(node, attr, None) 236 if value is not None: 237 setattr(node, attr, self.dispatch(value)) 238 for attr in ("body", "else_", "handler", "finally_", "code", "choices"): 239 value = getattr(node, attr, None) 240 if value is not None: 241 setattr(node, attr, self.dispatches(value)) 242 return node 243 244 def dispatch(self, node, *args): 245 return Visitor.dispatch(self, node, *args) 246 247 def visitGlobal(self, global_): 248 for name in global_.names: 249 self.namespace.make_global(name) 250 return global_ 251 252 def visitLoadName(self, loadname): 253 254 "Transform the 'loadname' node to a specific, scope-sensitive node." 255 256 scope = self.namespace.find_for_load(loadname.name) 257 258 # For structure namespaces, load an attribute. 259 260 if scope == "structure": 261 result = self.dispatch( 262 LoadAttr(loadname.original, loadname.defining, 263 expr=LoadRef(loadname.original, 264 ref=self.namespace.structure), 265 name=loadname.name, 266 nstype="structure") 267 ) 268 269 # For global accesses (ie. those outside the local namespace)... 270 271 elif scope == "global": 272 273 # Where a distinct global namespace exists, examine it. 274 275 if self.global_namespace is not None: 276 scope = self.global_namespace.find_for_load(loadname.name) 277 278 # Where the name is outside the global namespace, it must be a 279 # built-in. 280 281 if scope == "global": 282 result = self.dispatch( 283 LoadAttr(loadname.original, loadname.defining, 284 expr=LoadRef(loadname.original, 285 ref=self.builtins), 286 name=loadname.name, 287 nstype="module") 288 ) 289 290 # Otherwise, it is within the global namespace and must be a 291 # global. 292 293 else: 294 result = self.dispatch( 295 LoadAttr(loadname.original, loadname.defining, 296 expr=LoadRef(loadname.original, 297 ref=self.module), 298 name=loadname.name, 299 nstype="module") 300 ) 301 302 # Where no global namespace exists, we are at the module level and 303 # must be accessing a built-in. 304 305 else: 306 result = self.dispatch( 307 LoadAttr(loadname.original, loadname.defining, 308 expr=LoadRef(loadname.original, 309 ref=self.builtins), 310 name=loadname.name, 311 nstype="module") 312 ) 313 314 # For local accesses... 315 316 else: 317 318 # Where a distinct global namespace exists, it must be a local. 319 320 if self.global_namespace is not None: 321 result = loadname 322 323 # Otherwise, we must be accessing a global (which is local at the 324 # module level). 325 326 else: 327 result = self.dispatch( 328 LoadAttr(loadname.original, loadname.defining, 329 expr=LoadRef(loadname.original, 330 ref=self.module), 331 name=loadname.name, 332 nstype="module") 333 ) 334 335 return result 336 337 def visitStoreName(self, storename): 338 339 "Transform the 'storename' node to a specific, scope-sensitive node." 340 341 scope = self.namespace.find_for_store(storename.name) 342 343 # For structure namespaces, store an attribute. 344 345 if scope == "structure": 346 self.namespace.store(storename.name) 347 348 return self.dispatch( 349 StoreAttr(storename.original, storename.defining, 350 lvalue=LoadRef(storename.original, 351 ref=self.namespace.structure), 352 name=storename.name, 353 expr=storename.expr, 354 nstype="structure") 355 ) 356 357 # Where the name is outside the local namespace, disallow any built-in 358 # assignment and store the name globally. 359 360 elif scope == "global": 361 return self.dispatch( 362 StoreAttr(storename.original, storename.defining, 363 lvalue=LoadRef(storename.original, 364 ref=self.module), 365 name=storename.name, 366 expr=storename.expr, 367 nstype="module") 368 ) 369 370 # For local namespace accesses... 371 372 else: 373 self.namespace.store(storename.name) 374 375 # If a distinct global namespace exists, it must be a local access. 376 377 if self.global_namespace is not None: 378 return storename 379 380 # Otherwise, the name is being set at the module level and is 381 # considered global. 382 383 else: 384 return self.dispatch( 385 StoreAttr(storename.original, storename.defining, 386 lvalue=LoadRef(storename.original, 387 ref=self.module), 388 name=storename.name, 389 expr=storename.expr, 390 nstype="module") 391 ) 392 393 def visitInvokeFunction(self, invoke): 394 395 "Transform the 'invoke' node, performing processing on subprograms." 396 397 return self.default(invoke) 398 399 def visitInvokeBlock(self, invoke): 400 401 "Transform the 'invoke' node, performing processing on subprograms." 402 403 # The special case of internal subprogram invocation is addressed by 404 # propagating namespace information to the subprogram and processing it. 405 406 subprogram = self.process_node(invoke.expr.ref, self.namespace) 407 if subprogram is not None: 408 self.subprograms.append(subprogram) 409 return invoke 410 411 class ScopeMismatch(Exception): 412 pass 413 414 class NameOrganiser: 415 416 """ 417 A local namespace which may either relate to a genuine set of function 418 locals or the initialisation of a structure. 419 """ 420 421 def __init__(self, structure=None): 422 423 "Initialise the namespace with an optional 'structure'." 424 425 self.structure = structure 426 if structure is not None: 427 self.local = "structure" 428 else: 429 self.local = "local" 430 431 # Names may be self.local or "global". 432 433 self.names = {} 434 435 def make_global(self, name): 436 if not self.names.has_key(name): 437 self.names[name] = "global" 438 elif self.names[name] == self.local: 439 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local) 440 441 def find_for_load(self, name): 442 return self.names.get(name, "global") 443 444 def find_for_store(self, name): 445 return self.names.get(name, self.local) 446 447 def store(self, name): 448 if self.names.get(name) != "global": 449 self.names[name] = self.local 450 else: 451 raise ScopeMismatch, "Name '%s' already considered as global." % name 452 453 def merge(self, name, scope): 454 if self.names.get(name) in (None, scope): 455 self.names[name] = scope 456 else: 457 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.names[name]) 458 459 def merge_namespace(self, namespace): 460 self.merge_items(namespace.names.items()) 461 462 def merge_items(self, items): 463 for name, scope in items: 464 self.merge(name, scope) 465 466 def __repr__(self): 467 return repr(self.names) 468 469 # Convenience functions. 470 471 def fix(module, builtins=None): 472 473 """ 474 Fix the names in the given 'module', also employing the optional 'builtins' 475 module, if specified. 476 """ 477 478 fixer = Fixer() 479 if builtins is not None: 480 fixer.process(module, builtins) 481 else: 482 fixer.process(module) 483 484 # vim: tabstop=4 expandtab shiftwidth=4