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 from simplified import * 27 28 # Fixing of name-related operations. 29 30 class Fixer(Visitor): 31 32 """ 33 The name fixer which traverses the program nodes, typically depth-first, 34 and maintains a record of name usage in the different namespaces. As a 35 consequence of various observations, some parts of the program node tree are 36 modified with different operations employed to those originally defined. 37 """ 38 39 def __init__(self): 40 41 "Initialise the name fixer." 42 43 Visitor.__init__(self) 44 45 # Satisfy visitor issues. 46 47 self.visitor = self 48 49 def process(self, visitor): 50 51 "Process the resources of the given 'visitor'." 52 53 self.subprograms = [] 54 self.current_subprograms = [] 55 self.current_namespaces = [] 56 57 # First, process the top-level code, finding out which names are 58 # defined at that level. 59 60 self.global_namespace = None 61 visitor.result = self.process_node(visitor.result) 62 63 # Then, process all functions and methods, providing a global namespace. 64 65 self.global_namespace = self.namespace 66 67 for subprogram in visitor.subprograms: 68 69 # Internal subprograms are skipped here and processed specially via 70 # Invoke nodes. 71 72 if not getattr(subprogram, "acquire_locals", 0): 73 self.subprograms.append(self.process_node(subprogram)) 74 75 visitor.subprograms = self.subprograms 76 return visitor 77 78 def process_node(self, node, namespace=None): 79 80 """ 81 Process a subprogram or module 'node', discovering from attributes on 82 'node' any initial locals. Return a modified subprogram or module. 83 """ 84 85 # Do not process subprograms already being processed. 86 87 if node in self.current_subprograms: 88 return None 89 90 # Obtain a namespace either based on locals or on a structure. 91 92 structure = structure=getattr(node, "structure", None) 93 self.namespace = NameOrganiser(structure) 94 95 # Record the current subprogram and namespace. 96 97 self.current_subprograms.append(node) 98 self.current_namespaces.append(self.namespace) 99 100 # If passed some namespace, merge its contents into this namespace. 101 102 if namespace is not None: 103 self.namespace.merge_namespace(namespace) 104 105 # NOTE: Check this. 106 107 if hasattr(node, "params"): 108 for param, default in node.params: 109 self.namespace.store(param) 110 if getattr(node, "star", None): 111 param, default = node.star 112 self.namespace.store(param) 113 if getattr(node, "dstar", None): 114 param, default = node.dstar 115 self.namespace.store(param) 116 117 # Add namespace details to any structure involved. 118 119 if hasattr(node, "structure") and node.structure is not None: 120 121 # Initialise bases where appropriate. 122 123 if hasattr(node.structure, "bases"): 124 bases = [] 125 for base in node.structure.bases: 126 bases.append(self.dispatch(base)) 127 node.structure.bases = bases 128 129 # Dispatch to the code itself. 130 131 result = self.dispatch(node) 132 133 # Restore the previous subprogram and namespace. 134 135 self.current_namespaces.pop() 136 if self.current_namespaces: 137 self.namespace = self.current_namespaces[-1] 138 self.current_subprograms.pop() 139 140 return result 141 142 # Visitor methods. 143 144 def default(self, node): 145 146 """ 147 Process the given 'node', given that it does not have a specific 148 handler. 149 """ 150 151 for attr in ("args",): 152 value = getattr(node, attr, None) 153 if value is not None: 154 setattr(node, attr, self.dispatches(value)) 155 for attr in ("expr", "lvalue", "test", "star", "dstar"): 156 value = getattr(node, attr, None) 157 if value is not None: 158 setattr(node, attr, self.dispatch(value)) 159 for attr in ("body", "else_", "handler", "finally_", "code", "choices"): 160 value = getattr(node, attr, None) 161 if value is not None: 162 setattr(node, attr, self.dispatches(value)) 163 return node 164 165 def dispatch(self, node, *args): 166 return Visitor.dispatch(self, node, *args) 167 168 def visitGlobal(self, global_): 169 for name in global_.names: 170 self.namespace.make_global(name) 171 return global_ 172 173 def visitLoadName(self, loadname): 174 175 "Transform the 'loadname' node to a specific, scope-sensitive node." 176 177 scope = self.namespace.find_for_load(loadname.name) 178 179 # For structure namespaces, load an attribute. 180 181 if scope == "structure": 182 result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.namespace.structure), name=loadname.name)) 183 184 # For global accesses (ie. those outside the local namespace)... 185 186 elif scope == "global": 187 188 # Where a distinct global namespace exists, examine it. 189 190 if self.global_namespace is not None: 191 scope = self.global_namespace.find_for_load(loadname.name) 192 193 # Where the name is outside the global namespace, it must be a 194 # built-in. 195 196 if scope == "global": 197 result = self.dispatch(LoadBuiltin(name=loadname.name)) 198 199 # Otherwise, it is within the global namespace and must be a 200 # global. 201 202 else: 203 result = self.dispatch(LoadGlobal(name=loadname.name)) 204 205 # Where no global namespace exists, we are at the module level and 206 # must be accessing a built-in. 207 208 else: 209 result = self.dispatch(LoadBuiltin(name=loadname.name)) 210 211 # For local accesses... 212 213 else: 214 215 # Where a distinct global namespace exists, it must be a local. 216 217 if self.global_namespace is not None: 218 result = loadname 219 220 # Otherwise, we must be accessing a global (which is local at the 221 # module level). 222 223 else: 224 result = self.dispatch(LoadGlobal(name=loadname.name)) 225 226 return result 227 228 def visitStoreName(self, storename): 229 230 "Transform the 'storename' node to a specific, scope-sensitive node." 231 232 scope = self.namespace.find_for_store(storename.name) 233 234 # For structure namespaces, store an attribute. 235 236 if scope == "structure": 237 return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.namespace.structure), name=storename.name, expr=storename.expr)) 238 239 # Where the name is outside the local namespace, disallow any built-in 240 # assignment and store the name globally. 241 242 elif scope == "global": 243 return self.dispatch(StoreGlobal(name=storename.name, expr=storename.expr)) 244 245 # For local namespace accesses... 246 247 else: 248 self.namespace.store(storename.name) 249 250 # If a distinct global namespace exists, it must be a local access. 251 252 if self.global_namespace is not None: 253 return storename 254 255 # Otherwise, the name is being set at the module level and is 256 # considered global. 257 258 else: 259 return self.dispatch(StoreGlobal(name=storename.name, expr=storename.expr)) 260 261 def visitInvoke(self, invoke): 262 263 "Transform the 'invoke' node, performing processing on subprograms." 264 265 # The special case of internal subprogram invocation is addressed by 266 # propagating namespace information to the subprogram and processing it. 267 268 if getattr(invoke, "same_frame", 0): 269 subprogram = self.process_node(invoke.expr.ref, self.namespace) 270 if subprogram is not None: 271 self.subprograms.append(subprogram) 272 return invoke 273 else: 274 return self.default(invoke) 275 276 class NameOrganiser: 277 278 """ 279 A local namespace which may either relate to a genuine set of function 280 locals or the initialisation of a structure. 281 """ 282 283 def __init__(self, structure=None): 284 self.structure = structure 285 if structure is not None: 286 self.local = "structure" 287 else: 288 self.local = "local" 289 self.names = {} 290 self.not_local = [] 291 292 def make_global(self, name): 293 if name not in self.not_local: 294 self.not_local.append(name) 295 296 def find_for_store(self, name): 297 if name not in self.not_local: 298 return self.local 299 else: 300 return "global" 301 302 def find_for_load(self, name): 303 if name not in self.not_local and self.names.has_key(name): 304 return self.local 305 else: 306 return "global" 307 308 def store(self, name): 309 if name not in self.not_local: 310 self.names[name] = None 311 else: 312 raise KeyError, name 313 314 def load(self, name): 315 if name in self.not_local or not self.names.has_key(name): 316 raise KeyError, name 317 else: 318 return self.names[name] 319 320 def merge(self, name): 321 if not self.names.has_key(name): 322 self.names[name] = None 323 324 def merge_namespace(self, namespace): 325 self.merge_names(namespace.names.keys()) 326 327 def merge_names(self, names): 328 for name in names: 329 self.merge(name) 330 331 def __repr__(self): 332 return repr(self.names) 333 334 # vim: tabstop=4 expandtab shiftwidth=4