1 #!/usr/bin/env python 2 3 """ 4 Simplified program utilities. 5 6 Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from compiler.visitor import ASTVisitor 23 24 # Exceptions. 25 26 class SimplifiedError(Exception): 27 28 "An error in the annotation process." 29 30 def __init__(self, exc, node, *args): 31 32 """ 33 Initialise the error with an existing exception 'exc', the 'node' at 34 which this error occurs, along with additional optional arguments. 35 """ 36 37 Exception.__init__(self, *args) 38 self.nodes = [node] 39 self.exc = exc 40 41 def add(self, node): 42 43 "Add the given 'node' to the path of nodes leading from the exception." 44 45 self.nodes.append(node) 46 47 def __str__(self): 48 49 "Return a string showing the principal exception details." 50 51 return "%s, %s" % (self.exc, self.nodes) 52 53 # Elementary visitor support. 54 55 class Visitor(ASTVisitor): 56 57 "A visitor base class." 58 59 def __init__(self): 60 ASTVisitor.__init__(self) 61 62 def default(self, node, *args): 63 raise SimplifiedError, (None, node) 64 65 def dispatch(self, node, *args): 66 return ASTVisitor.dispatch(self, node, *args) 67 68 def dispatches(self, nodes, *args): 69 results = [] 70 for node in nodes: 71 results.append(self.dispatch(node, *args)) 72 return results 73 74 def dispatch_dict(self, d, *args): 75 results = {} 76 for name, node in d.items(): 77 results[name] = self.dispatch(node, *args) 78 return results 79 80 # Unique name registration. 81 82 class Naming: 83 84 "Maintain records of unique names for each simple name." 85 86 index_separator = "-" 87 88 def __init__(self): 89 self.names = {} 90 91 def get(self, obj): 92 return obj._unique_name 93 94 def set(self, obj, name): 95 if hasattr(obj, "_unique_name"): 96 return 97 if not self.names.has_key(name): 98 self.names[name] = 0 99 n = self.names[name] + 1 100 self.names[name] = n 101 obj._unique_name = "%s%s%d" % (name, self.index_separator, n) 102 103 def name(obj, name): 104 105 "Return a unique name for the given 'obj', indicating the base 'name'." 106 107 naming.set(obj, name) 108 return naming.get(obj) 109 110 # Naming singleton. 111 112 naming = Naming() 113 114 # Named nodes are those which can be referenced in some way. 115 116 class WithName: 117 118 "Node naming." 119 120 def __init__(self): 121 122 "Initialise the object's full name." 123 124 self._full_name = name(self, self.name or "$untitled") 125 126 def full_name(self): 127 128 "Return the object's full name." 129 130 return self._full_name 131 132 def fully_qualified_name(self): 133 134 """ 135 Return a fully-qualified name including module and structure components. 136 """ 137 138 module = self.module.name 139 name = self.name 140 if hasattr(self, "structures"): 141 structures = [x.name for x in self.structures] 142 else: 143 structures = [] 144 return ".".join([module] + structures + [name]) 145 146 # Comparable nodes based on naming. 147 148 class Comparable: 149 150 "Comparable nodes implementing the 'full_name' method." 151 152 def __eq__(self, other): 153 154 "This object is equal to 'other' if the full names are the same." 155 156 # NOTE: Single instance: all instances are the same 157 # NOTE: Multiple instances: all instances are different 158 if hasattr(other, "full_name"): 159 return self.full_name() == other.full_name() 160 else: 161 return NotImplemented 162 163 def __hash__(self): 164 165 "The hash of this object is based on its full name." 166 167 return hash(self.full_name()) 168 169 # Structure nodes indicating namespace-bearing objects. 170 171 class Structure(Comparable): 172 173 "A non-program node containing some kind of namespace." 174 175 def __init__(self, **kw): 176 for name, value in kw.items(): 177 setattr(self, name, value) 178 179 def __repr__(self): 180 return "%s '%s'" % (self.__class__.__name__, self.full_name()) 181 182 # Namespace classes. 183 184 class Namespace: 185 186 """ 187 A local namespace which may either relate to a genuine set of function 188 locals or the initialisation of a structure or module. 189 """ 190 191 def __init__(self): 192 193 """ 194 Initialise the namespace with a mapping of local names to possible 195 types, a list of return values and of possible returned local 196 namespaces. The namespace also tracks the "current" types and a mapping 197 of temporary value names to types. 198 """ 199 200 self.names = {} 201 self.returns = set() 202 self.return_locals = set() 203 self.raises = set() 204 self.temp = {} 205 self.types = set() 206 207 def set_types(self, types): 208 209 "Set the current collection of 'types'." 210 211 self.types = types.copy() 212 213 def add(self, name, types): 214 215 "Add to the entry with the given 'name' the specified 'types'." 216 217 if self.names.has_key(name): 218 self.names[name].update(types) 219 else: 220 self.store(name, types) 221 222 def store(self, name, types): 223 224 "Store in (or associate with) the given 'name' the specified 'types'." 225 226 self.names[name] = types.copy() 227 228 __setitem__ = store 229 230 def load(self, name): 231 232 "Load the types associated with the given 'name'." 233 234 return self.names[name] 235 236 __getitem__ = load 237 238 def has_key(self, name): 239 return self.names.has_key(name) 240 241 def keys(self): 242 return self.names.keys() 243 244 def values(self): 245 return self.names.values() 246 247 def items(self): 248 return self.names.items() 249 250 def get(self, name, default=None): 251 return self.names.get(name, default) 252 253 def revoke(self, name, type): 254 255 "Revoke from the entry for the given 'name' the specified 'type'." 256 257 new_types = self.names[name].copy() 258 new_types.remove(type) 259 self.names[name] = new_types 260 261 def revoke_exception_type(self, type): 262 263 "Revoke the given 'type' from the collection of exception types." 264 265 if type in self.raises: 266 self.raises.remove(type) 267 268 def revoke_temp_type(self, index, type): 269 270 "Revoke from the temporary variable 'index' the given 'type'." 271 272 new_types = self.temp[index][-1].copy() 273 new_types.remove(type) 274 self.temp[index][-1] = new_types 275 276 def merge_namespace(self, namespace, everything=1, temp=1): 277 278 """ 279 Merge items from the given 'namespace' with this namespace. When the 280 optional 'everything' parameter is set to a false value (unlike the 281 default), return values and locals snapshots will not be copied to this 282 namespace. 283 """ 284 285 self.merge_items(namespace.names.items()) 286 self.raises.update(namespace.raises) 287 if everything: 288 self.returns.update(namespace.returns) 289 self.return_locals.update(namespace.return_locals) 290 if temp: 291 for name, values in namespace.temp.items(): 292 if values: 293 if not self.temp.has_key(name) or not self.temp[name]: 294 self.temp[name] = [set()] 295 self.temp[name][-1].update(values[-1]) 296 297 def merge_items(self, items): 298 299 "Merge the given 'items' with this namespace." 300 301 for name, types in items: 302 self.merge(name, types) 303 304 def merge(self, name, types): 305 306 "Merge the entry for the given 'name' and 'types' with this namespace." 307 308 if not self.names.has_key(name): 309 self.names[name] = types.copy() 310 else: 311 existing = self.names[name] 312 existing.update(types) 313 314 def snapshot(self): 315 316 "Make a snapshot of the locals and remember them." 317 318 namespace = Namespace() 319 namespace.merge_namespace(self) 320 self.return_locals.add(namespace) 321 322 def reset(self): 323 324 "Reset a namespace in preparation for merging with returned locals." 325 326 self.names = {} 327 328 def __repr__(self): 329 return repr(self.names) + " (temp) " + repr(self.temp) 330 331 # vim: tabstop=4 expandtab shiftwidth=4