paul@39 | 1 | #!/usr/bin/env python |
paul@39 | 2 | |
paul@39 | 3 | """ |
paul@39 | 4 | Common classes. |
paul@39 | 5 | |
paul@787 | 6 | Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Paul Boddie <paul@boddie.org.uk> |
paul@39 | 7 | |
paul@39 | 8 | This program is free software; you can redistribute it and/or modify it under |
paul@39 | 9 | the terms of the GNU General Public License as published by the Free Software |
paul@39 | 10 | Foundation; either version 3 of the License, or (at your option) any later |
paul@39 | 11 | version. |
paul@39 | 12 | |
paul@39 | 13 | This program is distributed in the hope that it will be useful, but WITHOUT |
paul@39 | 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@39 | 15 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@39 | 16 | details. |
paul@39 | 17 | |
paul@39 | 18 | You should have received a copy of the GNU General Public License along with |
paul@39 | 19 | this program. If not, see <http://www.gnu.org/licenses/>. |
paul@39 | 20 | """ |
paul@39 | 21 | |
paul@738 | 22 | from micropython.stdcompiler import compiler |
paul@787 | 23 | from micropython.basicdata import Constant, TypedInstance, Instance |
paul@765 | 24 | from micropython.data import BaseAttr, Class |
paul@556 | 25 | from micropython.errors import * |
paul@685 | 26 | from os.path import split |
paul@339 | 27 | |
paul@582 | 28 | try: |
paul@582 | 29 | set |
paul@582 | 30 | except NameError: |
paul@582 | 31 | from sets import Set as set |
paul@582 | 32 | |
paul@357 | 33 | # Visitors and activities related to node annotations. |
paul@339 | 34 | |
paul@496 | 35 | class ASTVisitor: |
paul@339 | 36 | |
paul@339 | 37 | "A base class for visitors." |
paul@339 | 38 | |
paul@645 | 39 | def process_definitions(self, node): |
paul@645 | 40 | |
paul@645 | 41 | """ |
paul@645 | 42 | Process and return all definitions beneath 'node', but not definitions |
paul@645 | 43 | within definitions. |
paul@645 | 44 | """ |
paul@645 | 45 | |
paul@645 | 46 | definitions = [] |
paul@645 | 47 | for n in node.getChildNodes(): |
paul@645 | 48 | if isinstance(n, (compiler.ast.Class, compiler.ast.Function)): |
paul@748 | 49 | self.current_definition = n |
paul@645 | 50 | definitions.append(self.dispatch(n)) |
paul@752 | 51 | else: |
paul@752 | 52 | definitions += self.process_definitions(n) |
paul@645 | 53 | return definitions |
paul@645 | 54 | |
paul@748 | 55 | def processing_definition(self, n): |
paul@748 | 56 | return self.current_definition is n |
paul@748 | 57 | |
paul@645 | 58 | # Visitor support methods. |
paul@645 | 59 | |
paul@496 | 60 | def default(self, node, *args): |
paul@496 | 61 | for n in node.getChildNodes(): |
paul@496 | 62 | self.dispatch(n) |
paul@496 | 63 | |
paul@339 | 64 | def dispatch(self, node, *args): |
paul@357 | 65 | |
paul@357 | 66 | "Dispatch using 'node', annotating any raised exceptions." |
paul@357 | 67 | |
paul@496 | 68 | # Dispatch via a generic visit method. |
paul@496 | 69 | |
paul@339 | 70 | try: |
paul@496 | 71 | return node.visit(self, *args) |
paul@496 | 72 | |
paul@496 | 73 | # Annotate the exception in case of failure. |
paul@496 | 74 | |
paul@339 | 75 | except NodeProcessingError, exc: |
paul@340 | 76 | if exc.astnode is None: |
paul@340 | 77 | exc.astnode = node |
paul@623 | 78 | exc.unit_name = self.get_unit().full_name() |
paul@339 | 79 | raise |
paul@339 | 80 | |
paul@646 | 81 | def get_attributes(self, targets, attrname): |
paul@646 | 82 | |
paul@646 | 83 | "Return a list of attributes for 'targets' supporting 'attrname'." |
paul@646 | 84 | |
paul@646 | 85 | attributes = set() |
paul@646 | 86 | |
paul@646 | 87 | for target in targets: |
paul@646 | 88 | try: |
paul@646 | 89 | attributes.add(self.objtable.access(target.full_name(), attrname)) |
paul@646 | 90 | except TableError: |
paul@648 | 91 | pass |
paul@646 | 92 | |
paul@646 | 93 | return attributes |
paul@646 | 94 | |
paul@646 | 95 | def get_attribute_and_value(self, obj): |
paul@646 | 96 | |
paul@646 | 97 | """ |
paul@646 | 98 | Return (attribute, value) details for the given 'obj', where an |
paul@646 | 99 | attribute of None can be returned for constant objects, and where None |
paul@646 | 100 | can be returned as the result where no concrete details can be provided. |
paul@646 | 101 | """ |
paul@646 | 102 | |
paul@710 | 103 | if isinstance(obj, (Constant, TypedInstance)): |
paul@646 | 104 | return None, obj |
paul@646 | 105 | |
paul@717 | 106 | if isinstance(obj, BaseAttr): |
paul@646 | 107 | return obj, obj.get_value() |
paul@646 | 108 | |
paul@646 | 109 | return None |
paul@646 | 110 | |
paul@787 | 111 | def get_type_for_attribute(self, attr, value): |
paul@787 | 112 | |
paul@787 | 113 | """ |
paul@787 | 114 | For an attribute 'attr' and 'value', return the "parent" type of the |
paul@787 | 115 | attribute if possible. Otherwise, return None. |
paul@787 | 116 | """ |
paul@787 | 117 | |
paul@787 | 118 | if attr: |
paul@787 | 119 | return attr.get_type() |
paul@787 | 120 | elif value and not isinstance(value, Instance) and value.parent: |
paul@787 | 121 | return value.parent |
paul@787 | 122 | else: |
paul@787 | 123 | return None |
paul@787 | 124 | |
paul@787 | 125 | def get_values_for_attribute(self, attr, value): |
paul@787 | 126 | |
paul@787 | 127 | """ |
paul@787 | 128 | Return values defined for the given attribute 'attr' unless a specific |
paul@787 | 129 | 'value' is provided, in which case a list containing that value is |
paul@787 | 130 | returned. Where no values are defined, return None in a list. |
paul@787 | 131 | """ |
paul@787 | 132 | |
paul@787 | 133 | return value and [value] or attr and attr.get_values() or [None] |
paul@787 | 134 | |
paul@684 | 135 | def get_module_name(node, module): |
paul@684 | 136 | |
paul@684 | 137 | """ |
paul@684 | 138 | Using the given From 'node' and 'module' in which it is found, calculate |
paul@684 | 139 | any relative import information, returning a tuple containing a module |
paul@684 | 140 | to import along with any names to import based on the node's name |
paul@684 | 141 | information. |
paul@684 | 142 | |
paul@684 | 143 | Where the returned module is given as None, whole module imports should |
paul@684 | 144 | be performed for the returned modules using the returned names. |
paul@684 | 145 | """ |
paul@684 | 146 | |
paul@684 | 147 | # Absolute import. |
paul@684 | 148 | |
paul@684 | 149 | if node.level == 0: |
paul@684 | 150 | return node.modname, node.names |
paul@684 | 151 | |
paul@684 | 152 | # Relative to an ancestor of this module. |
paul@684 | 153 | |
paul@684 | 154 | else: |
paul@684 | 155 | path = module.full_name().split(".") |
paul@685 | 156 | level = node.level |
paul@685 | 157 | |
paul@685 | 158 | # Relative imports treat package roots as submodules. |
paul@685 | 159 | |
paul@685 | 160 | if split(module.filename)[-1] == "__init__.py": |
paul@685 | 161 | level -= 1 |
paul@685 | 162 | |
paul@685 | 163 | if level > len(path): |
paul@684 | 164 | raise InspectError("Relative import %r involves too many levels up from module %r" % ( |
paul@685 | 165 | ("%s%s" % ("." * node.level, node.modname or "")), module.full_name())) |
paul@685 | 166 | |
paul@685 | 167 | basename = ".".join(path[:len(path)-level]) |
paul@684 | 168 | |
paul@684 | 169 | # Name imports from a module. |
paul@684 | 170 | |
paul@684 | 171 | if node.modname: |
paul@684 | 172 | return "%s.%s" % (basename, node.modname), node.names |
paul@684 | 173 | |
paul@684 | 174 | # Relative whole module imports. |
paul@684 | 175 | |
paul@684 | 176 | else: |
paul@685 | 177 | return None, [("%s.%s" % (basename, name), alias or name) for name, alias in node.names] |
paul@684 | 178 | |
paul@353 | 179 | def used_by_unit(node): |
paul@353 | 180 | |
paul@353 | 181 | """ |
paul@353 | 182 | Return whether the definition made by a 'node' is actually employed by the |
paul@353 | 183 | program unit within which it is found. |
paul@353 | 184 | """ |
paul@353 | 185 | |
paul@665 | 186 | return node.unit and node.unit.parent.has_key(node.unit.original_name) |
paul@353 | 187 | |
paul@172 | 188 | # Useful data. |
paul@172 | 189 | |
paul@354 | 190 | operator_functions = { |
paul@354 | 191 | |
paul@668 | 192 | # Fundamental operations. |
paul@668 | 193 | |
paul@668 | 194 | "in" : "in_", |
paul@739 | 195 | "not in" : "not_in", |
paul@668 | 196 | |
paul@354 | 197 | # Binary operations. |
paul@354 | 198 | |
paul@354 | 199 | "Add" : "add", |
paul@354 | 200 | "Bitand" : "and_", |
paul@354 | 201 | "Bitor" : "or_", |
paul@354 | 202 | "Bitxor" : "xor", |
paul@354 | 203 | "Div" : "div", |
paul@354 | 204 | "FloorDiv" : "floordiv", |
paul@354 | 205 | "LeftShift" : "lshift", |
paul@354 | 206 | "Mod" : "mod", |
paul@354 | 207 | "Mul" : "mul", |
paul@354 | 208 | "Power" : "pow", |
paul@354 | 209 | "RightShift" : "rshift", |
paul@354 | 210 | "Sub" : "sub", |
paul@354 | 211 | |
paul@354 | 212 | # Unary operations. |
paul@354 | 213 | |
paul@354 | 214 | "Invert" : "invert", |
paul@354 | 215 | "UnaryAdd" : "pos", |
paul@354 | 216 | "UnarySub" : "neg", |
paul@354 | 217 | |
paul@354 | 218 | # Augmented assignment. |
paul@354 | 219 | |
paul@354 | 220 | "+=" : "iadd", |
paul@354 | 221 | "-=" : "isub", |
paul@354 | 222 | "*=" : "imul", |
paul@354 | 223 | "/=" : "idiv", |
paul@354 | 224 | "//=" : "ifloordiv", |
paul@354 | 225 | "%=" : "imod", |
paul@354 | 226 | "**=" : "ipow", |
paul@354 | 227 | "<<=" : "ilshift", |
paul@354 | 228 | ">>=" : "irshift", |
paul@354 | 229 | "&=" : "iand", |
paul@354 | 230 | "^=" : "ixor", |
paul@354 | 231 | "|=" : "ior", |
paul@354 | 232 | |
paul@354 | 233 | # Comparisons. |
paul@354 | 234 | |
paul@354 | 235 | "==" : "eq", |
paul@354 | 236 | "!=" : "ne", |
paul@354 | 237 | "<" : "lt", |
paul@354 | 238 | "<=" : "le", |
paul@354 | 239 | ">=" : "ge", |
paul@374 | 240 | ">" : "gt", |
paul@374 | 241 | |
paul@374 | 242 | # Access and slicing. |
paul@374 | 243 | |
paul@623 | 244 | "AssSlice" : "setslice", # Python 2.7 |
paul@374 | 245 | "Slice" : "getslice", |
paul@623 | 246 | "AssSubscript" : "setitem", # Python 2.7 |
paul@374 | 247 | "Subscript" : "getitem", |
paul@354 | 248 | } |
paul@354 | 249 | |
paul@39 | 250 | # vim: tabstop=4 expandtab shiftwidth=4 |