1 #!/usr/bin/env python 2 3 """ 4 Common classes. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 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 micropython.basicdata import Instance 23 import sys 24 25 # Visitors and activities related to node annotations. 26 27 class ASTVisitor: 28 29 "A base class for visitors." 30 31 def default(self, node, *args): 32 for n in node.getChildNodes(): 33 self.dispatch(n) 34 35 def dispatch(self, node, *args): 36 37 "Dispatch using 'node', annotating any raised exceptions." 38 39 # Dispatch via a generic visit method. 40 41 try: 42 return node.visit(self, *args) 43 44 # Annotate the exception in case of failure. 45 46 except NodeProcessingError, exc: 47 if exc.astnode is None: 48 exc.astnode = node 49 50 # NOTE: Should perhaps specialise the subclasses appropriately. 51 52 if hasattr(self, "unit"): 53 exc.unit_name = self.unit.full_name() 54 else: 55 exc.unit_name = self.full_name() 56 raise 57 58 def possible_accessor_types(self, node, defining_users=1): 59 60 """ 61 Given annotations made during the inspection process, return all possible 62 type names and indications of static usage for a 'node' involved in 63 attribute access. 64 65 If 'defining_users' is set to a false value, attempt to get the type 66 names specifically applicable to the node, rather than retrieving more 67 general definition-based type observations. 68 """ 69 70 target_names = set() 71 72 if hasattr(node, "_attrusers"): 73 74 # Visit each attribute user. 75 76 for user in node._attrusers: 77 78 # Since users such as branches may not provide type information, 79 # attempt to find defining users. 80 81 if defining_users: 82 for def_user in user._attrdefs or [user]: 83 for target_name, is_static in def_user._attrtypes.get(node._username, []): 84 target_names.add((target_name, is_static)) 85 else: 86 for target_name, is_static in user._attrspecifictypes.get(node._username, []): 87 target_names.add((target_name, is_static)) 88 89 return target_names 90 91 def used_by_unit(node): 92 93 """ 94 Return whether the definition made by a 'node' is actually employed by the 95 program unit within which it is found. 96 """ 97 98 return hasattr(node, "unit") and node.unit.parent.has_key(node.unit.name) 99 100 def get_object_types_for_usage(usage, objtable, name, unit_name, all_attributes): 101 102 """ 103 Return for the given attribute 'usage', using the 'objtable', the object 104 types which satisfy such usage, reporting any problems for the given 'name' 105 and 'unit_name'. Each object type is given as a tuple of the form (type, 106 is_static). 107 108 Where 'all_attributes' is set to a true value, all attributes in a usage 109 list must be matched to provide object types. Otherwise, the presence of any 110 attribute from a usage list in a type's set of attributes will be enough to 111 provide that type as a suitable object type. 112 """ 113 114 all_objtypes = set() 115 116 for attrnames in usage: 117 attrnames = attrnames or () 118 119 # Determine whether all attributes are required. 120 121 if all_attributes: 122 objtypes = objtable.all_possible_objects_plus_status(attrnames) 123 if not objtypes: 124 print >>sys.stderr, "Warning: usage in %r for %r finds no object supporting all attributes %r" % ( 125 unit_name, name, attrnames.keys()) 126 else: 127 objtypes = objtable.any_possible_objects_plus_status(attrnames) 128 if not objtypes: 129 print >>sys.stderr, "Warning: usage in %r for %r finds no object supporting any attributes %r" % ( 130 unit_name, name, attrnames.keys()) 131 132 all_objtypes.update(objtypes) 133 134 return all_objtypes 135 136 def filter_using_self(objtypes, cls): 137 138 """ 139 Filter the given 'objtypes' (a collection of object names and static 140 indicators) for the given 'cls', returning only the (name, static) elements 141 compatible with 'cls' and its descendants. 142 """ 143 144 descendants = cls.all_descendants() 145 filtered = [] 146 for objname, is_static in objtypes: 147 if objname == cls.full_name() or objname in descendants: 148 filtered.append((objname, is_static)) 149 return filtered 150 151 # Errors. 152 153 class ProcessingError(Exception): 154 155 "A processing error." 156 157 pass 158 159 class TableError(ProcessingError): 160 161 "An error occurring during access to a lookup table." 162 163 pass 164 165 class TableGenerationError(ProcessingError): 166 167 "An error occurring when generating a lookup table." 168 169 pass 170 171 class NodeProcessingError(ProcessingError): 172 173 "A processing error associated with a particular program node." 174 175 def __init__(self, message): 176 self.message = message 177 self.unit_name = None 178 self.astnode = None 179 180 def get_lineno(self, node): 181 if node is None: 182 return None 183 184 lineno = node.lineno 185 if lineno is not None: 186 return lineno 187 else: 188 for child in node.getChildNodes(): 189 lineno = self.get_lineno(child) 190 if lineno is not None: 191 return lineno 192 return None 193 194 def __repr__(self): 195 return "Error in %r at line %r: %s" % (self.unit_name, self.get_lineno(self.astnode), self.message) 196 197 def __str__(self): 198 return repr(self) 199 200 class InspectError(NodeProcessingError): 201 202 "An error during the module inspection process." 203 204 pass 205 206 class TranslateError(NodeProcessingError): 207 208 "An error during the module translation process." 209 210 pass 211 212 class TranslationNotImplementedError(TranslateError): 213 214 "An error caused by a node not being supported in translation." 215 216 pass 217 218 # Special representations. 219 220 class AtLeast: 221 222 "A special representation for numbers of a given value or greater." 223 224 def __init__(self, count): 225 self.count = count 226 227 def __eq__(self, other): 228 return 0 229 230 __lt__ = __le__ = __eq__ 231 232 def __ne__(self, other): 233 return 1 234 235 def __gt__(self, other): 236 if isinstance(other, AtLeast): 237 return 0 238 else: 239 return self.count > other 240 241 def __ge__(self, other): 242 if isinstance(other, AtLeast): 243 return 0 244 else: 245 return self.count >= other 246 247 def __iadd__(self, other): 248 if isinstance(other, AtLeast): 249 self.count += other.count 250 else: 251 self.count += other 252 return self 253 254 def __radd__(self, other): 255 if isinstance(other, AtLeast): 256 return AtLeast(self.count + other.count) 257 else: 258 return AtLeast(self.count + other) 259 260 def __repr__(self): 261 return "AtLeast(%r)" % self.count 262 263 # Useful data. 264 265 operator_functions = { 266 267 # Binary operations. 268 269 "Add" : "add", 270 "Bitand" : "and_", 271 "Bitor" : "or_", 272 "Bitxor" : "xor", 273 "Div" : "div", 274 "FloorDiv" : "floordiv", 275 "LeftShift" : "lshift", 276 "Mod" : "mod", 277 "Mul" : "mul", 278 "Power" : "pow", 279 "RightShift" : "rshift", 280 "Sub" : "sub", 281 282 # Unary operations. 283 284 "Invert" : "invert", 285 "UnaryAdd" : "pos", 286 "UnarySub" : "neg", 287 288 # Augmented assignment. 289 290 "+=" : "iadd", 291 "-=" : "isub", 292 "*=" : "imul", 293 "/=" : "idiv", 294 "//=" : "ifloordiv", 295 "%=" : "imod", 296 "**=" : "ipow", 297 "<<=" : "ilshift", 298 ">>=" : "irshift", 299 "&=" : "iand", 300 "^=" : "ixor", 301 "|=" : "ior", 302 303 # Comparisons. 304 305 "==" : "eq", 306 "!=" : "ne", 307 "<" : "lt", 308 "<=" : "le", 309 ">=" : "ge", 310 ">" : "gt", 311 312 # Access and slicing. 313 314 "AssSlice" : "setslice", 315 "Slice" : "getslice", 316 "AssSubscript" : "setitem", 317 "Subscript" : "getitem", 318 } 319 320 # vim: tabstop=4 expandtab shiftwidth=4