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