# HG changeset patch # User Paul Boddie # Date 1210522827 -7200 # Node ID 96c5304f2242add5b5ae7f545b746a2e01a8eede # Parent 619142384781e8b5f603058f76750a283645b3f5 Moved program data classes into micropython.data and some common classes into the common module from the inspect module. diff -r 619142384781 -r 96c5304f2242 micropython/ast.py --- a/micropython/ast.py Sat May 10 02:32:20 2008 +0200 +++ b/micropython/ast.py Sun May 11 18:20:27 2008 +0200 @@ -19,17 +19,11 @@ this program. If not, see . """ -import micropython.inspect +from micropython.common import * +from micropython.data import * from micropython.rsvp import * -from micropython.common import * import compiler.ast from compiler.visitor import ASTVisitor -try: - set -except NameError: - from sets import Set as set - -class TranslateError(NodeProcessingError): pass class Label: @@ -264,12 +258,12 @@ # Get the details of the access. - if isinstance(last.attr, micropython.inspect.Const): + if isinstance(last.attr, Const): target_name = last.attr.value_type_name() else: target = last.attr.value - if isinstance(target, micropython.inspect.Const): + if isinstance(target, Const): target_name = target.value_type_name() else: target_name = target.full_name() @@ -527,11 +521,11 @@ if scope == "local": unit = self.unit - if isinstance(unit, micropython.inspect.Function): + if isinstance(unit, Function): self.new_op(NameInstruction(unit.all_locals()[name])) - elif isinstance(unit, micropython.inspect.Class): + elif isinstance(unit, Class): self.new_op(AddressInstruction(unit.all_class_attributes()[name])) - elif isinstance(unit, micropython.inspect.Module): + elif isinstance(unit, Module): self.new_op(AddressInstruction(unit.module_attributes()[name])) else: raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r." % (unit, name)) @@ -575,14 +569,14 @@ def _have_constant_input(self, n): last = self.last_ops(n+1) return len(last) > n and (isinstance(last[n], LoadAddress) and last[n].attr.assignments == 1 or - isinstance(last[n], LoadConst)) # and not isinstance(last[n].attr, micropython.inspect.Instance) + isinstance(last[n], LoadConst)) # and not isinstance(last[n].attr, Instance) def _have_known_target(self): return self._have_constant_input(0) def _have_self_input(self): last = self.last_op() - return isinstance(self.unit, micropython.inspect.Function) and \ + return isinstance(self.unit, Function) and \ self.unit.is_method() and isinstance(last, LoadName) and \ last.attr.name == "self" @@ -669,9 +663,9 @@ # Handle calls to classes. - if isinstance(target, micropython.inspect.Class): + if isinstance(target, Class): target = target.get_instantiator() - context = micropython.inspect.Instance() + context = Instance() # A special context is chosen to avoid generating unnecessary # context loading and checking instructions. diff -r 619142384781 -r 96c5304f2242 micropython/common.py --- a/micropython/common.py Sat May 10 02:32:20 2008 +0200 +++ b/micropython/common.py Sun May 11 18:20:27 2008 +0200 @@ -19,12 +19,25 @@ this program. If not, see . """ +try: + set +except NameError: + from sets import Set as set + +# Errors. + class ProcessingError(Exception): "A processing error." pass +class InspectError(ProcessingError): + + "An error during the module inspection process." + + pass + class NodeProcessingError(ProcessingError): "A processing error associated with a particular program node." @@ -40,4 +53,65 @@ def __str__(self): return repr(self) +class TranslateError(NodeProcessingError): + + "An error during the module translation process." + + pass + +# Inspection representations. + +class AtLeast: + + "A special representation for numbers of a given value or greater." + + def __init__(self, count): + self.count = count + + def __eq__(self, other): + return 0 + + __lt__ = __le__ = __eq__ + + def __ne__(self, other): + return 1 + + def __gt__(self, other): + if isinstance(other, AtLeast): + return 0 + else: + return self.count > other + + def __ge__(self, other): + if isinstance(other, AtLeast): + return 0 + else: + return self.count >= other + + def __iadd__(self, other): + if isinstance(other, AtLeast): + self.count += other.count + else: + self.count += other + return self + + def __radd__(self, other): + if isinstance(other, AtLeast): + return AtLeast(self.count + other.count) + else: + return AtLeast(self.count + other) + + def __repr__(self): + return "AtLeast(%r)" % self.count + +class Naming: + + "A mix-in providing naming conveniences." + + def full_name(self): + if self.name is not None: + return self.parent.full_name() + "." + self.name + else: + return self.parent.full_name() + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 619142384781 -r 96c5304f2242 micropython/data.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micropython/data.py Sun May 11 18:20:27 2008 +0200 @@ -0,0 +1,775 @@ +#!/usr/bin/env python + +""" +Data classes. + +Copyright (C) 2007, 2008 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . + +-------- + +The central classes in this module are the following: + + * Class + * Function + * Module + * InspectedModule (derived from Module) + +All of the above support the Naming interface either explicitly or through +general conformance, meaning that all can be asked to provide their 'full_name' +using the method of that name. + +Additionally, all of the above also support a dictionary interface in order to +access names within their defined scopes. Specific methods also exist in order +to distinguish between certain kinds of attributes: + + * Class: (class|all_class|instance|all)_attributes + * Function: parameters, locals, all_locals + * Module: module_attributes + +These specific methods are useful in certain situations. + +The above classes also provide a 'node' attribute, indicating the AST node where +each such object is defined. +""" + +from micropython.common import * + +# Mix-ins and abstract classes. + +class NamespaceDict: + + "A mix-in providing dictionary methods." + + def __init__(self, global_namespace=None): + self.namespace = {} + self.globals = set() + self.global_namespace = global_namespace + + def __getitem__(self, name): + return self.namespace[name] + + def get(self, name, default=None): + return self.namespace.get(name, default) + + def __setitem__(self, name, value): + self.set(name, value) + + def set(self, name, value, single_assignment=1): + + """ + A more powerful set operation, making 'name' refer to 'value' whilst + indicating whether a 'single_assignment' (true by default) occurs in + this operation (or whether the operation covers potentially many + assignments in the lifetime of a program). + """ + + if name in self.globals: + self.global_namespace.set(name, value, 0) + else: + attr = self._set(name, value) + + # NOTE: Insist on assignments with known values. + + if value is not None: + attr.update(value, single_assignment) + + def set_module(self, name, value): + + """ + A specialised set operation, making 'name' refer to 'value' in the + context of making a module reference available in association with + 'name' as part of the import of that module or a submodule of that + module. + """ + + attr = self._set(name, value) + if attr.assignments is None: + attr.assignments = 1 + attr.assignment_values.add(value) + + def _set(self, name, value): + + "The underlying set operation associating 'name' with 'value'." + + if not self.namespace.has_key(name): + self.namespace[name] = Attr(None, self, name, value) + return self.namespace[name] + + def __delitem__(self, name): + del self.namespace[name] + + def has_key(self, name): + return self.namespace.has_key(name) + + def keys(self): + return self.namespace.keys() + + def values(self): + return self.namespace.values() + + def items(self): + return self.namespace.items() + + def make_global(self, name): + if not self.namespace.has_key(name): + self.globals.add(name) + else: + raise InspectError(self.full_name(), self.node, "Name %r is both global and local in %r" % (name, self.full_name())) + + def get_assignments(self, name): + if self.assignments.has_key(name): + return max(self.assignments[name], len(self.assignment_values[name])) + else: + return None + + def attributes_as_list(self): + self.finalise_attributes() + l = [None] * len(self.keys()) + for attr in self.values(): + l[attr.position] = attr + return l + + def finalise_attributes(self): + + "Make sure all attributes are fully defined." + + # The default action is to assign attribute positions sequentially. + + for i, attr in enumerate(self.values()): + attr.position = i + +# Program data structures. + +class Attr: + + "An attribute entry having a parent as context." + + def __init__(self, position, parent, name, value=None, assignments=None): + self.position = position + self.parent = parent + self.name = name + self.value = value + + # Number of assignments per name. + + self.assignments = assignments + self.assignment_values = set() + + def update(self, value, single_assignment): + + """ + Update the attribute, adding the 'value' provided to the known values + associated with the attribute, changing the number of assignments + according to the 'single_assignment' status of the operation, where + a true value indicates that only one assignment is associated with the + update, and a false value indicates that potentially many assignments + may be involved. + """ + + if self.assignments is None: + if single_assignment: + self.assignments = 1 + else: + self.assignments = AtLeast(1) + else: + if single_assignment: + self.assignments += 1 + else: + self.assignments += AtLeast(1) + self.assignment_values.add(value) + + def __repr__(self): + return "Attr(%r, %r, %r, %r, %r)" % (self.position, self.parent, self.name, self.value, self.assignments) + +class Const: + + "A constant object with no context." + + def __init__(self, value): + self.value = value + + # Image generation details. + + self.location = None + + def __repr__(self): + if self.location is not None: + return "Const(%r, location=%r)" % (self.value, self.location) + else: + return "Const(%r)" % self.value + + # Support constants as dictionary keys in order to build constant tables. + + def __eq__(self, other): + return self.value == other.value + + def __hash__(self): + return hash(self.value) + + def value_type_name(self): + return "__builtins__." + self.value.__class__.__name__ + +class Class(NamespaceDict, Naming): + + "An inspected class." + + def __init__(self, name, parent, global_namespace=None, node=None): + + """ + Initialise the class with the given 'name', 'parent' object, optional + 'global_namespace' and optional AST 'node'. + """ + + NamespaceDict.__init__(self, global_namespace) + self.name = name + self.parent = parent + self.node = node + + # Superclasses, descendants and attributes. + + self.bases = [] + self.descendants = set() + self.instattr = set() # instance attributes + self.relocated = set() # attributes which do not have the same + # position as those of the same name in + # some superclasses + + # Caches. + + self.all_instattr = None # cache for instance_attributes + self.all_instattr_names = None # from all_instattr + self.all_classattr = None # cache for all_class_attributes + self.all_classattr_names = None # from all_classattr + self.allattr = None # cache for all_attributes + self.allattr_names = None # from allattr + + # Add this class to its attributes. + + self.set("__class__", self) + + # Image generation details. + + self.location = None + self.code_location = None + self.instantiator = None + + # Program-related details. + + self.stack_usage = 0 + self.stack_temp_usage = 0 + self.stack_local_usage = 0 + + def __repr__(self): + if self.location is not None: + return "Class(%r, %r, location=%r)" % (self.name, self.parent, self.location) + else: + return "Class(%r, %r)" % (self.name, self.parent) + + def finalise_attributes(self): + + "Make sure that all attributes are fully defined." + + self.finalise_class_attributes() + self.finalise_instance_attributes() + + def get_instantiator(self): + + "Return a function which can be used to instantiate the class." + + if self.instantiator is None: + init_method = self.all_class_attributes()["__init__"].value + self.instantiator = init_method.function_from_method() + return self.instantiator + + # Class-specific methods. + + def add_base(self, base): + self.bases.append(base) + base.add_descendant(self) + + def add_instance_attribute(self, name): + self.instattr.add(name) + + def add_descendant(self, cls): + self.descendants.add(cls) + for base in self.bases: + base.add_descendant(cls) + + "Return the attribute names provided by this class only." + + class_attribute_names = NamespaceDict.keys + + def class_attributes(self): + + "Return class attributes provided by this class only." + + self.finalise_class_attributes() + return dict(self) + + def all_class_attribute_names(self): + + "Return the attribute names provided by classes in this hierarchy." + + if self.all_classattr_names is None: + self.all_class_attributes() + return self.all_classattr_names + + def all_class_attributes(self): + + "Return all class attributes, indicating the class which provides them." + + self.finalise_class_attributes() + return self.all_classattr + + def finalise_class_attributes(self): + + "Make sure that the class attributes are fully defined." + + if self.all_classattr is None: + self.all_classattr = {} + clsattr = {} + + # Record provisional position information for attributes of this + # class. + + for name in self.class_attributes().keys(): + clsattr[name] = set() # position not yet defined + + reversed_bases = self.bases[:] + reversed_bases.reverse() + + # For the bases in reverse order, acquire class attribute details. + + for cls in reversed_bases: + for name, attr in cls.all_class_attributes().items(): + self.all_classattr[name] = attr + + # Record previous attribute information. + + if clsattr.has_key(name): + clsattr[name].add(attr.position) + + # Record class attributes provided by this class and its bases, + # along with their positions. + + self.all_classattr.update(self.class_attributes()) + + if clsattr: + for i, name in enumerate(self._get_position_list(clsattr)): + self.all_classattr[name].position = i + + return self.all_classattr + + def instance_attribute_names(self): + + "Return the instance attribute names provided by the class." + + if self.all_instattr_names is None: + self.instance_attributes() + return self.all_instattr_names + + def instance_attributes(self): + + "Return instance-only attributes for instances of this class." + + self.finalise_instance_attributes() + return self.all_instattr + + def finalise_instance_attributes(self): + + "Make sure that the instance attributes are fully defined." + + if self.all_instattr is None: + self.all_instattr = {} + instattr = {} + + # Record provisional position information for attributes of this + # instance. + + for name in self.instattr: + instattr[name] = set() # position not yet defined + + reversed_bases = self.bases[:] + reversed_bases.reverse() + + # For the bases in reverse order, acquire instance attribute + # details. + + for cls in reversed_bases: + for name, attr in cls.instance_attributes().items(): + + # Record previous attribute information. + + if instattr.has_key(name): + instattr[name].add(attr.position) + + # Cache the attributes by converting the positioned attributes into + # a dictionary. + + if not instattr: + self.all_instattr = {} + else: + self.all_instattr = self._get_attributes(instattr) + + self.all_instattr_names = self.all_instattr.keys() + + return self.all_instattr + + def _get_position_list(self, positions): + + """ + Return a list of attribute names for the given 'positions' mapping from + names to positions, indicating the positions of the attributes in the + final instance structure. + """ + + position_items = positions.items() + namearray = [None] * len(position_items) + + # Get the positions in ascending order of list size, with lists + # of the same size ordered according to their smallest position + # value. + + position_items.sort(self._cmp_positions) + + # Get the names in position order. + + held = [] + + for name, pos in position_items: + pos = list(pos) + pos.sort() + if pos and pos[0] < len(namearray) and namearray[pos[0]] is None: + namearray[pos[0]] = name + else: + if pos: + self.relocated.add(name) + held.append((name, pos)) + + for i, attr in enumerate(namearray): + if attr is None: + name, pos = held.pop() + namearray[i] = name + + #print self.name, positions + #print "->", namearray + return namearray + + def _get_attributes(self, positions): + + """ + For the given 'positions' mapping from names to positions, return a + dictionary mapping names to Attr instances incorporating information + about their positions in the final instance structure. + """ + + d = {} + for i, name in enumerate(self._get_position_list(positions)): + d[name] = Attr(i, Instance(), name, None) + return d + + def _cmp_positions(self, a, b): + + "Compare name plus position list operands 'a' and 'b'." + + name_a, list_a = a + name_b, list_b = b + if len(list_a) < len(list_b): + return -1 + elif len(list_a) > len(list_b): + return 1 + elif not list_a: + return 0 + else: + return cmp(min(list_a), min(list_b)) + + def all_attribute_names(self): + + """ + Return the names of all attributes provided by instances of this class. + """ + + self.allattr_names = self.allattr_names or self.all_attributes().keys() + return self.allattr_names + + def all_attributes(self): + + """ + Return all attributes for an instance, indicating either the class which + provides them or that the instance itself provides them. + """ + + if self.allattr is None: + self.allattr = {} + self.allattr.update(self.all_class_attributes()) + for name, attr in self.instance_attributes().items(): + if self.allattr.has_key(name): + print "Instance attribute %r in %r overrides class attribute." % (name, self) + self.allattr[name] = attr + return self.allattr + +class Function(NamespaceDict, Naming): + + "An inspected function." + + def __init__(self, name, parent, argnames, defaults, has_star, has_dstar, global_namespace=None, node=None): + + """ + Initialise the function with the given 'name', 'parent', list of + 'argnames', list of 'defaults', the 'has_star' flag (indicating the + presence of a * parameter), the 'has_dstar' flag (indicating the + presence of a ** parameter), optional 'global_namespace', and optional + AST 'node'. + """ + + NamespaceDict.__init__(self, global_namespace) + self.name = name + self.parent = parent + self.argnames = argnames + self.defaults = defaults + self.has_star = has_star + self.has_dstar = has_dstar + self.node = node + + # Initialise the positional names. + + self.positional_names = self.argnames[:] + if has_dstar: + self.dstar_name = self.positional_names[-1] + del self.positional_names[-1] + if has_star: + self.star_name = self.positional_names[-1] + del self.positional_names[-1] + + # Initialise default storage. + # NOTE: This must be initialised separately due to the reliance on node + # NOTE: visiting. + + self.default_attrs = [] + + # Caches. + + self.localnames = None # cache for locals + + # Add parameters to the namespace. + + self._add_parameters(argnames) + + # Image generation details. + + self.location = None + self.code_location = None + + # Program-related details. + + self.stack_usage = 0 + self.stack_temp_usage = 0 + self.stack_local_usage = 0 + + def _add_parameters(self, argnames): + for name in argnames: + if isinstance(name, tuple): + self._add_parameters(name) + else: + self.set(name, None) + + def __repr__(self): + if self.location is not None: + return "Function(%r, %r, %r, %r, %r, %r, location=%r)" % ( + self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar, self.location + ) + else: + return "Function(%r, %r, %r, %r, %r, %r)" % ( + self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar + ) + + def store_default(self, value): + attr = Attr(None, self, None, value) + attr.update(value, 1) + self.default_attrs.append(attr) + + def make_global(self, name): + if name not in self.argnames and not self.has_key(name): + self.globals.add(name) + else: + raise InspectError(self.full_name(), self.node, "Name %r is global and local in %r" % (name, self.full_name())) + + def parameters(self): + + """ + Return a dictionary mapping parameter names to their position in the + parameter list. + """ + + parameters = {} + for i, name in enumerate(self.argnames): + parameters[name] = i + return parameters + + def all_locals(self): + + "Return a dictionary mapping names to local and parameter details." + + return dict(self) + + def locals(self): + + "Return a dictionary mapping names to local details." + + if self.localnames is None: + self.localnames = {} + self.localnames.update(self.all_locals()) + for name in self.argnames: + del self.localnames[name] + return self.localnames + + def is_method(self): + + "Return whether this function is a method." + + return isinstance(self.parent, Class) + + def is_relocated(self, name): + + """ + Determine whether the given attribute 'name' is relocated for instances + having this function as a method. + """ + + for cls in self.parent.descendants: + if name in cls.relocated: + return 1 + return 0 + + def finalise_attributes(self): + + """ + Make sure all attributes (locals) are fully defined. Note that locals + are not attributes in the sense of class, module or instance attributes. + Defaults are also finalised by this method. + """ + + for i, default in enumerate(self.default_attrs): + default.position = i + + i = None + for i, name in enumerate(self.argnames): + self[name].position = i + + if i is not None: + j = i + else: + j = 0 + + i = -1 + for i, attr in enumerate(self.locals().values()): + attr.position = i + j + + self.stack_local_usage = i + 1 + + def function_from_method(self): + + "Make a function from a method." + + function = Function(self.name, self.parent, self.argnames[1:], self.defaults, + self.has_star, self.has_dstar, self.global_namespace, self.node) + function.default_attrs = self.default_attrs + return function + +class UnresolvedName(NamespaceDict): + + "A module, class or function which was mentioned but could not be imported." + + def __init__(self, name, parent_name, global_namespace=None): + NamespaceDict.__init__(self, global_namespace) + self.name = name + self.parent_name = parent_name + + def all_class_attributes(self): + return {} + + def instance_attributes(self): + return {} + + def __repr__(self): + return "UnresolvedName(%r, %r)" % (self.name, self.parent_name) + + def full_name(self): + if self.name is not None: + return self.parent_name + "." + self.name + else: + return self.parent_name + +class Instance: + + "A placeholder indicating the involvement of an instance." + + def __repr__(self): + return "Instance()" + +class Module(NamespaceDict): + + "An inspected module's core details." + + def __init__(self, name): + NamespaceDict.__init__(self, self) + self.name = name + + # Original location details. + + self.node = None + + # Complete lists of classes and functions. + + self.all_objects = set() + + # Keyword records. + + self.keyword_names = set() + + # Image generation details. + + self.location = None + self.code_location = None + + # Program-related details. + + self.stack_usage = 0 + self.stack_temp_usage = 0 + self.stack_local_usage = 0 + + def full_name(self): + return self.name + + def __repr__(self): + if self.location is not None: + return "Module(%r, location=%r)" % (self.name, self.location) + else: + return "Module(%r)" % self.name + + # Attribute methods. + + "Return the module attribute names provided by the module." + + module_attribute_names = NamespaceDict.keys + + def module_attributes(self): + + "Return a dictionary mapping names to module attributes." + + return dict(self) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 619142384781 -r 96c5304f2242 micropython/inspect.py --- a/micropython/inspect.py Sat May 10 02:32:20 2008 +0200 +++ b/micropython/inspect.py Sun May 11 18:20:27 2008 +0200 @@ -17,821 +17,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - --------- - -The central classes in this module are the following: - - * Class - * Function - * Module - * InspectedModule (derived from Module) - -All of the above support the Naming interface either explicitly or through -general conformance, meaning that all can be asked to provide their 'full_name' -using the method of that name. - -Additionally, all of the above also support a dictionary interface in order to -access names within their defined scopes. Specific methods also exist in order -to distinguish between certain kinds of attributes: - - * Class: (class|all_class|instance|all)_attributes - * Function: parameters, locals, all_locals - * Module: module_attributes - -These specific methods are useful in certain situations. - -The above classes also provide a 'node' attribute, indicating the AST node where -each such object is defined. """ from micropython.common import * +from micropython.data import * import compiler.ast from compiler.visitor import ASTVisitor -try: - set -except NameError: - from sets import Set as set - -class InspectError(ProcessingError): pass - -class AtLeast: - - "A special representation for numbers of a given value or greater." - - def __init__(self, count): - self.count = count - - def __eq__(self, other): - return 0 - - __lt__ = __le__ = __eq__ - - def __ne__(self, other): - return 1 - - def __gt__(self, other): - if isinstance(other, AtLeast): - return 0 - else: - return self.count > other - - def __ge__(self, other): - if isinstance(other, AtLeast): - return 0 - else: - return self.count >= other - - def __iadd__(self, other): - if isinstance(other, AtLeast): - self.count += other.count - else: - self.count += other - return self - - def __radd__(self, other): - if isinstance(other, AtLeast): - return AtLeast(self.count + other.count) - else: - return AtLeast(self.count + other) - - def __repr__(self): - return "AtLeast(%r)" % self.count - -# Mix-ins and abstract classes. - -class NamespaceDict: - - "A mix-in providing dictionary methods." - - def __init__(self, global_namespace=None): - self.namespace = {} - self.globals = set() - self.global_namespace = global_namespace - - def __getitem__(self, name): - return self.namespace[name] - - def get(self, name, default=None): - return self.namespace.get(name, default) - - def __setitem__(self, name, value): - self.set(name, value) - - def set(self, name, value, single_assignment=1): - - """ - A more powerful set operation, making 'name' refer to 'value' whilst - indicating whether a 'single_assignment' (true by default) occurs in - this operation (or whether the operation covers potentially many - assignments in the lifetime of a program). - """ - - if name in self.globals: - self.global_namespace.set(name, value, 0) - else: - attr = self._set(name, value) - - # NOTE: Insist on assignments with known values. - - if value is not None: - attr.update(value, single_assignment) - - def set_module(self, name, value): - - """ - A specialised set operation, making 'name' refer to 'value' in the - context of making a module reference available in association with - 'name' as part of the import of that module or a submodule of that - module. - """ - - attr = self._set(name, value) - if attr.assignments is None: - attr.assignments = 1 - attr.assignment_values.add(value) - - def _set(self, name, value): - - "The underlying set operation associating 'name' with 'value'." - - if not self.namespace.has_key(name): - self.namespace[name] = Attr(None, self, name, value) - return self.namespace[name] - - def __delitem__(self, name): - del self.namespace[name] - - def has_key(self, name): - return self.namespace.has_key(name) - - def keys(self): - return self.namespace.keys() - - def values(self): - return self.namespace.values() - - def items(self): - return self.namespace.items() - - def make_global(self, name): - if not self.namespace.has_key(name): - self.globals.add(name) - else: - raise InspectError(self.full_name(), self.node, "Name %r is both global and local in %r" % (name, self.full_name())) - - def get_assignments(self, name): - if self.assignments.has_key(name): - return max(self.assignments[name], len(self.assignment_values[name])) - else: - return None - - def attributes_as_list(self): - self.finalise_attributes() - l = [None] * len(self.keys()) - for attr in self.values(): - l[attr.position] = attr - return l - - def finalise_attributes(self): - - "Make sure all attributes are fully defined." - - # The default action is to assign attribute positions sequentially. - - for i, attr in enumerate(self.values()): - attr.position = i - -class Naming: - - "A mix-in providing naming conveniences." - - def full_name(self): - if self.name is not None: - return self.parent.full_name() + "." + self.name - else: - return self.parent.full_name() - -# Program data structures. - -class Attr: - - "An attribute entry having a parent as context." - - def __init__(self, position, parent, name, value=None, assignments=None): - self.position = position - self.parent = parent - self.name = name - self.value = value - - # Number of assignments per name. - - self.assignments = assignments - self.assignment_values = set() - - def update(self, value, single_assignment): - - """ - Update the attribute, adding the 'value' provided to the known values - associated with the attribute, changing the number of assignments - according to the 'single_assignment' status of the operation, where - a true value indicates that only one assignment is associated with the - update, and a false value indicates that potentially many assignments - may be involved. - """ - - if self.assignments is None: - if single_assignment: - self.assignments = 1 - else: - self.assignments = AtLeast(1) - else: - if single_assignment: - self.assignments += 1 - else: - self.assignments += AtLeast(1) - self.assignment_values.add(value) - - def __repr__(self): - return "Attr(%r, %r, %r, %r, %r)" % (self.position, self.parent, self.name, self.value, self.assignments) - -class Const: - - "A constant object with no context." - - def __init__(self, value): - self.value = value - - # Image generation details. - - self.location = None - - def __repr__(self): - if self.location is not None: - return "Const(%r, location=%r)" % (self.value, self.location) - else: - return "Const(%r)" % self.value - - # Support constants as dictionary keys in order to build constant tables. - - def __eq__(self, other): - return self.value == other.value - - def __hash__(self): - return hash(self.value) - - def value_type_name(self): - return "__builtins__." + self.value.__class__.__name__ - -class Class(NamespaceDict, Naming): - - "An inspected class." - - def __init__(self, name, parent, global_namespace=None, node=None): - - """ - Initialise the class with the given 'name', 'parent' object, optional - 'global_namespace' and optional AST 'node'. - """ - - NamespaceDict.__init__(self, global_namespace) - self.name = name - self.parent = parent - self.node = node - - # Superclasses, descendants and attributes. - - self.bases = [] - self.descendants = set() - self.instattr = set() # instance attributes - self.relocated = set() # attributes which do not have the same - # position as those of the same name in - # some superclasses - - # Caches. - - self.all_instattr = None # cache for instance_attributes - self.all_instattr_names = None # from all_instattr - self.all_classattr = None # cache for all_class_attributes - self.all_classattr_names = None # from all_classattr - self.allattr = None # cache for all_attributes - self.allattr_names = None # from allattr - - # Add this class to its attributes. - - self.set("__class__", self) - - # Image generation details. - - self.location = None - self.code_location = None - self.instantiator = None - - # Program-related details. - - self.stack_usage = 0 - self.stack_temp_usage = 0 - self.stack_local_usage = 0 - - def __repr__(self): - if self.location is not None: - return "Class(%r, %r, location=%r)" % (self.name, self.parent, self.location) - else: - return "Class(%r, %r)" % (self.name, self.parent) - - def finalise_attributes(self): - - "Make sure that all attributes are fully defined." - - self.finalise_class_attributes() - self.finalise_instance_attributes() - - def get_instantiator(self): - - "Return a function which can be used to instantiate the class." - - if self.instantiator is None: - init_method = self.all_class_attributes()["__init__"].value - self.instantiator = init_method.function_from_method() - return self.instantiator - - # Class-specific methods. - - def add_base(self, base): - self.bases.append(base) - base.add_descendant(self) - - def add_instance_attribute(self, name): - self.instattr.add(name) - - def add_descendant(self, cls): - self.descendants.add(cls) - for base in self.bases: - base.add_descendant(cls) - - "Return the attribute names provided by this class only." - - class_attribute_names = NamespaceDict.keys - - def class_attributes(self): - - "Return class attributes provided by this class only." - - self.finalise_class_attributes() - return dict(self) - - def all_class_attribute_names(self): - - "Return the attribute names provided by classes in this hierarchy." - - if self.all_classattr_names is None: - self.all_class_attributes() - return self.all_classattr_names - - def all_class_attributes(self): - - "Return all class attributes, indicating the class which provides them." - - self.finalise_class_attributes() - return self.all_classattr - - def finalise_class_attributes(self): - - "Make sure that the class attributes are fully defined." - - if self.all_classattr is None: - self.all_classattr = {} - clsattr = {} - - # Record provisional position information for attributes of this - # class. - - for name in self.class_attributes().keys(): - clsattr[name] = set() # position not yet defined - - reversed_bases = self.bases[:] - reversed_bases.reverse() - - # For the bases in reverse order, acquire class attribute details. - - for cls in reversed_bases: - for name, attr in cls.all_class_attributes().items(): - self.all_classattr[name] = attr - - # Record previous attribute information. - - if clsattr.has_key(name): - clsattr[name].add(attr.position) - - # Record class attributes provided by this class and its bases, - # along with their positions. - - self.all_classattr.update(self.class_attributes()) - - if clsattr: - for i, name in enumerate(self._get_position_list(clsattr)): - self.all_classattr[name].position = i - - return self.all_classattr - - def instance_attribute_names(self): - - "Return the instance attribute names provided by the class." - - if self.all_instattr_names is None: - self.instance_attributes() - return self.all_instattr_names - - def instance_attributes(self): - - "Return instance-only attributes for instances of this class." - - self.finalise_instance_attributes() - return self.all_instattr - - def finalise_instance_attributes(self): - - "Make sure that the instance attributes are fully defined." - - if self.all_instattr is None: - self.all_instattr = {} - instattr = {} - - # Record provisional position information for attributes of this - # instance. - - for name in self.instattr: - instattr[name] = set() # position not yet defined - - reversed_bases = self.bases[:] - reversed_bases.reverse() - - # For the bases in reverse order, acquire instance attribute - # details. - - for cls in reversed_bases: - for name, attr in cls.instance_attributes().items(): - - # Record previous attribute information. - - if instattr.has_key(name): - instattr[name].add(attr.position) - - # Cache the attributes by converting the positioned attributes into - # a dictionary. - - if not instattr: - self.all_instattr = {} - else: - self.all_instattr = self._get_attributes(instattr) - - self.all_instattr_names = self.all_instattr.keys() - - return self.all_instattr - - def _get_position_list(self, positions): - - """ - Return a list of attribute names for the given 'positions' mapping from - names to positions, indicating the positions of the attributes in the - final instance structure. - """ - - position_items = positions.items() - namearray = [None] * len(position_items) - - # Get the positions in ascending order of list size, with lists - # of the same size ordered according to their smallest position - # value. - - position_items.sort(self._cmp_positions) - - # Get the names in position order. - - held = [] - - for name, pos in position_items: - pos = list(pos) - pos.sort() - if pos and pos[0] < len(namearray) and namearray[pos[0]] is None: - namearray[pos[0]] = name - else: - if pos: - self.relocated.add(name) - held.append((name, pos)) - - for i, attr in enumerate(namearray): - if attr is None: - name, pos = held.pop() - namearray[i] = name - - #print self.name, positions - #print "->", namearray - return namearray - - def _get_attributes(self, positions): - - """ - For the given 'positions' mapping from names to positions, return a - dictionary mapping names to Attr instances incorporating information - about their positions in the final instance structure. - """ - - d = {} - for i, name in enumerate(self._get_position_list(positions)): - d[name] = Attr(i, Instance(), name, None) - return d - - def _cmp_positions(self, a, b): - - "Compare name plus position list operands 'a' and 'b'." - - name_a, list_a = a - name_b, list_b = b - if len(list_a) < len(list_b): - return -1 - elif len(list_a) > len(list_b): - return 1 - elif not list_a: - return 0 - else: - return cmp(min(list_a), min(list_b)) - - def all_attribute_names(self): - - """ - Return the names of all attributes provided by instances of this class. - """ - - self.allattr_names = self.allattr_names or self.all_attributes().keys() - return self.allattr_names - - def all_attributes(self): - - """ - Return all attributes for an instance, indicating either the class which - provides them or that the instance itself provides them. - """ - - if self.allattr is None: - self.allattr = {} - self.allattr.update(self.all_class_attributes()) - for name, attr in self.instance_attributes().items(): - if self.allattr.has_key(name): - print "Instance attribute %r in %r overrides class attribute." % (name, self) - self.allattr[name] = attr - return self.allattr - -class Function(NamespaceDict, Naming): - - "An inspected function." - - def __init__(self, name, parent, argnames, defaults, has_star, has_dstar, global_namespace=None, node=None): - - """ - Initialise the function with the given 'name', 'parent', list of - 'argnames', list of 'defaults', the 'has_star' flag (indicating the - presence of a * parameter), the 'has_dstar' flag (indicating the - presence of a ** parameter), optional 'global_namespace', and optional - AST 'node'. - """ - - NamespaceDict.__init__(self, global_namespace) - self.name = name - self.parent = parent - self.argnames = argnames - self.defaults = defaults - self.has_star = has_star - self.has_dstar = has_dstar - self.node = node - - # Initialise the positional names. - - self.positional_names = self.argnames[:] - if has_dstar: - self.dstar_name = self.positional_names[-1] - del self.positional_names[-1] - if has_star: - self.star_name = self.positional_names[-1] - del self.positional_names[-1] - - # Initialise default storage. - # NOTE: This must be initialised separately due to the reliance on node - # NOTE: visiting. - - self.default_attrs = [] - - # Caches. - - self.localnames = None # cache for locals - - # Add parameters to the namespace. - - self._add_parameters(argnames) - - # Image generation details. - - self.location = None - self.code_location = None - - # Program-related details. - - self.stack_usage = 0 - self.stack_temp_usage = 0 - self.stack_local_usage = 0 - - def _add_parameters(self, argnames): - for name in argnames: - if isinstance(name, tuple): - self._add_parameters(name) - else: - self.set(name, None) - - def __repr__(self): - if self.location is not None: - return "Function(%r, %r, %r, %r, %r, %r, location=%r)" % ( - self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar, self.location - ) - else: - return "Function(%r, %r, %r, %r, %r, %r)" % ( - self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar - ) - - def store_default(self, value): - attr = Attr(None, self, None, value) - attr.update(value, 1) - self.default_attrs.append(attr) - - def make_global(self, name): - if name not in self.argnames and not self.has_key(name): - self.globals.add(name) - else: - raise InspectError(self.full_name(), self.node, "Name %r is global and local in %r" % (name, self.full_name())) - - def parameters(self): - - """ - Return a dictionary mapping parameter names to their position in the - parameter list. - """ - - parameters = {} - for i, name in enumerate(self.argnames): - parameters[name] = i - return parameters - - def all_locals(self): - - "Return a dictionary mapping names to local and parameter details." - - return dict(self) - - def locals(self): - - "Return a dictionary mapping names to local details." - - if self.localnames is None: - self.localnames = {} - self.localnames.update(self.all_locals()) - for name in self.argnames: - del self.localnames[name] - return self.localnames - - def is_method(self): - - "Return whether this function is a method." - - return isinstance(self.parent, Class) - - def is_relocated(self, name): - - """ - Determine whether the given attribute 'name' is relocated for instances - having this function as a method. - """ - - for cls in self.parent.descendants: - if name in cls.relocated: - return 1 - return 0 - - def finalise_attributes(self): - - """ - Make sure all attributes (locals) are fully defined. Note that locals - are not attributes in the sense of class, module or instance attributes. - Defaults are also finalised by this method. - """ - - for i, default in enumerate(self.default_attrs): - default.position = i - - i = None - for i, name in enumerate(self.argnames): - self[name].position = i - - if i is not None: - j = i - else: - j = 0 - - i = -1 - for i, attr in enumerate(self.locals().values()): - attr.position = i + j - - self.stack_local_usage = i + 1 - - def function_from_method(self): - - "Make a function from a method." - - function = Function(self.name, self.parent, self.argnames[1:], self.defaults, - self.has_star, self.has_dstar, self.global_namespace, self.node) - function.default_attrs = self.default_attrs - return function - -class UnresolvedName(NamespaceDict): - - "A module, class or function which was mentioned but could not be imported." - - def __init__(self, name, parent_name, global_namespace=None): - NamespaceDict.__init__(self, global_namespace) - self.name = name - self.parent_name = parent_name - - def all_class_attributes(self): - return {} - - def instance_attributes(self): - return {} - - def __repr__(self): - return "UnresolvedName(%r, %r)" % (self.name, self.parent_name) - - def full_name(self): - if self.name is not None: - return self.parent_name + "." + self.name - else: - return self.parent_name - -class Instance: - - "A placeholder indicating the involvement of an instance." - - def __repr__(self): - return "Instance()" - -class Module(NamespaceDict): - - "An inspected module's core details." - - def __init__(self, name): - NamespaceDict.__init__(self, self) - self.name = name - - # Original location details. - - self.node = None - - # Complete lists of classes and functions. - - self.all_objects = set() - - # Keyword records. - - self.keyword_names = set() - - # Image generation details. - - self.location = None - self.code_location = None - - # Program-related details. - - self.stack_usage = 0 - self.stack_temp_usage = 0 - self.stack_local_usage = 0 - - def full_name(self): - return self.name - - def __repr__(self): - if self.location is not None: - return "Module(%r, location=%r)" % (self.name, self.location) - else: - return "Module(%r)" % self.name - - # Attribute methods. - - "Return the module attribute names provided by the module." - - module_attribute_names = NamespaceDict.keys - - def module_attributes(self): - - "Return a dictionary mapping names to module attributes." - - return dict(self) # Program visitors.