1 #!/usr/bin/env python 2 3 """ 4 Common classes. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 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.ast import AssAttr, Getattr, Name 23 import compiler.ast 24 from micropython.basicdata import Const, Constant 25 from micropython.data import Attr, Class, Module 26 from micropython.errors import * 27 28 try: 29 set 30 except NameError: 31 from sets import Set as set 32 33 # Visitors and activities related to node annotations. 34 35 class ASTVisitor: 36 37 "A base class for visitors." 38 39 def process_definitions(self, node): 40 41 """ 42 Process and return all definitions beneath 'node', but not definitions 43 within definitions. 44 """ 45 46 definitions = [] 47 for n in node.getChildNodes(): 48 if isinstance(n, (compiler.ast.Class, compiler.ast.Function)): 49 definitions.append(self.dispatch(n)) 50 else: 51 definitions += self.process_definitions(n) 52 return definitions 53 54 # Visitor support methods. 55 56 def default(self, node, *args): 57 for n in node.getChildNodes(): 58 self.dispatch(n) 59 60 def dispatch(self, node, *args): 61 62 "Dispatch using 'node', annotating any raised exceptions." 63 64 # Dispatch via a generic visit method. 65 66 try: 67 return node.visit(self, *args) 68 69 # Annotate the exception in case of failure. 70 71 except NodeProcessingError, exc: 72 if exc.astnode is None: 73 exc.astnode = node 74 exc.unit_name = self.get_unit().full_name() 75 raise 76 77 # Deduction-related methods. 78 79 def get_attributes(self, targets, attrname): 80 81 "Return a list of attributes for 'targets' supporting 'attrname'." 82 83 attributes = set() 84 85 for target in targets: 86 try: 87 attributes.add(self.objtable.access(target.full_name(), attrname)) 88 except TableError: 89 pass 90 91 return attributes 92 93 def get_attribute_and_value(self, obj): 94 95 """ 96 Return (attribute, value) details for the given 'obj', where an 97 attribute of None can be returned for constant objects, and where None 98 can be returned as the result where no concrete details can be provided. 99 """ 100 101 if isinstance(obj, Constant): 102 return None, obj 103 104 if isinstance(obj, Attr): 105 return obj, obj.get_value() 106 107 return None 108 109 def provides_constant_result(self, value): 110 111 "Return whether 'value' provides a constant result." 112 113 return isinstance(value, (Const, Constant)) 114 115 def provides_self_access(self, node, unit): 116 117 """ 118 Return whether the 'node' in the given 'unit' provides a self-based 119 attribute access. 120 """ 121 122 attr_value = self.get_attribute_and_value(node._expr) 123 124 if attr_value: 125 target, value = attr_value 126 127 return target and target.name == "self" and target.parent is unit and \ 128 unit.is_method() 129 130 return False 131 132 def possible_attributes_from_annotation(self, node): 133 134 """ 135 Return (attribute, value) details provided by any _expr or _attr 136 annotations on 'node'. 137 """ 138 139 attr_value = self.get_attribute_and_value(node._attr) 140 141 if attr_value: 142 return [attr_value] 143 144 attrs = set() 145 expr = node._expr 146 147 if expr: 148 149 # Permitting multiple expression types if they provide the 150 # attribute. 151 152 if isinstance(expr, Attr): 153 exprs = expr.get_values() 154 else: 155 exprs = [expr] 156 157 # For each expression value try and get a concrete 158 # attribute. 159 160 for expr in exprs: 161 attr = expr.all_attributes().get(node.attrname) 162 163 # Where an attribute can be obtained, record its 164 # details. 165 166 if attr: 167 attrs.add((attr, attr.get_value())) 168 169 return attrs 170 171 def possible_accessor_types_from_usage(self, node, defining_users=1): 172 173 """ 174 Return a set of (target name, static) tuples from an investigation of 175 attribute usage observations stored on the given 'node'. 176 177 If 'defining_users' is set to a false value, attempt to get the type 178 names specifically applicable to the node, rather than retrieving more 179 general definition-based type observations. 180 """ 181 182 target_names = set() 183 184 if node._attrusers: 185 186 # Visit each attribute user. 187 188 for user in node._attrusers: 189 190 # Since users such as branches may not provide type information, 191 # attempt to find defining users. 192 193 if defining_users: 194 for def_user in user._attrdefs or [user]: 195 for target_name, is_static in def_user._attrtypes.get(node._username, []): 196 target_names.add((target_name, is_static)) 197 else: 198 for target_name, is_static in user._attrspecifictypes.get(node._username, []): 199 target_names.add((target_name, is_static)) 200 201 return target_names 202 203 def possible_accessors_from_usage(self, node, defining_users=1): 204 205 """ 206 Return possible accessors from the usage recorded on the given 'node'. 207 208 If 'defining_users' is set to a false value, attempt to get the type 209 names specifically applicable to the node, rather than retrieving more 210 general definition-based type observations. 211 """ 212 213 targets = set() 214 target_names = self.possible_accessor_types_from_usage(node, defining_users) 215 216 if target_names: 217 for target_name, is_static in target_names: 218 targets.add(self.objtable.get_object(target_name)) 219 220 return targets 221 222 def possible_accessors_for_attribute(self, attrname): 223 224 "Return possible accessors given the single 'attrname'." 225 226 targets = set() 227 228 for target_name in self.objtable.any_possible_objects([attrname]): 229 targets.add(self.objtable.get_object(target_name)) 230 231 return targets 232 233 def used_by_unit(node): 234 235 """ 236 Return whether the definition made by a 'node' is actually employed by the 237 program unit within which it is found. 238 """ 239 240 return node.unit and node.unit.parent.has_key(node.unit.original_name) 241 242 # Useful data. 243 244 operator_functions = { 245 246 # Binary operations. 247 248 "Add" : "add", 249 "Bitand" : "and_", 250 "Bitor" : "or_", 251 "Bitxor" : "xor", 252 "Div" : "div", 253 "FloorDiv" : "floordiv", 254 "LeftShift" : "lshift", 255 "Mod" : "mod", 256 "Mul" : "mul", 257 "Power" : "pow", 258 "RightShift" : "rshift", 259 "Sub" : "sub", 260 261 # Unary operations. 262 263 "Invert" : "invert", 264 "UnaryAdd" : "pos", 265 "UnarySub" : "neg", 266 267 # Augmented assignment. 268 269 "+=" : "iadd", 270 "-=" : "isub", 271 "*=" : "imul", 272 "/=" : "idiv", 273 "//=" : "ifloordiv", 274 "%=" : "imod", 275 "**=" : "ipow", 276 "<<=" : "ilshift", 277 ">>=" : "irshift", 278 "&=" : "iand", 279 "^=" : "ixor", 280 "|=" : "ior", 281 282 # Comparisons. 283 284 "==" : "eq", 285 "!=" : "ne", 286 "<" : "lt", 287 "<=" : "le", 288 ">=" : "ge", 289 ">" : "gt", 290 291 # Access and slicing. 292 293 "AssSlice" : "setslice", # Python 2.7 294 "Slice" : "getslice", 295 "AssSubscript" : "setitem", # Python 2.7 296 "Subscript" : "getitem", 297 } 298 299 # vim: tabstop=4 expandtab shiftwidth=4