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 # Register the names of parameters in the namespace. 168 169 if hasattr(node, "params"): 170 new_params = [] 171 for param, default in node.params: 172 new_params.append((param, self.dispatch(default))) 173 self.namespace.store(param) 174 node.params = new_params 175 if getattr(node, "star", None): 176 param, default = node.star 177 self.namespace.store(param) 178 node.star = param, self.dispatch(default) 179 if getattr(node, "dstar", None): 180 param, default = node.dstar 181 self.namespace.store(param) 182 node.dstar = param, self.dispatch(default) 183 184 # Add namespace details to any structure involved. 185 186 if hasattr(node, "structure") and node.structure is not None: 187 188 # Initialise bases where appropriate. 189 190 if hasattr(node.structure, "bases"): 191 bases = [] 192 for base in node.structure.bases: 193 bases.append(self.dispatch(base)) 194 node.structure.bases = bases 195 196 # Dispatch to the code itself. 197 198 result = self.dispatch(node) 199 result.organiser = self.namespace 200 201 # Restore the previous subprogram and namespace. 202 203 self.current_namespaces.pop() 204 if self.current_namespaces: 205 self.namespace = self.current_namespaces[-1] 206 self.current_subprograms.pop() 207 208 return result 209 210 # Visitor methods. 211 212 def default(self, node): 213 214 """ 215 Process the given 'node', given that it does not have a specific 216 handler. 217 """ 218 219 for attr in ("pos_args",): 220 value = getattr(node, attr, None) 221 if value is not None: 222 setattr(node, attr, self.dispatches(value)) 223 for attr in ("kw_args",): 224 value = getattr(node, attr, None) 225 if value is not None: 226 setattr(node, attr, self.dispatch_dict(value)) 227 for attr in ("expr", "lvalue", "test", "star", "dstar"): 228 value = getattr(node, attr, None) 229 if value is not None: 230 setattr(node, attr, self.dispatch(value)) 231 for attr in ("body", "else_", "handler", "finally_", "code", "choices"): 232 value = getattr(node, attr, None) 233 if value is not None: 234 setattr(node, attr, self.dispatches(value)) 235 return node 236 237 def dispatch(self, node, *args): 238 return Visitor.dispatch(self, node, *args) 239 240 def visitGlobal(self, global_): 241 for name in global_.names: 242 self.namespace.make_global(name) 243 return global_ 244 245 def visitLoadName(self, loadname): 246 247 "Transform the 'loadname' node to a specific, scope-sensitive node." 248 249 scope = self.namespace.find_for_load(loadname.name) 250 251 # For structure namespaces, load an attribute. 252 253 if scope == "structure": 254 result = self.dispatch( 255 LoadAttr(loadname.original, loadname.defining, 256 expr=LoadRef(loadname.original, 257 ref=self.namespace.structure), 258 name=loadname.name, 259 nstype="structure") 260 ) 261 262 # For global accesses (ie. those outside the local namespace)... 263 264 elif scope == "global": 265 266 # Where a distinct global namespace exists, examine it. 267 268 if self.global_namespace is not None: 269 scope = self.global_namespace.find_for_load(loadname.name) 270 271 # Where the name is outside the global namespace, it must be a 272 # built-in. 273 274 if scope == "global": 275 result = self.dispatch( 276 LoadAttr(loadname.original, loadname.defining, 277 expr=LoadRef(loadname.original, 278 ref=self.builtins), 279 name=loadname.name, 280 nstype="module") 281 ) 282 283 # Otherwise, it is within the global namespace and must be a 284 # global. 285 286 else: 287 result = self.dispatch( 288 LoadAttr(loadname.original, loadname.defining, 289 expr=LoadRef(loadname.original, 290 ref=self.module), 291 name=loadname.name, 292 nstype="module") 293 ) 294 295 # Where no global namespace exists, we are at the module level and 296 # must be accessing a built-in. 297 298 else: 299 result = self.dispatch( 300 LoadAttr(loadname.original, loadname.defining, 301 expr=LoadRef(loadname.original, 302 ref=self.builtins), 303 name=loadname.name, 304 nstype="module") 305 ) 306 307 # For local accesses... 308 309 else: 310 311 # Where a distinct global namespace exists, it must be a local. 312 313 if self.global_namespace is not None: 314 result = loadname 315 316 # Otherwise, we must be accessing a global (which is local at the 317 # module level). 318 319 else: 320 result = self.dispatch( 321 LoadAttr(loadname.original, loadname.defining, 322 expr=LoadRef(loadname.original, 323 ref=self.module), 324 name=loadname.name, 325 nstype="module") 326 ) 327 328 return result 329 330 def visitStoreName(self, storename): 331 332 "Transform the 'storename' node to a specific, scope-sensitive node." 333 334 scope = self.namespace.find_for_store(storename.name) 335 336 # For structure namespaces, store an attribute. 337 338 if scope == "structure": 339 self.namespace.store(storename.name) 340 341 return self.dispatch( 342 StoreAttr(storename.original, storename.defining, 343 lvalue=LoadRef(storename.original, 344 ref=self.namespace.structure), 345 name=storename.name, 346 expr=storename.expr, 347 nstype="structure") 348 ) 349 350 # Where the name is outside the local namespace, disallow any built-in 351 # assignment and store the name globally. 352 353 elif scope == "global": 354 return self.dispatch( 355 StoreAttr(storename.original, storename.defining, 356 lvalue=LoadRef(storename.original, 357 ref=self.module), 358 name=storename.name, 359 expr=storename.expr, 360 nstype="module") 361 ) 362 363 # For local namespace accesses... 364 365 else: 366 self.namespace.store(storename.name) 367 368 # If a distinct global namespace exists, it must be a local access. 369 370 if self.global_namespace is not None: 371 return storename 372 373 # Otherwise, the name is being set at the module level and is 374 # considered global. 375 376 else: 377 return self.dispatch( 378 StoreAttr(storename.original, storename.defining, 379 lvalue=LoadRef(storename.original, 380 ref=self.module), 381 name=storename.name, 382 expr=storename.expr, 383 nstype="module") 384 ) 385 386 def visitInvokeFunction(self, invoke): 387 388 "Transform the 'invoke' node, performing processing on subprograms." 389 390 return self.default(invoke) 391 392 def visitInvokeBlock(self, invoke): 393 394 "Transform the 'invoke' node, performing processing on subprograms." 395 396 # The special case of internal subprogram invocation is addressed by 397 # propagating namespace information to the subprogram and processing it. 398 399 subprogram = self.process_node(invoke.expr.ref, self.namespace) 400 if subprogram is not None: 401 self.subprograms.append(subprogram) 402 return invoke 403 404 class ScopeMismatch(Exception): 405 pass 406 407 class NameOrganiser: 408 409 """ 410 A local namespace which may either relate to a genuine set of function 411 locals or the initialisation of a structure. 412 """ 413 414 def __init__(self, structure=None): 415 416 "Initialise the namespace with an optional 'structure'." 417 418 self.structure = structure 419 if structure is not None: 420 self.local = "structure" 421 else: 422 self.local = "local" 423 424 # Names may be self.local or "global". 425 426 self.names = {} 427 428 def make_global(self, name): 429 if not self.names.has_key(name): 430 self.names[name] = "global" 431 elif self.names[name] == self.local: 432 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local) 433 434 def find_for_load(self, name): 435 return self.names.get(name, "global") 436 437 def find_for_store(self, name): 438 return self.names.get(name, self.local) 439 440 def store(self, name): 441 if self.names.get(name) != "global": 442 self.names[name] = self.local 443 else: 444 raise ScopeMismatch, "Name '%s' already considered as global." % name 445 446 def merge(self, name, scope): 447 if self.names.get(name) in (None, scope): 448 self.names[name] = scope 449 else: 450 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.names[name]) 451 452 def merge_namespace(self, namespace): 453 self.merge_items(namespace.names.items()) 454 455 def merge_items(self, items): 456 for name, scope in items: 457 self.merge(name, scope) 458 459 def __repr__(self): 460 return repr(self.names) 461 462 # Convenience functions. 463 464 def fix(module, builtins=None): 465 466 """ 467 Fix the names in the given 'module', also employing the optional 'builtins' 468 module, if specified. 469 """ 470 471 fixer = Fixer() 472 if builtins is not None: 473 fixer.process(module, builtins) 474 else: 475 fixer.process(module) 476 477 # vim: tabstop=4 expandtab shiftwidth=4