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 type names and indications of static usage for a 'node' involved in 118 attribute access. 119 """ 120 121 target_names = set() 122 123 if hasattr(node, "_attrusers"): 124 125 # Visit each attribute user. 126 127 for user in node._attrusers: 128 129 # Since users such as branches may not provide type information, 130 # attempt to find defining users. 131 132 for def_user in user._attrdefs or [user]: 133 for target_name, is_static in def_user._attrtypes.get(node._username, []): 134 target_names.add((target_name, is_static)) 135 136 return target_names 137 138 def used_by_unit(node): 139 140 """ 141 Return whether the definition made by a 'node' is actually employed by the 142 program unit within which it is found. 143 """ 144 145 return hasattr(node, "unit") and node.unit.parent.has_key(node.unit.name) 146 147 # Errors. 148 149 class ProcessingError(Exception): 150 151 "A processing error." 152 153 pass 154 155 class TableError(ProcessingError): 156 157 "An error occurring during access to a lookup table." 158 159 pass 160 161 class TableGenerationError(ProcessingError): 162 163 "An error occurring when generating a lookup table." 164 165 pass 166 167 class NodeProcessingError(ProcessingError): 168 169 "A processing error associated with a particular program node." 170 171 def __init__(self, message): 172 self.message = message 173 self.unit_name = None 174 self.astnode = None 175 176 def get_lineno(self, node): 177 lineno = node.lineno 178 if lineno is not None: 179 return lineno 180 else: 181 for child in node.getChildNodes(): 182 lineno = self.get_lineno(child) 183 if lineno is not None: 184 return lineno 185 return None 186 187 def __repr__(self): 188 return "Error in %r at line %r: %s" % (self.unit_name, self.get_lineno(self.astnode), self.message) 189 190 def __str__(self): 191 return repr(self) 192 193 class InspectError(NodeProcessingError): 194 195 "An error during the module inspection process." 196 197 pass 198 199 class TranslateError(NodeProcessingError): 200 201 "An error during the module translation process." 202 203 pass 204 205 class TranslationNotImplementedError(TranslateError): 206 207 "An error caused by a node not being supported in translation." 208 209 pass 210 211 # Special representations. 212 213 class AtLeast: 214 215 "A special representation for numbers of a given value or greater." 216 217 def __init__(self, count): 218 self.count = count 219 220 def __eq__(self, other): 221 return 0 222 223 __lt__ = __le__ = __eq__ 224 225 def __ne__(self, other): 226 return 1 227 228 def __gt__(self, other): 229 if isinstance(other, AtLeast): 230 return 0 231 else: 232 return self.count > other 233 234 def __ge__(self, other): 235 if isinstance(other, AtLeast): 236 return 0 237 else: 238 return self.count >= other 239 240 def __iadd__(self, other): 241 if isinstance(other, AtLeast): 242 self.count += other.count 243 else: 244 self.count += other 245 return self 246 247 def __radd__(self, other): 248 if isinstance(other, AtLeast): 249 return AtLeast(self.count + other.count) 250 else: 251 return AtLeast(self.count + other) 252 253 def __repr__(self): 254 return "AtLeast(%r)" % self.count 255 256 # Useful data. 257 258 comparison_methods = { 259 "==" : ("__eq__", "__eq__"), 260 "!=" : ("__ne__", "__ne__"), 261 "<" : ("__lt__", "__gt__"), 262 "<=" : ("__le__", "__ge__"), 263 ">=" : ("__ge__", "__le__"), 264 ">" : ("__gt__", "__lt__"), 265 "is" : None, 266 "is not" : None, 267 "in" : None, 268 "not in" : None 269 } 270 271 augassign_methods = { 272 "+=" : ("__iadd__", ("__add__", "__radd__")), 273 "-=" : ("__isub__", ("__sub__", "__rsub__")), 274 "*=" : ("__imul__", ("__mul__", "__rmul__")), 275 "/=" : ("__idiv__", ("__div__", "__rdiv__")), 276 "//=" : ("__ifloordiv__", ("__floordiv__", "__rfloordiv__")), 277 "%=" : ("__imod__", ("__mod__", "__rmod__")), 278 "**=" : ("__ipow__", ("__pow__", "__rpow__")), 279 "<<=" : ("__ilshift__", ("__lshift__", "__rlshift__")), 280 ">>=" : ("__irshift__", ("__rshift__", "__rrshift__")), 281 "&=" : ("__iand__", ("__and__", "__rand__")), 282 "^=" : ("__ixor__", ("__xor__", "__rxor__")), 283 "|=" : ("__ior__", ("__or__", "__ror__")) 284 } 285 286 binary_methods = { 287 "Add" : ("__add__", "__radd__"), 288 "Bitand" : ("__and__", "__rand__"), 289 "Bitor" : ("__or__", "__ror__"), 290 "Bitxor" : ("__xor__", "__rxor__"), 291 "Div" : ("__div__", "__rdiv__"), 292 "FloorDiv" : ("__floordiv__", "__rfloordiv__"), 293 "LeftShift" : ("__lshift__", "__rlshift__"), 294 "Mod" : ("__mod__", "__rmod__"), 295 "Mul" : ("__mul__", "__rmul__"), 296 "Power" : ("__pow__", "__rpow__"), 297 "RightShift" : ("__rshift__", "__rrshift__"), 298 "Sub" : ("__sub__", "__rsub__") 299 } 300 301 unary_methods = { 302 "Invert" : "__invert__", 303 "UnaryAdd" : "__pos__", 304 "UnarySub" : "__neg__" 305 } 306 307 operator_functions = { 308 309 # Binary operations. 310 311 "Add" : "add", 312 "Bitand" : "and_", 313 "Bitor" : "or_", 314 "Bitxor" : "xor", 315 "Div" : "div", 316 "FloorDiv" : "floordiv", 317 "LeftShift" : "lshift", 318 "Mod" : "mod", 319 "Mul" : "mul", 320 "Power" : "pow", 321 "RightShift" : "rshift", 322 "Sub" : "sub", 323 324 # Unary operations. 325 326 "Invert" : "invert", 327 "UnaryAdd" : "pos", 328 "UnarySub" : "neg", 329 330 # Augmented assignment. 331 332 "+=" : "iadd", 333 "-=" : "isub", 334 "*=" : "imul", 335 "/=" : "idiv", 336 "//=" : "ifloordiv", 337 "%=" : "imod", 338 "**=" : "ipow", 339 "<<=" : "ilshift", 340 ">>=" : "irshift", 341 "&=" : "iand", 342 "^=" : "ixor", 343 "|=" : "ior", 344 345 # Comparisons. 346 347 "==" : "eq", 348 "!=" : "ne", 349 "<" : "lt", 350 "<=" : "le", 351 ">=" : "ge", 352 ">" : "gt", 353 354 # Access and slicing. 355 356 "AssSlice" : "setslice", 357 "Slice" : "getslice", 358 "AssSubscript" : "setitem", 359 "Subscript" : "getitem", 360 } 361 362 # vim: tabstop=4 expandtab shiftwidth=4