1 #!/usr/bin/env python 2 3 """ 4 Common classes. 5 6 Copyright (C) 2007, 2008, 2009, 2010 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 import compiler.visitor 23 24 try: 25 set 26 except NameError: 27 from sets import Set as set 28 29 def merge_set_dicts(dicts): 30 31 "Merge the given 'dicts' mapping keys to sets of objects." 32 33 new_dict = {} 34 update_set_dict(new_dict, dicts) 35 return new_dict 36 37 def update_set_dict(new_dict, dicts): 38 39 "Update 'new_dict' with the contents of the set dictionary 'dicts'." 40 41 for old_dict in dicts: 42 for key, values in old_dict.items(): 43 if not new_dict.has_key(key): 44 new_dict[key] = set(values) 45 else: 46 new_dict[key].update(values) 47 48 def deepen_set_dict(d): 49 50 """ 51 Return a new dictionary mapping keys to sets of tuples of values, using data 52 from 'd' which is a dictionary mapping keys to sets of values. 53 """ 54 55 d2 = {} 56 for key, values in d.items(): 57 d2[key] = set([tuple(values)]) 58 return d2 59 60 def combine_set_dicts(d1, d2): 61 62 """ 63 Combine dictionaries 'd1' and 'd2' in a resulting dictionary, with the set 64 values of the contributing dictionaries being merged such that a "product" 65 of the values for a given key are stored in the combined dictionary. 66 """ 67 68 combined = {} 69 d2_keys = d2.keys() 70 71 for key in d2_keys: 72 73 d1_values = d1.get(key) 74 75 if d1_values is None: 76 combined[key] = d2[key] 77 else: 78 combined[key] = set() 79 for d2_value in d2[key]: 80 for d1_value in d1_values: 81 combined[key].add(tuple(set(d1_value).union(d2_value))) 82 83 for key in d1.keys(): 84 if key not in d2_keys: 85 combined[key] = d1[key] 86 87 return combined 88 89 # Visitors and activities related to node annotations. 90 91 class ASTVisitor(compiler.visitor.ASTVisitor): 92 93 "A base class for visitors." 94 95 def dispatch(self, node, *args): 96 97 "Dispatch using 'node', annotating any raised exceptions." 98 99 try: 100 return compiler.visitor.ASTVisitor.dispatch(self, node, *args) 101 except NodeProcessingError, exc: 102 if exc.astnode is None: 103 exc.astnode = node 104 105 # NOTE: Should perhaps specialise the subclasses appropriately. 106 107 if hasattr(self, "unit"): 108 exc.unit_name = self.unit.full_name() 109 else: 110 exc.unit_name = self.full_name() 111 raise 112 113 def possible_accessor_types(self, node): 114 115 """ 116 Given annotations made during the inspection process, return all possible 117 types for a 'node' involved in attribute access, or return None if no 118 annotations are available. 119 """ 120 121 if hasattr(node, "_attrusers"): 122 target_names = set() 123 124 # Visit each attribute user. 125 126 for user in node._attrusers: 127 128 # Since users such as branches may not provide type information, 129 # attempt to find defining users. 130 131 for def_user in user._attrdefs or [user]: 132 for target_name, is_static in def_user._attrtypes.get(node._username, []): 133 target_names.add(target_name) 134 135 return target_names 136 137 else: 138 return None 139 140 def used_by_unit(node): 141 142 """ 143 Return whether the definition made by a 'node' is actually employed by the 144 program unit within which it is found. 145 """ 146 147 return hasattr(node, "unit") and node.unit.parent.has_key(node.unit.name) 148 149 # Errors. 150 151 class ProcessingError(Exception): 152 153 "A processing error." 154 155 pass 156 157 class TableError(ProcessingError): 158 159 "An error occurring during access to a lookup table." 160 161 pass 162 163 class TableGenerationError(ProcessingError): 164 165 "An error occurring when generating a lookup table." 166 167 pass 168 169 class NodeProcessingError(ProcessingError): 170 171 "A processing error associated with a particular program node." 172 173 def __init__(self, message): 174 self.message = message 175 self.unit_name = None 176 self.astnode = None 177 178 def get_lineno(self, node): 179 lineno = node.lineno 180 if lineno is not None: 181 return lineno 182 else: 183 for child in node.getChildNodes(): 184 lineno = self.get_lineno(child) 185 if lineno is not None: 186 return lineno 187 return None 188 189 def __repr__(self): 190 return "Error in %r at line %r: %s" % (self.unit_name, self.get_lineno(self.astnode), self.message) 191 192 def __str__(self): 193 return repr(self) 194 195 class InspectError(NodeProcessingError): 196 197 "An error during the module inspection process." 198 199 pass 200 201 class TranslateError(NodeProcessingError): 202 203 "An error during the module translation process." 204 205 pass 206 207 class TranslationNotImplementedError(TranslateError): 208 209 "An error caused by a node not being supported in translation." 210 211 pass 212 213 # Special representations. 214 215 class AtLeast: 216 217 "A special representation for numbers of a given value or greater." 218 219 def __init__(self, count): 220 self.count = count 221 222 def __eq__(self, other): 223 return 0 224 225 __lt__ = __le__ = __eq__ 226 227 def __ne__(self, other): 228 return 1 229 230 def __gt__(self, other): 231 if isinstance(other, AtLeast): 232 return 0 233 else: 234 return self.count > other 235 236 def __ge__(self, other): 237 if isinstance(other, AtLeast): 238 return 0 239 else: 240 return self.count >= other 241 242 def __iadd__(self, other): 243 if isinstance(other, AtLeast): 244 self.count += other.count 245 else: 246 self.count += other 247 return self 248 249 def __radd__(self, other): 250 if isinstance(other, AtLeast): 251 return AtLeast(self.count + other.count) 252 else: 253 return AtLeast(self.count + other) 254 255 def __repr__(self): 256 return "AtLeast(%r)" % self.count 257 258 # Useful data. 259 260 comparison_methods = { 261 "==" : ("__eq__", "__eq__"), 262 "!=" : ("__ne__", "__ne__"), 263 "<" : ("__lt__", "__gt__"), 264 "<=" : ("__le__", "__ge__"), 265 ">=" : ("__ge__", "__le__"), 266 ">" : ("__gt__", "__lt__"), 267 "is" : None, 268 "is not" : None, 269 "in" : None, 270 "not in" : None 271 } 272 273 augassign_methods = { 274 "+=" : ("__iadd__", ("__add__", "__radd__")), 275 "-=" : ("__isub__", ("__sub__", "__rsub__")), 276 "*=" : ("__imul__", ("__mul__", "__rmul__")), 277 "/=" : ("__idiv__", ("__div__", "__rdiv__")), 278 "//=" : ("__ifloordiv__", ("__floordiv__", "__rfloordiv__")), 279 "%=" : ("__imod__", ("__mod__", "__rmod__")), 280 "**=" : ("__ipow__", ("__pow__", "__rpow__")), 281 "<<=" : ("__ilshift__", ("__lshift__", "__rlshift__")), 282 ">>=" : ("__irshift__", ("__rshift__", "__rrshift__")), 283 "&=" : ("__iand__", ("__and__", "__rand__")), 284 "^=" : ("__ixor__", ("__xor__", "__rxor__")), 285 "|=" : ("__ior__", ("__or__", "__ror__")) 286 } 287 288 binary_methods = { 289 "Add" : ("__add__", "__radd__"), 290 "Bitand" : ("__and__", "__rand__"), 291 "Bitor" : ("__or__", "__ror__"), 292 "Bitxor" : ("__xor__", "__rxor__"), 293 "Div" : ("__div__", "__rdiv__"), 294 "FloorDiv" : ("__floordiv__", "__rfloordiv__"), 295 "LeftShift" : ("__lshift__", "__rlshift__"), 296 "Mod" : ("__mod__", "__rmod__"), 297 "Mul" : ("__mul__", "__rmul__"), 298 "Power" : ("__pow__", "__rpow__"), 299 "RightShift" : ("__rshift__", "__rrshift__"), 300 "Sub" : ("__sub__", "__rsub__") 301 } 302 303 unary_methods = { 304 "Invert" : "__invert__", 305 "UnaryAdd" : "__pos__", 306 "UnarySub" : "__neg__" 307 } 308 309 operator_functions = { 310 311 # Binary operations. 312 313 "Add" : "add", 314 "Bitand" : "and_", 315 "Bitor" : "or_", 316 "Bitxor" : "xor", 317 "Div" : "div", 318 "FloorDiv" : "floordiv", 319 "LeftShift" : "lshift", 320 "Mod" : "mod", 321 "Mul" : "mul", 322 "Power" : "pow", 323 "RightShift" : "rshift", 324 "Sub" : "sub", 325 326 # Unary operations. 327 328 "Invert" : "invert", 329 "UnaryAdd" : "pos", 330 "UnarySub" : "neg", 331 332 # Augmented assignment. 333 334 "+=" : "iadd", 335 "-=" : "isub", 336 "*=" : "imul", 337 "/=" : "idiv", 338 "//=" : "ifloordiv", 339 "%=" : "imod", 340 "**=" : "ipow", 341 "<<=" : "ilshift", 342 ">>=" : "irshift", 343 "&=" : "iand", 344 "^=" : "ixor", 345 "|=" : "ior", 346 347 # Comparisons. 348 349 "==" : "eq", 350 "!=" : "ne", 351 "<" : "lt", 352 "<=" : "le", 353 ">=" : "ge", 354 ">" : "gt", 355 356 # Access and slicing. 357 358 "AssSlice" : "setslice", 359 "Slice" : "getslice", 360 "AssSubscript" : "setitem", 361 "Subscript" : "getitem", 362 } 363 364 # vim: tabstop=4 expandtab shiftwidth=4