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