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 # Comparable nodes based on naming. 133 134 class Comparable: 135 136 "Comparable nodes implementing the 'full_name' method." 137 138 def __eq__(self, other): 139 140 "This object is equal to 'other' if the full names are the same." 141 142 # NOTE: Single instance: all instances are the same 143 # NOTE: Multiple instances: all instances are different 144 if hasattr(other, "full_name"): 145 return self.full_name() == other.full_name() 146 else: 147 return NotImplemented 148 149 def __hash__(self): 150 151 "The hash of this object is based on its full name." 152 153 return hash(self.full_name()) 154 155 # Structure nodes indicating namespace-bearing objects. 156 157 class Structure(Comparable): 158 159 "A non-program node containing some kind of namespace." 160 161 def __init__(self, **kw): 162 for name, value in kw.items(): 163 setattr(self, name, value) 164 165 def __repr__(self): 166 return "%s '%s'" % (self.__class__.__name__, self.full_name()) 167 168 # Namespace classes. 169 170 class Namespace: 171 172 """ 173 A local namespace which may either relate to a genuine set of function 174 locals or the initialisation of a structure or module. 175 """ 176 177 def __init__(self): 178 179 """ 180 Initialise the namespace with a mapping of local names to possible 181 types, a list of return values and of possible returned local 182 namespaces. The namespace also tracks the "current" types and a mapping 183 of temporary value names to types. 184 """ 185 186 self.names = {} 187 self.returns = set() 188 self.return_locals = set() 189 self.raises = set() 190 self.temp = {} 191 self.types = set() 192 193 def set_types(self, types): 194 195 "Set the current collection of 'types'." 196 197 self.types = types.copy() 198 199 def add(self, name, types): 200 201 "Add to the entry with the given 'name' the specified 'types'." 202 203 if self.names.has_key(name): 204 self.names[name].update(types) 205 else: 206 self.store(name, types) 207 208 def store(self, name, types): 209 210 "Store in (or associate with) the given 'name' the specified 'types'." 211 212 self.names[name] = types.copy() 213 214 __setitem__ = store 215 216 def load(self, name): 217 218 "Load the types associated with the given 'name'." 219 220 return self.names[name] 221 222 __getitem__ = load 223 224 def has_key(self, name): 225 return self.names.has_key(name) 226 227 def keys(self): 228 return self.names.keys() 229 230 def values(self): 231 return self.names.values() 232 233 def items(self): 234 return self.names.items() 235 236 def get(self, name, default=None): 237 return self.names.get(name, default) 238 239 def revoke(self, name, type): 240 241 "Revoke from the entry for the given 'name' the specified 'type'." 242 243 new_types = self.names[name].copy() 244 new_types.remove(type) 245 self.names[name] = new_types 246 247 def revoke_exception_type(self, type): 248 249 "Revoke the given 'type' from the collection of exception types." 250 251 if type in self.raises: 252 self.raises.remove(type) 253 254 def revoke_temp_type(self, index, type): 255 256 "Revoke from the temporary variable 'index' the given 'type'." 257 258 new_types = self.temp[index][-1].copy() 259 new_types.remove(type) 260 self.temp[index][-1] = new_types 261 262 def merge_namespace(self, namespace, everything=1, temp=1): 263 264 """ 265 Merge items from the given 'namespace' with this namespace. When the 266 optional 'everything' parameter is set to a false value (unlike the 267 default), return values and locals snapshots will not be copied to this 268 namespace. 269 """ 270 271 self.merge_items(namespace.names.items()) 272 self.raises.update(namespace.raises) 273 if everything: 274 self.returns.update(namespace.returns) 275 self.return_locals.update(namespace.return_locals) 276 if temp: 277 for name, values in namespace.temp.items(): 278 if values: 279 if not self.temp.has_key(name) or not self.temp[name]: 280 self.temp[name] = [set()] 281 self.temp[name][-1].update(values[-1]) 282 283 def merge_items(self, items): 284 285 "Merge the given 'items' with this namespace." 286 287 for name, types in items: 288 self.merge(name, types) 289 290 def merge(self, name, types): 291 292 "Merge the entry for the given 'name' and 'types' with this namespace." 293 294 if not self.names.has_key(name): 295 self.names[name] = types.copy() 296 else: 297 existing = self.names[name] 298 existing.update(types) 299 300 def snapshot(self): 301 302 "Make a snapshot of the locals and remember them." 303 304 namespace = Namespace() 305 namespace.merge_namespace(self) 306 self.return_locals.add(namespace) 307 308 def reset(self): 309 310 "Reset a namespace in preparation for merging with returned locals." 311 312 self.names = {} 313 314 def __repr__(self): 315 return repr(self.names) + " (temp) " + repr(self.temp) 316 317 # vim: tabstop=4 expandtab shiftwidth=4