1.1 --- a/micropython/ast.py Sat May 10 02:32:20 2008 +0200
1.2 +++ b/micropython/ast.py Sun May 11 18:20:27 2008 +0200
1.3 @@ -19,17 +19,11 @@
1.4 this program. If not, see <http://www.gnu.org/licenses/>.
1.5 """
1.6
1.7 -import micropython.inspect
1.8 +from micropython.common import *
1.9 +from micropython.data import *
1.10 from micropython.rsvp import *
1.11 -from micropython.common import *
1.12 import compiler.ast
1.13 from compiler.visitor import ASTVisitor
1.14 -try:
1.15 - set
1.16 -except NameError:
1.17 - from sets import Set as set
1.18 -
1.19 -class TranslateError(NodeProcessingError): pass
1.20
1.21 class Label:
1.22
1.23 @@ -264,12 +258,12 @@
1.24
1.25 # Get the details of the access.
1.26
1.27 - if isinstance(last.attr, micropython.inspect.Const):
1.28 + if isinstance(last.attr, Const):
1.29 target_name = last.attr.value_type_name()
1.30 else:
1.31 target = last.attr.value
1.32
1.33 - if isinstance(target, micropython.inspect.Const):
1.34 + if isinstance(target, Const):
1.35 target_name = target.value_type_name()
1.36 else:
1.37 target_name = target.full_name()
1.38 @@ -527,11 +521,11 @@
1.39
1.40 if scope == "local":
1.41 unit = self.unit
1.42 - if isinstance(unit, micropython.inspect.Function):
1.43 + if isinstance(unit, Function):
1.44 self.new_op(NameInstruction(unit.all_locals()[name]))
1.45 - elif isinstance(unit, micropython.inspect.Class):
1.46 + elif isinstance(unit, Class):
1.47 self.new_op(AddressInstruction(unit.all_class_attributes()[name]))
1.48 - elif isinstance(unit, micropython.inspect.Module):
1.49 + elif isinstance(unit, Module):
1.50 self.new_op(AddressInstruction(unit.module_attributes()[name]))
1.51 else:
1.52 raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r." % (unit, name))
1.53 @@ -575,14 +569,14 @@
1.54 def _have_constant_input(self, n):
1.55 last = self.last_ops(n+1)
1.56 return len(last) > n and (isinstance(last[n], LoadAddress) and last[n].attr.assignments == 1 or
1.57 - isinstance(last[n], LoadConst)) # and not isinstance(last[n].attr, micropython.inspect.Instance)
1.58 + isinstance(last[n], LoadConst)) # and not isinstance(last[n].attr, Instance)
1.59
1.60 def _have_known_target(self):
1.61 return self._have_constant_input(0)
1.62
1.63 def _have_self_input(self):
1.64 last = self.last_op()
1.65 - return isinstance(self.unit, micropython.inspect.Function) and \
1.66 + return isinstance(self.unit, Function) and \
1.67 self.unit.is_method() and isinstance(last, LoadName) and \
1.68 last.attr.name == "self"
1.69
1.70 @@ -669,9 +663,9 @@
1.71
1.72 # Handle calls to classes.
1.73
1.74 - if isinstance(target, micropython.inspect.Class):
1.75 + if isinstance(target, Class):
1.76 target = target.get_instantiator()
1.77 - context = micropython.inspect.Instance()
1.78 + context = Instance()
1.79
1.80 # A special context is chosen to avoid generating unnecessary
1.81 # context loading and checking instructions.
2.1 --- a/micropython/common.py Sat May 10 02:32:20 2008 +0200
2.2 +++ b/micropython/common.py Sun May 11 18:20:27 2008 +0200
2.3 @@ -19,12 +19,25 @@
2.4 this program. If not, see <http://www.gnu.org/licenses/>.
2.5 """
2.6
2.7 +try:
2.8 + set
2.9 +except NameError:
2.10 + from sets import Set as set
2.11 +
2.12 +# Errors.
2.13 +
2.14 class ProcessingError(Exception):
2.15
2.16 "A processing error."
2.17
2.18 pass
2.19
2.20 +class InspectError(ProcessingError):
2.21 +
2.22 + "An error during the module inspection process."
2.23 +
2.24 + pass
2.25 +
2.26 class NodeProcessingError(ProcessingError):
2.27
2.28 "A processing error associated with a particular program node."
2.29 @@ -40,4 +53,65 @@
2.30 def __str__(self):
2.31 return repr(self)
2.32
2.33 +class TranslateError(NodeProcessingError):
2.34 +
2.35 + "An error during the module translation process."
2.36 +
2.37 + pass
2.38 +
2.39 +# Inspection representations.
2.40 +
2.41 +class AtLeast:
2.42 +
2.43 + "A special representation for numbers of a given value or greater."
2.44 +
2.45 + def __init__(self, count):
2.46 + self.count = count
2.47 +
2.48 + def __eq__(self, other):
2.49 + return 0
2.50 +
2.51 + __lt__ = __le__ = __eq__
2.52 +
2.53 + def __ne__(self, other):
2.54 + return 1
2.55 +
2.56 + def __gt__(self, other):
2.57 + if isinstance(other, AtLeast):
2.58 + return 0
2.59 + else:
2.60 + return self.count > other
2.61 +
2.62 + def __ge__(self, other):
2.63 + if isinstance(other, AtLeast):
2.64 + return 0
2.65 + else:
2.66 + return self.count >= other
2.67 +
2.68 + def __iadd__(self, other):
2.69 + if isinstance(other, AtLeast):
2.70 + self.count += other.count
2.71 + else:
2.72 + self.count += other
2.73 + return self
2.74 +
2.75 + def __radd__(self, other):
2.76 + if isinstance(other, AtLeast):
2.77 + return AtLeast(self.count + other.count)
2.78 + else:
2.79 + return AtLeast(self.count + other)
2.80 +
2.81 + def __repr__(self):
2.82 + return "AtLeast(%r)" % self.count
2.83 +
2.84 +class Naming:
2.85 +
2.86 + "A mix-in providing naming conveniences."
2.87 +
2.88 + def full_name(self):
2.89 + if self.name is not None:
2.90 + return self.parent.full_name() + "." + self.name
2.91 + else:
2.92 + return self.parent.full_name()
2.93 +
2.94 # vim: tabstop=4 expandtab shiftwidth=4
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/micropython/data.py Sun May 11 18:20:27 2008 +0200
3.3 @@ -0,0 +1,775 @@
3.4 +#!/usr/bin/env python
3.5 +
3.6 +"""
3.7 +Data classes.
3.8 +
3.9 +Copyright (C) 2007, 2008 Paul Boddie <paul@boddie.org.uk>
3.10 +
3.11 +This program is free software; you can redistribute it and/or modify it under
3.12 +the terms of the GNU General Public License as published by the Free Software
3.13 +Foundation; either version 3 of the License, or (at your option) any later
3.14 +version.
3.15 +
3.16 +This program is distributed in the hope that it will be useful, but WITHOUT
3.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
3.19 +details.
3.20 +
3.21 +You should have received a copy of the GNU General Public License along with
3.22 +this program. If not, see <http://www.gnu.org/licenses/>.
3.23 +
3.24 +--------
3.25 +
3.26 +The central classes in this module are the following:
3.27 +
3.28 + * Class
3.29 + * Function
3.30 + * Module
3.31 + * InspectedModule (derived from Module)
3.32 +
3.33 +All of the above support the Naming interface either explicitly or through
3.34 +general conformance, meaning that all can be asked to provide their 'full_name'
3.35 +using the method of that name.
3.36 +
3.37 +Additionally, all of the above also support a dictionary interface in order to
3.38 +access names within their defined scopes. Specific methods also exist in order
3.39 +to distinguish between certain kinds of attributes:
3.40 +
3.41 + * Class: (class|all_class|instance|all)_attributes
3.42 + * Function: parameters, locals, all_locals
3.43 + * Module: module_attributes
3.44 +
3.45 +These specific methods are useful in certain situations.
3.46 +
3.47 +The above classes also provide a 'node' attribute, indicating the AST node where
3.48 +each such object is defined.
3.49 +"""
3.50 +
3.51 +from micropython.common import *
3.52 +
3.53 +# Mix-ins and abstract classes.
3.54 +
3.55 +class NamespaceDict:
3.56 +
3.57 + "A mix-in providing dictionary methods."
3.58 +
3.59 + def __init__(self, global_namespace=None):
3.60 + self.namespace = {}
3.61 + self.globals = set()
3.62 + self.global_namespace = global_namespace
3.63 +
3.64 + def __getitem__(self, name):
3.65 + return self.namespace[name]
3.66 +
3.67 + def get(self, name, default=None):
3.68 + return self.namespace.get(name, default)
3.69 +
3.70 + def __setitem__(self, name, value):
3.71 + self.set(name, value)
3.72 +
3.73 + def set(self, name, value, single_assignment=1):
3.74 +
3.75 + """
3.76 + A more powerful set operation, making 'name' refer to 'value' whilst
3.77 + indicating whether a 'single_assignment' (true by default) occurs in
3.78 + this operation (or whether the operation covers potentially many
3.79 + assignments in the lifetime of a program).
3.80 + """
3.81 +
3.82 + if name in self.globals:
3.83 + self.global_namespace.set(name, value, 0)
3.84 + else:
3.85 + attr = self._set(name, value)
3.86 +
3.87 + # NOTE: Insist on assignments with known values.
3.88 +
3.89 + if value is not None:
3.90 + attr.update(value, single_assignment)
3.91 +
3.92 + def set_module(self, name, value):
3.93 +
3.94 + """
3.95 + A specialised set operation, making 'name' refer to 'value' in the
3.96 + context of making a module reference available in association with
3.97 + 'name' as part of the import of that module or a submodule of that
3.98 + module.
3.99 + """
3.100 +
3.101 + attr = self._set(name, value)
3.102 + if attr.assignments is None:
3.103 + attr.assignments = 1
3.104 + attr.assignment_values.add(value)
3.105 +
3.106 + def _set(self, name, value):
3.107 +
3.108 + "The underlying set operation associating 'name' with 'value'."
3.109 +
3.110 + if not self.namespace.has_key(name):
3.111 + self.namespace[name] = Attr(None, self, name, value)
3.112 + return self.namespace[name]
3.113 +
3.114 + def __delitem__(self, name):
3.115 + del self.namespace[name]
3.116 +
3.117 + def has_key(self, name):
3.118 + return self.namespace.has_key(name)
3.119 +
3.120 + def keys(self):
3.121 + return self.namespace.keys()
3.122 +
3.123 + def values(self):
3.124 + return self.namespace.values()
3.125 +
3.126 + def items(self):
3.127 + return self.namespace.items()
3.128 +
3.129 + def make_global(self, name):
3.130 + if not self.namespace.has_key(name):
3.131 + self.globals.add(name)
3.132 + else:
3.133 + raise InspectError(self.full_name(), self.node, "Name %r is both global and local in %r" % (name, self.full_name()))
3.134 +
3.135 + def get_assignments(self, name):
3.136 + if self.assignments.has_key(name):
3.137 + return max(self.assignments[name], len(self.assignment_values[name]))
3.138 + else:
3.139 + return None
3.140 +
3.141 + def attributes_as_list(self):
3.142 + self.finalise_attributes()
3.143 + l = [None] * len(self.keys())
3.144 + for attr in self.values():
3.145 + l[attr.position] = attr
3.146 + return l
3.147 +
3.148 + def finalise_attributes(self):
3.149 +
3.150 + "Make sure all attributes are fully defined."
3.151 +
3.152 + # The default action is to assign attribute positions sequentially.
3.153 +
3.154 + for i, attr in enumerate(self.values()):
3.155 + attr.position = i
3.156 +
3.157 +# Program data structures.
3.158 +
3.159 +class Attr:
3.160 +
3.161 + "An attribute entry having a parent as context."
3.162 +
3.163 + def __init__(self, position, parent, name, value=None, assignments=None):
3.164 + self.position = position
3.165 + self.parent = parent
3.166 + self.name = name
3.167 + self.value = value
3.168 +
3.169 + # Number of assignments per name.
3.170 +
3.171 + self.assignments = assignments
3.172 + self.assignment_values = set()
3.173 +
3.174 + def update(self, value, single_assignment):
3.175 +
3.176 + """
3.177 + Update the attribute, adding the 'value' provided to the known values
3.178 + associated with the attribute, changing the number of assignments
3.179 + according to the 'single_assignment' status of the operation, where
3.180 + a true value indicates that only one assignment is associated with the
3.181 + update, and a false value indicates that potentially many assignments
3.182 + may be involved.
3.183 + """
3.184 +
3.185 + if self.assignments is None:
3.186 + if single_assignment:
3.187 + self.assignments = 1
3.188 + else:
3.189 + self.assignments = AtLeast(1)
3.190 + else:
3.191 + if single_assignment:
3.192 + self.assignments += 1
3.193 + else:
3.194 + self.assignments += AtLeast(1)
3.195 + self.assignment_values.add(value)
3.196 +
3.197 + def __repr__(self):
3.198 + return "Attr(%r, %r, %r, %r, %r)" % (self.position, self.parent, self.name, self.value, self.assignments)
3.199 +
3.200 +class Const:
3.201 +
3.202 + "A constant object with no context."
3.203 +
3.204 + def __init__(self, value):
3.205 + self.value = value
3.206 +
3.207 + # Image generation details.
3.208 +
3.209 + self.location = None
3.210 +
3.211 + def __repr__(self):
3.212 + if self.location is not None:
3.213 + return "Const(%r, location=%r)" % (self.value, self.location)
3.214 + else:
3.215 + return "Const(%r)" % self.value
3.216 +
3.217 + # Support constants as dictionary keys in order to build constant tables.
3.218 +
3.219 + def __eq__(self, other):
3.220 + return self.value == other.value
3.221 +
3.222 + def __hash__(self):
3.223 + return hash(self.value)
3.224 +
3.225 + def value_type_name(self):
3.226 + return "__builtins__." + self.value.__class__.__name__
3.227 +
3.228 +class Class(NamespaceDict, Naming):
3.229 +
3.230 + "An inspected class."
3.231 +
3.232 + def __init__(self, name, parent, global_namespace=None, node=None):
3.233 +
3.234 + """
3.235 + Initialise the class with the given 'name', 'parent' object, optional
3.236 + 'global_namespace' and optional AST 'node'.
3.237 + """
3.238 +
3.239 + NamespaceDict.__init__(self, global_namespace)
3.240 + self.name = name
3.241 + self.parent = parent
3.242 + self.node = node
3.243 +
3.244 + # Superclasses, descendants and attributes.
3.245 +
3.246 + self.bases = []
3.247 + self.descendants = set()
3.248 + self.instattr = set() # instance attributes
3.249 + self.relocated = set() # attributes which do not have the same
3.250 + # position as those of the same name in
3.251 + # some superclasses
3.252 +
3.253 + # Caches.
3.254 +
3.255 + self.all_instattr = None # cache for instance_attributes
3.256 + self.all_instattr_names = None # from all_instattr
3.257 + self.all_classattr = None # cache for all_class_attributes
3.258 + self.all_classattr_names = None # from all_classattr
3.259 + self.allattr = None # cache for all_attributes
3.260 + self.allattr_names = None # from allattr
3.261 +
3.262 + # Add this class to its attributes.
3.263 +
3.264 + self.set("__class__", self)
3.265 +
3.266 + # Image generation details.
3.267 +
3.268 + self.location = None
3.269 + self.code_location = None
3.270 + self.instantiator = None
3.271 +
3.272 + # Program-related details.
3.273 +
3.274 + self.stack_usage = 0
3.275 + self.stack_temp_usage = 0
3.276 + self.stack_local_usage = 0
3.277 +
3.278 + def __repr__(self):
3.279 + if self.location is not None:
3.280 + return "Class(%r, %r, location=%r)" % (self.name, self.parent, self.location)
3.281 + else:
3.282 + return "Class(%r, %r)" % (self.name, self.parent)
3.283 +
3.284 + def finalise_attributes(self):
3.285 +
3.286 + "Make sure that all attributes are fully defined."
3.287 +
3.288 + self.finalise_class_attributes()
3.289 + self.finalise_instance_attributes()
3.290 +
3.291 + def get_instantiator(self):
3.292 +
3.293 + "Return a function which can be used to instantiate the class."
3.294 +
3.295 + if self.instantiator is None:
3.296 + init_method = self.all_class_attributes()["__init__"].value
3.297 + self.instantiator = init_method.function_from_method()
3.298 + return self.instantiator
3.299 +
3.300 + # Class-specific methods.
3.301 +
3.302 + def add_base(self, base):
3.303 + self.bases.append(base)
3.304 + base.add_descendant(self)
3.305 +
3.306 + def add_instance_attribute(self, name):
3.307 + self.instattr.add(name)
3.308 +
3.309 + def add_descendant(self, cls):
3.310 + self.descendants.add(cls)
3.311 + for base in self.bases:
3.312 + base.add_descendant(cls)
3.313 +
3.314 + "Return the attribute names provided by this class only."
3.315 +
3.316 + class_attribute_names = NamespaceDict.keys
3.317 +
3.318 + def class_attributes(self):
3.319 +
3.320 + "Return class attributes provided by this class only."
3.321 +
3.322 + self.finalise_class_attributes()
3.323 + return dict(self)
3.324 +
3.325 + def all_class_attribute_names(self):
3.326 +
3.327 + "Return the attribute names provided by classes in this hierarchy."
3.328 +
3.329 + if self.all_classattr_names is None:
3.330 + self.all_class_attributes()
3.331 + return self.all_classattr_names
3.332 +
3.333 + def all_class_attributes(self):
3.334 +
3.335 + "Return all class attributes, indicating the class which provides them."
3.336 +
3.337 + self.finalise_class_attributes()
3.338 + return self.all_classattr
3.339 +
3.340 + def finalise_class_attributes(self):
3.341 +
3.342 + "Make sure that the class attributes are fully defined."
3.343 +
3.344 + if self.all_classattr is None:
3.345 + self.all_classattr = {}
3.346 + clsattr = {}
3.347 +
3.348 + # Record provisional position information for attributes of this
3.349 + # class.
3.350 +
3.351 + for name in self.class_attributes().keys():
3.352 + clsattr[name] = set() # position not yet defined
3.353 +
3.354 + reversed_bases = self.bases[:]
3.355 + reversed_bases.reverse()
3.356 +
3.357 + # For the bases in reverse order, acquire class attribute details.
3.358 +
3.359 + for cls in reversed_bases:
3.360 + for name, attr in cls.all_class_attributes().items():
3.361 + self.all_classattr[name] = attr
3.362 +
3.363 + # Record previous attribute information.
3.364 +
3.365 + if clsattr.has_key(name):
3.366 + clsattr[name].add(attr.position)
3.367 +
3.368 + # Record class attributes provided by this class and its bases,
3.369 + # along with their positions.
3.370 +
3.371 + self.all_classattr.update(self.class_attributes())
3.372 +
3.373 + if clsattr:
3.374 + for i, name in enumerate(self._get_position_list(clsattr)):
3.375 + self.all_classattr[name].position = i
3.376 +
3.377 + return self.all_classattr
3.378 +
3.379 + def instance_attribute_names(self):
3.380 +
3.381 + "Return the instance attribute names provided by the class."
3.382 +
3.383 + if self.all_instattr_names is None:
3.384 + self.instance_attributes()
3.385 + return self.all_instattr_names
3.386 +
3.387 + def instance_attributes(self):
3.388 +
3.389 + "Return instance-only attributes for instances of this class."
3.390 +
3.391 + self.finalise_instance_attributes()
3.392 + return self.all_instattr
3.393 +
3.394 + def finalise_instance_attributes(self):
3.395 +
3.396 + "Make sure that the instance attributes are fully defined."
3.397 +
3.398 + if self.all_instattr is None:
3.399 + self.all_instattr = {}
3.400 + instattr = {}
3.401 +
3.402 + # Record provisional position information for attributes of this
3.403 + # instance.
3.404 +
3.405 + for name in self.instattr:
3.406 + instattr[name] = set() # position not yet defined
3.407 +
3.408 + reversed_bases = self.bases[:]
3.409 + reversed_bases.reverse()
3.410 +
3.411 + # For the bases in reverse order, acquire instance attribute
3.412 + # details.
3.413 +
3.414 + for cls in reversed_bases:
3.415 + for name, attr in cls.instance_attributes().items():
3.416 +
3.417 + # Record previous attribute information.
3.418 +
3.419 + if instattr.has_key(name):
3.420 + instattr[name].add(attr.position)
3.421 +
3.422 + # Cache the attributes by converting the positioned attributes into
3.423 + # a dictionary.
3.424 +
3.425 + if not instattr:
3.426 + self.all_instattr = {}
3.427 + else:
3.428 + self.all_instattr = self._get_attributes(instattr)
3.429 +
3.430 + self.all_instattr_names = self.all_instattr.keys()
3.431 +
3.432 + return self.all_instattr
3.433 +
3.434 + def _get_position_list(self, positions):
3.435 +
3.436 + """
3.437 + Return a list of attribute names for the given 'positions' mapping from
3.438 + names to positions, indicating the positions of the attributes in the
3.439 + final instance structure.
3.440 + """
3.441 +
3.442 + position_items = positions.items()
3.443 + namearray = [None] * len(position_items)
3.444 +
3.445 + # Get the positions in ascending order of list size, with lists
3.446 + # of the same size ordered according to their smallest position
3.447 + # value.
3.448 +
3.449 + position_items.sort(self._cmp_positions)
3.450 +
3.451 + # Get the names in position order.
3.452 +
3.453 + held = []
3.454 +
3.455 + for name, pos in position_items:
3.456 + pos = list(pos)
3.457 + pos.sort()
3.458 + if pos and pos[0] < len(namearray) and namearray[pos[0]] is None:
3.459 + namearray[pos[0]] = name
3.460 + else:
3.461 + if pos:
3.462 + self.relocated.add(name)
3.463 + held.append((name, pos))
3.464 +
3.465 + for i, attr in enumerate(namearray):
3.466 + if attr is None:
3.467 + name, pos = held.pop()
3.468 + namearray[i] = name
3.469 +
3.470 + #print self.name, positions
3.471 + #print "->", namearray
3.472 + return namearray
3.473 +
3.474 + def _get_attributes(self, positions):
3.475 +
3.476 + """
3.477 + For the given 'positions' mapping from names to positions, return a
3.478 + dictionary mapping names to Attr instances incorporating information
3.479 + about their positions in the final instance structure.
3.480 + """
3.481 +
3.482 + d = {}
3.483 + for i, name in enumerate(self._get_position_list(positions)):
3.484 + d[name] = Attr(i, Instance(), name, None)
3.485 + return d
3.486 +
3.487 + def _cmp_positions(self, a, b):
3.488 +
3.489 + "Compare name plus position list operands 'a' and 'b'."
3.490 +
3.491 + name_a, list_a = a
3.492 + name_b, list_b = b
3.493 + if len(list_a) < len(list_b):
3.494 + return -1
3.495 + elif len(list_a) > len(list_b):
3.496 + return 1
3.497 + elif not list_a:
3.498 + return 0
3.499 + else:
3.500 + return cmp(min(list_a), min(list_b))
3.501 +
3.502 + def all_attribute_names(self):
3.503 +
3.504 + """
3.505 + Return the names of all attributes provided by instances of this class.
3.506 + """
3.507 +
3.508 + self.allattr_names = self.allattr_names or self.all_attributes().keys()
3.509 + return self.allattr_names
3.510 +
3.511 + def all_attributes(self):
3.512 +
3.513 + """
3.514 + Return all attributes for an instance, indicating either the class which
3.515 + provides them or that the instance itself provides them.
3.516 + """
3.517 +
3.518 + if self.allattr is None:
3.519 + self.allattr = {}
3.520 + self.allattr.update(self.all_class_attributes())
3.521 + for name, attr in self.instance_attributes().items():
3.522 + if self.allattr.has_key(name):
3.523 + print "Instance attribute %r in %r overrides class attribute." % (name, self)
3.524 + self.allattr[name] = attr
3.525 + return self.allattr
3.526 +
3.527 +class Function(NamespaceDict, Naming):
3.528 +
3.529 + "An inspected function."
3.530 +
3.531 + def __init__(self, name, parent, argnames, defaults, has_star, has_dstar, global_namespace=None, node=None):
3.532 +
3.533 + """
3.534 + Initialise the function with the given 'name', 'parent', list of
3.535 + 'argnames', list of 'defaults', the 'has_star' flag (indicating the
3.536 + presence of a * parameter), the 'has_dstar' flag (indicating the
3.537 + presence of a ** parameter), optional 'global_namespace', and optional
3.538 + AST 'node'.
3.539 + """
3.540 +
3.541 + NamespaceDict.__init__(self, global_namespace)
3.542 + self.name = name
3.543 + self.parent = parent
3.544 + self.argnames = argnames
3.545 + self.defaults = defaults
3.546 + self.has_star = has_star
3.547 + self.has_dstar = has_dstar
3.548 + self.node = node
3.549 +
3.550 + # Initialise the positional names.
3.551 +
3.552 + self.positional_names = self.argnames[:]
3.553 + if has_dstar:
3.554 + self.dstar_name = self.positional_names[-1]
3.555 + del self.positional_names[-1]
3.556 + if has_star:
3.557 + self.star_name = self.positional_names[-1]
3.558 + del self.positional_names[-1]
3.559 +
3.560 + # Initialise default storage.
3.561 + # NOTE: This must be initialised separately due to the reliance on node
3.562 + # NOTE: visiting.
3.563 +
3.564 + self.default_attrs = []
3.565 +
3.566 + # Caches.
3.567 +
3.568 + self.localnames = None # cache for locals
3.569 +
3.570 + # Add parameters to the namespace.
3.571 +
3.572 + self._add_parameters(argnames)
3.573 +
3.574 + # Image generation details.
3.575 +
3.576 + self.location = None
3.577 + self.code_location = None
3.578 +
3.579 + # Program-related details.
3.580 +
3.581 + self.stack_usage = 0
3.582 + self.stack_temp_usage = 0
3.583 + self.stack_local_usage = 0
3.584 +
3.585 + def _add_parameters(self, argnames):
3.586 + for name in argnames:
3.587 + if isinstance(name, tuple):
3.588 + self._add_parameters(name)
3.589 + else:
3.590 + self.set(name, None)
3.591 +
3.592 + def __repr__(self):
3.593 + if self.location is not None:
3.594 + return "Function(%r, %r, %r, %r, %r, %r, location=%r)" % (
3.595 + self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar, self.location
3.596 + )
3.597 + else:
3.598 + return "Function(%r, %r, %r, %r, %r, %r)" % (
3.599 + self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar
3.600 + )
3.601 +
3.602 + def store_default(self, value):
3.603 + attr = Attr(None, self, None, value)
3.604 + attr.update(value, 1)
3.605 + self.default_attrs.append(attr)
3.606 +
3.607 + def make_global(self, name):
3.608 + if name not in self.argnames and not self.has_key(name):
3.609 + self.globals.add(name)
3.610 + else:
3.611 + raise InspectError(self.full_name(), self.node, "Name %r is global and local in %r" % (name, self.full_name()))
3.612 +
3.613 + def parameters(self):
3.614 +
3.615 + """
3.616 + Return a dictionary mapping parameter names to their position in the
3.617 + parameter list.
3.618 + """
3.619 +
3.620 + parameters = {}
3.621 + for i, name in enumerate(self.argnames):
3.622 + parameters[name] = i
3.623 + return parameters
3.624 +
3.625 + def all_locals(self):
3.626 +
3.627 + "Return a dictionary mapping names to local and parameter details."
3.628 +
3.629 + return dict(self)
3.630 +
3.631 + def locals(self):
3.632 +
3.633 + "Return a dictionary mapping names to local details."
3.634 +
3.635 + if self.localnames is None:
3.636 + self.localnames = {}
3.637 + self.localnames.update(self.all_locals())
3.638 + for name in self.argnames:
3.639 + del self.localnames[name]
3.640 + return self.localnames
3.641 +
3.642 + def is_method(self):
3.643 +
3.644 + "Return whether this function is a method."
3.645 +
3.646 + return isinstance(self.parent, Class)
3.647 +
3.648 + def is_relocated(self, name):
3.649 +
3.650 + """
3.651 + Determine whether the given attribute 'name' is relocated for instances
3.652 + having this function as a method.
3.653 + """
3.654 +
3.655 + for cls in self.parent.descendants:
3.656 + if name in cls.relocated:
3.657 + return 1
3.658 + return 0
3.659 +
3.660 + def finalise_attributes(self):
3.661 +
3.662 + """
3.663 + Make sure all attributes (locals) are fully defined. Note that locals
3.664 + are not attributes in the sense of class, module or instance attributes.
3.665 + Defaults are also finalised by this method.
3.666 + """
3.667 +
3.668 + for i, default in enumerate(self.default_attrs):
3.669 + default.position = i
3.670 +
3.671 + i = None
3.672 + for i, name in enumerate(self.argnames):
3.673 + self[name].position = i
3.674 +
3.675 + if i is not None:
3.676 + j = i
3.677 + else:
3.678 + j = 0
3.679 +
3.680 + i = -1
3.681 + for i, attr in enumerate(self.locals().values()):
3.682 + attr.position = i + j
3.683 +
3.684 + self.stack_local_usage = i + 1
3.685 +
3.686 + def function_from_method(self):
3.687 +
3.688 + "Make a function from a method."
3.689 +
3.690 + function = Function(self.name, self.parent, self.argnames[1:], self.defaults,
3.691 + self.has_star, self.has_dstar, self.global_namespace, self.node)
3.692 + function.default_attrs = self.default_attrs
3.693 + return function
3.694 +
3.695 +class UnresolvedName(NamespaceDict):
3.696 +
3.697 + "A module, class or function which was mentioned but could not be imported."
3.698 +
3.699 + def __init__(self, name, parent_name, global_namespace=None):
3.700 + NamespaceDict.__init__(self, global_namespace)
3.701 + self.name = name
3.702 + self.parent_name = parent_name
3.703 +
3.704 + def all_class_attributes(self):
3.705 + return {}
3.706 +
3.707 + def instance_attributes(self):
3.708 + return {}
3.709 +
3.710 + def __repr__(self):
3.711 + return "UnresolvedName(%r, %r)" % (self.name, self.parent_name)
3.712 +
3.713 + def full_name(self):
3.714 + if self.name is not None:
3.715 + return self.parent_name + "." + self.name
3.716 + else:
3.717 + return self.parent_name
3.718 +
3.719 +class Instance:
3.720 +
3.721 + "A placeholder indicating the involvement of an instance."
3.722 +
3.723 + def __repr__(self):
3.724 + return "Instance()"
3.725 +
3.726 +class Module(NamespaceDict):
3.727 +
3.728 + "An inspected module's core details."
3.729 +
3.730 + def __init__(self, name):
3.731 + NamespaceDict.__init__(self, self)
3.732 + self.name = name
3.733 +
3.734 + # Original location details.
3.735 +
3.736 + self.node = None
3.737 +
3.738 + # Complete lists of classes and functions.
3.739 +
3.740 + self.all_objects = set()
3.741 +
3.742 + # Keyword records.
3.743 +
3.744 + self.keyword_names = set()
3.745 +
3.746 + # Image generation details.
3.747 +
3.748 + self.location = None
3.749 + self.code_location = None
3.750 +
3.751 + # Program-related details.
3.752 +
3.753 + self.stack_usage = 0
3.754 + self.stack_temp_usage = 0
3.755 + self.stack_local_usage = 0
3.756 +
3.757 + def full_name(self):
3.758 + return self.name
3.759 +
3.760 + def __repr__(self):
3.761 + if self.location is not None:
3.762 + return "Module(%r, location=%r)" % (self.name, self.location)
3.763 + else:
3.764 + return "Module(%r)" % self.name
3.765 +
3.766 + # Attribute methods.
3.767 +
3.768 + "Return the module attribute names provided by the module."
3.769 +
3.770 + module_attribute_names = NamespaceDict.keys
3.771 +
3.772 + def module_attributes(self):
3.773 +
3.774 + "Return a dictionary mapping names to module attributes."
3.775 +
3.776 + return dict(self)
3.777 +
3.778 +# vim: tabstop=4 expandtab shiftwidth=4
4.1 --- a/micropython/inspect.py Sat May 10 02:32:20 2008 +0200
4.2 +++ b/micropython/inspect.py Sun May 11 18:20:27 2008 +0200
4.3 @@ -17,821 +17,12 @@
4.4
4.5 You should have received a copy of the GNU General Public License along with
4.6 this program. If not, see <http://www.gnu.org/licenses/>.
4.7 -
4.8 ---------
4.9 -
4.10 -The central classes in this module are the following:
4.11 -
4.12 - * Class
4.13 - * Function
4.14 - * Module
4.15 - * InspectedModule (derived from Module)
4.16 -
4.17 -All of the above support the Naming interface either explicitly or through
4.18 -general conformance, meaning that all can be asked to provide their 'full_name'
4.19 -using the method of that name.
4.20 -
4.21 -Additionally, all of the above also support a dictionary interface in order to
4.22 -access names within their defined scopes. Specific methods also exist in order
4.23 -to distinguish between certain kinds of attributes:
4.24 -
4.25 - * Class: (class|all_class|instance|all)_attributes
4.26 - * Function: parameters, locals, all_locals
4.27 - * Module: module_attributes
4.28 -
4.29 -These specific methods are useful in certain situations.
4.30 -
4.31 -The above classes also provide a 'node' attribute, indicating the AST node where
4.32 -each such object is defined.
4.33 """
4.34
4.35 from micropython.common import *
4.36 +from micropython.data import *
4.37 import compiler.ast
4.38 from compiler.visitor import ASTVisitor
4.39 -try:
4.40 - set
4.41 -except NameError:
4.42 - from sets import Set as set
4.43 -
4.44 -class InspectError(ProcessingError): pass
4.45 -
4.46 -class AtLeast:
4.47 -
4.48 - "A special representation for numbers of a given value or greater."
4.49 -
4.50 - def __init__(self, count):
4.51 - self.count = count
4.52 -
4.53 - def __eq__(self, other):
4.54 - return 0
4.55 -
4.56 - __lt__ = __le__ = __eq__
4.57 -
4.58 - def __ne__(self, other):
4.59 - return 1
4.60 -
4.61 - def __gt__(self, other):
4.62 - if isinstance(other, AtLeast):
4.63 - return 0
4.64 - else:
4.65 - return self.count > other
4.66 -
4.67 - def __ge__(self, other):
4.68 - if isinstance(other, AtLeast):
4.69 - return 0
4.70 - else:
4.71 - return self.count >= other
4.72 -
4.73 - def __iadd__(self, other):
4.74 - if isinstance(other, AtLeast):
4.75 - self.count += other.count
4.76 - else:
4.77 - self.count += other
4.78 - return self
4.79 -
4.80 - def __radd__(self, other):
4.81 - if isinstance(other, AtLeast):
4.82 - return AtLeast(self.count + other.count)
4.83 - else:
4.84 - return AtLeast(self.count + other)
4.85 -
4.86 - def __repr__(self):
4.87 - return "AtLeast(%r)" % self.count
4.88 -
4.89 -# Mix-ins and abstract classes.
4.90 -
4.91 -class NamespaceDict:
4.92 -
4.93 - "A mix-in providing dictionary methods."
4.94 -
4.95 - def __init__(self, global_namespace=None):
4.96 - self.namespace = {}
4.97 - self.globals = set()
4.98 - self.global_namespace = global_namespace
4.99 -
4.100 - def __getitem__(self, name):
4.101 - return self.namespace[name]
4.102 -
4.103 - def get(self, name, default=None):
4.104 - return self.namespace.get(name, default)
4.105 -
4.106 - def __setitem__(self, name, value):
4.107 - self.set(name, value)
4.108 -
4.109 - def set(self, name, value, single_assignment=1):
4.110 -
4.111 - """
4.112 - A more powerful set operation, making 'name' refer to 'value' whilst
4.113 - indicating whether a 'single_assignment' (true by default) occurs in
4.114 - this operation (or whether the operation covers potentially many
4.115 - assignments in the lifetime of a program).
4.116 - """
4.117 -
4.118 - if name in self.globals:
4.119 - self.global_namespace.set(name, value, 0)
4.120 - else:
4.121 - attr = self._set(name, value)
4.122 -
4.123 - # NOTE: Insist on assignments with known values.
4.124 -
4.125 - if value is not None:
4.126 - attr.update(value, single_assignment)
4.127 -
4.128 - def set_module(self, name, value):
4.129 -
4.130 - """
4.131 - A specialised set operation, making 'name' refer to 'value' in the
4.132 - context of making a module reference available in association with
4.133 - 'name' as part of the import of that module or a submodule of that
4.134 - module.
4.135 - """
4.136 -
4.137 - attr = self._set(name, value)
4.138 - if attr.assignments is None:
4.139 - attr.assignments = 1
4.140 - attr.assignment_values.add(value)
4.141 -
4.142 - def _set(self, name, value):
4.143 -
4.144 - "The underlying set operation associating 'name' with 'value'."
4.145 -
4.146 - if not self.namespace.has_key(name):
4.147 - self.namespace[name] = Attr(None, self, name, value)
4.148 - return self.namespace[name]
4.149 -
4.150 - def __delitem__(self, name):
4.151 - del self.namespace[name]
4.152 -
4.153 - def has_key(self, name):
4.154 - return self.namespace.has_key(name)
4.155 -
4.156 - def keys(self):
4.157 - return self.namespace.keys()
4.158 -
4.159 - def values(self):
4.160 - return self.namespace.values()
4.161 -
4.162 - def items(self):
4.163 - return self.namespace.items()
4.164 -
4.165 - def make_global(self, name):
4.166 - if not self.namespace.has_key(name):
4.167 - self.globals.add(name)
4.168 - else:
4.169 - raise InspectError(self.full_name(), self.node, "Name %r is both global and local in %r" % (name, self.full_name()))
4.170 -
4.171 - def get_assignments(self, name):
4.172 - if self.assignments.has_key(name):
4.173 - return max(self.assignments[name], len(self.assignment_values[name]))
4.174 - else:
4.175 - return None
4.176 -
4.177 - def attributes_as_list(self):
4.178 - self.finalise_attributes()
4.179 - l = [None] * len(self.keys())
4.180 - for attr in self.values():
4.181 - l[attr.position] = attr
4.182 - return l
4.183 -
4.184 - def finalise_attributes(self):
4.185 -
4.186 - "Make sure all attributes are fully defined."
4.187 -
4.188 - # The default action is to assign attribute positions sequentially.
4.189 -
4.190 - for i, attr in enumerate(self.values()):
4.191 - attr.position = i
4.192 -
4.193 -class Naming:
4.194 -
4.195 - "A mix-in providing naming conveniences."
4.196 -
4.197 - def full_name(self):
4.198 - if self.name is not None:
4.199 - return self.parent.full_name() + "." + self.name
4.200 - else:
4.201 - return self.parent.full_name()
4.202 -
4.203 -# Program data structures.
4.204 -
4.205 -class Attr:
4.206 -
4.207 - "An attribute entry having a parent as context."
4.208 -
4.209 - def __init__(self, position, parent, name, value=None, assignments=None):
4.210 - self.position = position
4.211 - self.parent = parent
4.212 - self.name = name
4.213 - self.value = value
4.214 -
4.215 - # Number of assignments per name.
4.216 -
4.217 - self.assignments = assignments
4.218 - self.assignment_values = set()
4.219 -
4.220 - def update(self, value, single_assignment):
4.221 -
4.222 - """
4.223 - Update the attribute, adding the 'value' provided to the known values
4.224 - associated with the attribute, changing the number of assignments
4.225 - according to the 'single_assignment' status of the operation, where
4.226 - a true value indicates that only one assignment is associated with the
4.227 - update, and a false value indicates that potentially many assignments
4.228 - may be involved.
4.229 - """
4.230 -
4.231 - if self.assignments is None:
4.232 - if single_assignment:
4.233 - self.assignments = 1
4.234 - else:
4.235 - self.assignments = AtLeast(1)
4.236 - else:
4.237 - if single_assignment:
4.238 - self.assignments += 1
4.239 - else:
4.240 - self.assignments += AtLeast(1)
4.241 - self.assignment_values.add(value)
4.242 -
4.243 - def __repr__(self):
4.244 - return "Attr(%r, %r, %r, %r, %r)" % (self.position, self.parent, self.name, self.value, self.assignments)
4.245 -
4.246 -class Const:
4.247 -
4.248 - "A constant object with no context."
4.249 -
4.250 - def __init__(self, value):
4.251 - self.value = value
4.252 -
4.253 - # Image generation details.
4.254 -
4.255 - self.location = None
4.256 -
4.257 - def __repr__(self):
4.258 - if self.location is not None:
4.259 - return "Const(%r, location=%r)" % (self.value, self.location)
4.260 - else:
4.261 - return "Const(%r)" % self.value
4.262 -
4.263 - # Support constants as dictionary keys in order to build constant tables.
4.264 -
4.265 - def __eq__(self, other):
4.266 - return self.value == other.value
4.267 -
4.268 - def __hash__(self):
4.269 - return hash(self.value)
4.270 -
4.271 - def value_type_name(self):
4.272 - return "__builtins__." + self.value.__class__.__name__
4.273 -
4.274 -class Class(NamespaceDict, Naming):
4.275 -
4.276 - "An inspected class."
4.277 -
4.278 - def __init__(self, name, parent, global_namespace=None, node=None):
4.279 -
4.280 - """
4.281 - Initialise the class with the given 'name', 'parent' object, optional
4.282 - 'global_namespace' and optional AST 'node'.
4.283 - """
4.284 -
4.285 - NamespaceDict.__init__(self, global_namespace)
4.286 - self.name = name
4.287 - self.parent = parent
4.288 - self.node = node
4.289 -
4.290 - # Superclasses, descendants and attributes.
4.291 -
4.292 - self.bases = []
4.293 - self.descendants = set()
4.294 - self.instattr = set() # instance attributes
4.295 - self.relocated = set() # attributes which do not have the same
4.296 - # position as those of the same name in
4.297 - # some superclasses
4.298 -
4.299 - # Caches.
4.300 -
4.301 - self.all_instattr = None # cache for instance_attributes
4.302 - self.all_instattr_names = None # from all_instattr
4.303 - self.all_classattr = None # cache for all_class_attributes
4.304 - self.all_classattr_names = None # from all_classattr
4.305 - self.allattr = None # cache for all_attributes
4.306 - self.allattr_names = None # from allattr
4.307 -
4.308 - # Add this class to its attributes.
4.309 -
4.310 - self.set("__class__", self)
4.311 -
4.312 - # Image generation details.
4.313 -
4.314 - self.location = None
4.315 - self.code_location = None
4.316 - self.instantiator = None
4.317 -
4.318 - # Program-related details.
4.319 -
4.320 - self.stack_usage = 0
4.321 - self.stack_temp_usage = 0
4.322 - self.stack_local_usage = 0
4.323 -
4.324 - def __repr__(self):
4.325 - if self.location is not None:
4.326 - return "Class(%r, %r, location=%r)" % (self.name, self.parent, self.location)
4.327 - else:
4.328 - return "Class(%r, %r)" % (self.name, self.parent)
4.329 -
4.330 - def finalise_attributes(self):
4.331 -
4.332 - "Make sure that all attributes are fully defined."
4.333 -
4.334 - self.finalise_class_attributes()
4.335 - self.finalise_instance_attributes()
4.336 -
4.337 - def get_instantiator(self):
4.338 -
4.339 - "Return a function which can be used to instantiate the class."
4.340 -
4.341 - if self.instantiator is None:
4.342 - init_method = self.all_class_attributes()["__init__"].value
4.343 - self.instantiator = init_method.function_from_method()
4.344 - return self.instantiator
4.345 -
4.346 - # Class-specific methods.
4.347 -
4.348 - def add_base(self, base):
4.349 - self.bases.append(base)
4.350 - base.add_descendant(self)
4.351 -
4.352 - def add_instance_attribute(self, name):
4.353 - self.instattr.add(name)
4.354 -
4.355 - def add_descendant(self, cls):
4.356 - self.descendants.add(cls)
4.357 - for base in self.bases:
4.358 - base.add_descendant(cls)
4.359 -
4.360 - "Return the attribute names provided by this class only."
4.361 -
4.362 - class_attribute_names = NamespaceDict.keys
4.363 -
4.364 - def class_attributes(self):
4.365 -
4.366 - "Return class attributes provided by this class only."
4.367 -
4.368 - self.finalise_class_attributes()
4.369 - return dict(self)
4.370 -
4.371 - def all_class_attribute_names(self):
4.372 -
4.373 - "Return the attribute names provided by classes in this hierarchy."
4.374 -
4.375 - if self.all_classattr_names is None:
4.376 - self.all_class_attributes()
4.377 - return self.all_classattr_names
4.378 -
4.379 - def all_class_attributes(self):
4.380 -
4.381 - "Return all class attributes, indicating the class which provides them."
4.382 -
4.383 - self.finalise_class_attributes()
4.384 - return self.all_classattr
4.385 -
4.386 - def finalise_class_attributes(self):
4.387 -
4.388 - "Make sure that the class attributes are fully defined."
4.389 -
4.390 - if self.all_classattr is None:
4.391 - self.all_classattr = {}
4.392 - clsattr = {}
4.393 -
4.394 - # Record provisional position information for attributes of this
4.395 - # class.
4.396 -
4.397 - for name in self.class_attributes().keys():
4.398 - clsattr[name] = set() # position not yet defined
4.399 -
4.400 - reversed_bases = self.bases[:]
4.401 - reversed_bases.reverse()
4.402 -
4.403 - # For the bases in reverse order, acquire class attribute details.
4.404 -
4.405 - for cls in reversed_bases:
4.406 - for name, attr in cls.all_class_attributes().items():
4.407 - self.all_classattr[name] = attr
4.408 -
4.409 - # Record previous attribute information.
4.410 -
4.411 - if clsattr.has_key(name):
4.412 - clsattr[name].add(attr.position)
4.413 -
4.414 - # Record class attributes provided by this class and its bases,
4.415 - # along with their positions.
4.416 -
4.417 - self.all_classattr.update(self.class_attributes())
4.418 -
4.419 - if clsattr:
4.420 - for i, name in enumerate(self._get_position_list(clsattr)):
4.421 - self.all_classattr[name].position = i
4.422 -
4.423 - return self.all_classattr
4.424 -
4.425 - def instance_attribute_names(self):
4.426 -
4.427 - "Return the instance attribute names provided by the class."
4.428 -
4.429 - if self.all_instattr_names is None:
4.430 - self.instance_attributes()
4.431 - return self.all_instattr_names
4.432 -
4.433 - def instance_attributes(self):
4.434 -
4.435 - "Return instance-only attributes for instances of this class."
4.436 -
4.437 - self.finalise_instance_attributes()
4.438 - return self.all_instattr
4.439 -
4.440 - def finalise_instance_attributes(self):
4.441 -
4.442 - "Make sure that the instance attributes are fully defined."
4.443 -
4.444 - if self.all_instattr is None:
4.445 - self.all_instattr = {}
4.446 - instattr = {}
4.447 -
4.448 - # Record provisional position information for attributes of this
4.449 - # instance.
4.450 -
4.451 - for name in self.instattr:
4.452 - instattr[name] = set() # position not yet defined
4.453 -
4.454 - reversed_bases = self.bases[:]
4.455 - reversed_bases.reverse()
4.456 -
4.457 - # For the bases in reverse order, acquire instance attribute
4.458 - # details.
4.459 -
4.460 - for cls in reversed_bases:
4.461 - for name, attr in cls.instance_attributes().items():
4.462 -
4.463 - # Record previous attribute information.
4.464 -
4.465 - if instattr.has_key(name):
4.466 - instattr[name].add(attr.position)
4.467 -
4.468 - # Cache the attributes by converting the positioned attributes into
4.469 - # a dictionary.
4.470 -
4.471 - if not instattr:
4.472 - self.all_instattr = {}
4.473 - else:
4.474 - self.all_instattr = self._get_attributes(instattr)
4.475 -
4.476 - self.all_instattr_names = self.all_instattr.keys()
4.477 -
4.478 - return self.all_instattr
4.479 -
4.480 - def _get_position_list(self, positions):
4.481 -
4.482 - """
4.483 - Return a list of attribute names for the given 'positions' mapping from
4.484 - names to positions, indicating the positions of the attributes in the
4.485 - final instance structure.
4.486 - """
4.487 -
4.488 - position_items = positions.items()
4.489 - namearray = [None] * len(position_items)
4.490 -
4.491 - # Get the positions in ascending order of list size, with lists
4.492 - # of the same size ordered according to their smallest position
4.493 - # value.
4.494 -
4.495 - position_items.sort(self._cmp_positions)
4.496 -
4.497 - # Get the names in position order.
4.498 -
4.499 - held = []
4.500 -
4.501 - for name, pos in position_items:
4.502 - pos = list(pos)
4.503 - pos.sort()
4.504 - if pos and pos[0] < len(namearray) and namearray[pos[0]] is None:
4.505 - namearray[pos[0]] = name
4.506 - else:
4.507 - if pos:
4.508 - self.relocated.add(name)
4.509 - held.append((name, pos))
4.510 -
4.511 - for i, attr in enumerate(namearray):
4.512 - if attr is None:
4.513 - name, pos = held.pop()
4.514 - namearray[i] = name
4.515 -
4.516 - #print self.name, positions
4.517 - #print "->", namearray
4.518 - return namearray
4.519 -
4.520 - def _get_attributes(self, positions):
4.521 -
4.522 - """
4.523 - For the given 'positions' mapping from names to positions, return a
4.524 - dictionary mapping names to Attr instances incorporating information
4.525 - about their positions in the final instance structure.
4.526 - """
4.527 -
4.528 - d = {}
4.529 - for i, name in enumerate(self._get_position_list(positions)):
4.530 - d[name] = Attr(i, Instance(), name, None)
4.531 - return d
4.532 -
4.533 - def _cmp_positions(self, a, b):
4.534 -
4.535 - "Compare name plus position list operands 'a' and 'b'."
4.536 -
4.537 - name_a, list_a = a
4.538 - name_b, list_b = b
4.539 - if len(list_a) < len(list_b):
4.540 - return -1
4.541 - elif len(list_a) > len(list_b):
4.542 - return 1
4.543 - elif not list_a:
4.544 - return 0
4.545 - else:
4.546 - return cmp(min(list_a), min(list_b))
4.547 -
4.548 - def all_attribute_names(self):
4.549 -
4.550 - """
4.551 - Return the names of all attributes provided by instances of this class.
4.552 - """
4.553 -
4.554 - self.allattr_names = self.allattr_names or self.all_attributes().keys()
4.555 - return self.allattr_names
4.556 -
4.557 - def all_attributes(self):
4.558 -
4.559 - """
4.560 - Return all attributes for an instance, indicating either the class which
4.561 - provides them or that the instance itself provides them.
4.562 - """
4.563 -
4.564 - if self.allattr is None:
4.565 - self.allattr = {}
4.566 - self.allattr.update(self.all_class_attributes())
4.567 - for name, attr in self.instance_attributes().items():
4.568 - if self.allattr.has_key(name):
4.569 - print "Instance attribute %r in %r overrides class attribute." % (name, self)
4.570 - self.allattr[name] = attr
4.571 - return self.allattr
4.572 -
4.573 -class Function(NamespaceDict, Naming):
4.574 -
4.575 - "An inspected function."
4.576 -
4.577 - def __init__(self, name, parent, argnames, defaults, has_star, has_dstar, global_namespace=None, node=None):
4.578 -
4.579 - """
4.580 - Initialise the function with the given 'name', 'parent', list of
4.581 - 'argnames', list of 'defaults', the 'has_star' flag (indicating the
4.582 - presence of a * parameter), the 'has_dstar' flag (indicating the
4.583 - presence of a ** parameter), optional 'global_namespace', and optional
4.584 - AST 'node'.
4.585 - """
4.586 -
4.587 - NamespaceDict.__init__(self, global_namespace)
4.588 - self.name = name
4.589 - self.parent = parent
4.590 - self.argnames = argnames
4.591 - self.defaults = defaults
4.592 - self.has_star = has_star
4.593 - self.has_dstar = has_dstar
4.594 - self.node = node
4.595 -
4.596 - # Initialise the positional names.
4.597 -
4.598 - self.positional_names = self.argnames[:]
4.599 - if has_dstar:
4.600 - self.dstar_name = self.positional_names[-1]
4.601 - del self.positional_names[-1]
4.602 - if has_star:
4.603 - self.star_name = self.positional_names[-1]
4.604 - del self.positional_names[-1]
4.605 -
4.606 - # Initialise default storage.
4.607 - # NOTE: This must be initialised separately due to the reliance on node
4.608 - # NOTE: visiting.
4.609 -
4.610 - self.default_attrs = []
4.611 -
4.612 - # Caches.
4.613 -
4.614 - self.localnames = None # cache for locals
4.615 -
4.616 - # Add parameters to the namespace.
4.617 -
4.618 - self._add_parameters(argnames)
4.619 -
4.620 - # Image generation details.
4.621 -
4.622 - self.location = None
4.623 - self.code_location = None
4.624 -
4.625 - # Program-related details.
4.626 -
4.627 - self.stack_usage = 0
4.628 - self.stack_temp_usage = 0
4.629 - self.stack_local_usage = 0
4.630 -
4.631 - def _add_parameters(self, argnames):
4.632 - for name in argnames:
4.633 - if isinstance(name, tuple):
4.634 - self._add_parameters(name)
4.635 - else:
4.636 - self.set(name, None)
4.637 -
4.638 - def __repr__(self):
4.639 - if self.location is not None:
4.640 - return "Function(%r, %r, %r, %r, %r, %r, location=%r)" % (
4.641 - self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar, self.location
4.642 - )
4.643 - else:
4.644 - return "Function(%r, %r, %r, %r, %r, %r)" % (
4.645 - self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar
4.646 - )
4.647 -
4.648 - def store_default(self, value):
4.649 - attr = Attr(None, self, None, value)
4.650 - attr.update(value, 1)
4.651 - self.default_attrs.append(attr)
4.652 -
4.653 - def make_global(self, name):
4.654 - if name not in self.argnames and not self.has_key(name):
4.655 - self.globals.add(name)
4.656 - else:
4.657 - raise InspectError(self.full_name(), self.node, "Name %r is global and local in %r" % (name, self.full_name()))
4.658 -
4.659 - def parameters(self):
4.660 -
4.661 - """
4.662 - Return a dictionary mapping parameter names to their position in the
4.663 - parameter list.
4.664 - """
4.665 -
4.666 - parameters = {}
4.667 - for i, name in enumerate(self.argnames):
4.668 - parameters[name] = i
4.669 - return parameters
4.670 -
4.671 - def all_locals(self):
4.672 -
4.673 - "Return a dictionary mapping names to local and parameter details."
4.674 -
4.675 - return dict(self)
4.676 -
4.677 - def locals(self):
4.678 -
4.679 - "Return a dictionary mapping names to local details."
4.680 -
4.681 - if self.localnames is None:
4.682 - self.localnames = {}
4.683 - self.localnames.update(self.all_locals())
4.684 - for name in self.argnames:
4.685 - del self.localnames[name]
4.686 - return self.localnames
4.687 -
4.688 - def is_method(self):
4.689 -
4.690 - "Return whether this function is a method."
4.691 -
4.692 - return isinstance(self.parent, Class)
4.693 -
4.694 - def is_relocated(self, name):
4.695 -
4.696 - """
4.697 - Determine whether the given attribute 'name' is relocated for instances
4.698 - having this function as a method.
4.699 - """
4.700 -
4.701 - for cls in self.parent.descendants:
4.702 - if name in cls.relocated:
4.703 - return 1
4.704 - return 0
4.705 -
4.706 - def finalise_attributes(self):
4.707 -
4.708 - """
4.709 - Make sure all attributes (locals) are fully defined. Note that locals
4.710 - are not attributes in the sense of class, module or instance attributes.
4.711 - Defaults are also finalised by this method.
4.712 - """
4.713 -
4.714 - for i, default in enumerate(self.default_attrs):
4.715 - default.position = i
4.716 -
4.717 - i = None
4.718 - for i, name in enumerate(self.argnames):
4.719 - self[name].position = i
4.720 -
4.721 - if i is not None:
4.722 - j = i
4.723 - else:
4.724 - j = 0
4.725 -
4.726 - i = -1
4.727 - for i, attr in enumerate(self.locals().values()):
4.728 - attr.position = i + j
4.729 -
4.730 - self.stack_local_usage = i + 1
4.731 -
4.732 - def function_from_method(self):
4.733 -
4.734 - "Make a function from a method."
4.735 -
4.736 - function = Function(self.name, self.parent, self.argnames[1:], self.defaults,
4.737 - self.has_star, self.has_dstar, self.global_namespace, self.node)
4.738 - function.default_attrs = self.default_attrs
4.739 - return function
4.740 -
4.741 -class UnresolvedName(NamespaceDict):
4.742 -
4.743 - "A module, class or function which was mentioned but could not be imported."
4.744 -
4.745 - def __init__(self, name, parent_name, global_namespace=None):
4.746 - NamespaceDict.__init__(self, global_namespace)
4.747 - self.name = name
4.748 - self.parent_name = parent_name
4.749 -
4.750 - def all_class_attributes(self):
4.751 - return {}
4.752 -
4.753 - def instance_attributes(self):
4.754 - return {}
4.755 -
4.756 - def __repr__(self):
4.757 - return "UnresolvedName(%r, %r)" % (self.name, self.parent_name)
4.758 -
4.759 - def full_name(self):
4.760 - if self.name is not None:
4.761 - return self.parent_name + "." + self.name
4.762 - else:
4.763 - return self.parent_name
4.764 -
4.765 -class Instance:
4.766 -
4.767 - "A placeholder indicating the involvement of an instance."
4.768 -
4.769 - def __repr__(self):
4.770 - return "Instance()"
4.771 -
4.772 -class Module(NamespaceDict):
4.773 -
4.774 - "An inspected module's core details."
4.775 -
4.776 - def __init__(self, name):
4.777 - NamespaceDict.__init__(self, self)
4.778 - self.name = name
4.779 -
4.780 - # Original location details.
4.781 -
4.782 - self.node = None
4.783 -
4.784 - # Complete lists of classes and functions.
4.785 -
4.786 - self.all_objects = set()
4.787 -
4.788 - # Keyword records.
4.789 -
4.790 - self.keyword_names = set()
4.791 -
4.792 - # Image generation details.
4.793 -
4.794 - self.location = None
4.795 - self.code_location = None
4.796 -
4.797 - # Program-related details.
4.798 -
4.799 - self.stack_usage = 0
4.800 - self.stack_temp_usage = 0
4.801 - self.stack_local_usage = 0
4.802 -
4.803 - def full_name(self):
4.804 - return self.name
4.805 -
4.806 - def __repr__(self):
4.807 - if self.location is not None:
4.808 - return "Module(%r, location=%r)" % (self.name, self.location)
4.809 - else:
4.810 - return "Module(%r)" % self.name
4.811 -
4.812 - # Attribute methods.
4.813 -
4.814 - "Return the module attribute names provided by the module."
4.815 -
4.816 - module_attribute_names = NamespaceDict.keys
4.817 -
4.818 - def module_attributes(self):
4.819 -
4.820 - "Return a dictionary mapping names to module attributes."
4.821 -
4.822 - return dict(self)
4.823
4.824 # Program visitors.
4.825