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 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 return self.dispatch( 340 StoreAttr(storename.original, storename.defining, 341 lvalue=LoadRef(storename.original, 342 ref=self.namespace.structure), 343 name=storename.name, 344 expr=storename.expr, 345 nstype="structure") 346 ) 347 348 # Where the name is outside the local namespace, disallow any built-in 349 # assignment and store the name globally. 350 351 elif scope == "global": 352 return self.dispatch( 353 StoreAttr(storename.original, storename.defining, 354 lvalue=LoadRef(storename.original, 355 ref=self.module), 356 name=storename.name, 357 expr=storename.expr, 358 nstype="module") 359 ) 360 361 # For local namespace accesses... 362 363 else: 364 self.namespace.store(storename.name) 365 366 # If a distinct global namespace exists, it must be a local access. 367 368 if self.global_namespace is not None: 369 return storename 370 371 # Otherwise, the name is being set at the module level and is 372 # considered global. 373 374 else: 375 return self.dispatch( 376 StoreAttr(storename.original, storename.defining, 377 lvalue=LoadRef(storename.original, 378 ref=self.module), 379 name=storename.name, 380 expr=storename.expr, 381 nstype="module") 382 ) 383 384 def visitInvokeFunction(self, invoke): 385 386 "Transform the 'invoke' node, performing processing on subprograms." 387 388 return self.default(invoke) 389 390 def visitInvokeBlock(self, invoke): 391 392 "Transform the 'invoke' node, performing processing on subprograms." 393 394 # The special case of internal subprogram invocation is addressed by 395 # propagating namespace information to the subprogram and processing it. 396 397 subprogram = self.process_node(invoke.expr.ref, self.namespace) 398 if subprogram is not None: 399 self.subprograms.append(subprogram) 400 return invoke 401 402 class ScopeMismatch(Exception): 403 pass 404 405 class NameOrganiser: 406 407 """ 408 A local namespace which may either relate to a genuine set of function 409 locals or the initialisation of a structure. 410 """ 411 412 def __init__(self, structure=None): 413 414 "Initialise the namespace with an optional 'structure'." 415 416 self.structure = structure 417 if structure is not None: 418 self.local = "structure" 419 else: 420 self.local = "local" 421 422 # Names may be self.local or "global". 423 424 self.names = {} 425 426 def make_global(self, name): 427 if not self.names.has_key(name): 428 self.names[name] = "global" 429 elif self.names[name] == self.local: 430 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local) 431 432 def find_for_load(self, name): 433 return self.names.get(name, "global") 434 435 def find_for_store(self, name): 436 return self.names.get(name, self.local) 437 438 def store(self, name): 439 if self.names.get(name) != "global": 440 self.names[name] = self.local 441 else: 442 raise ScopeMismatch, "Name '%s' already considered as global." % name 443 444 def merge(self, name, scope): 445 if self.names.get(name) in (None, scope): 446 self.names[name] = scope 447 else: 448 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.names[name]) 449 450 def merge_namespace(self, namespace): 451 self.merge_items(namespace.names.items()) 452 453 def merge_items(self, items): 454 for name, scope in items: 455 self.merge(name, scope) 456 457 def __repr__(self): 458 return repr(self.names) 459 460 # Convenience functions. 461 462 def fix(module, builtins=None): 463 464 """ 465 Fix the names in the given 'module', also employing the optional 'builtins' 466 module, if specified. 467 """ 468 469 fixer = Fixer() 470 if builtins is not None: 471 fixer.process(module, builtins) 472 else: 473 fixer.process(module) 474 475 # vim: tabstop=4 expandtab shiftwidth=4