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