# HG changeset patch # User Paul Boddie # Date 1235948695 -3600 # Node ID 2066cb2e583ed5ebae2bbde2d9d51986e3a7d27a # Parent 55c8334f8ef15e24049a3a03a6aa8490fbb8bab5 Changed the Attr class to manage contexts and values together, replacing the exposed value attribute with a get_value method (also supported by Const); this complicates the test for an attribute being defined within a class hierarchy since all contexts must be tested. Updated the NamespaceDict class to update namespace entries according to the revised Attr interface. Updated the inspection code to permit the use of Attr objects when updating namespaces, since the context information is important in such operations. diff -r 55c8334f8ef1 -r 2066cb2e583e micropython/ast.py --- a/micropython/ast.py Sat Feb 28 19:40:15 2009 +0100 +++ b/micropython/ast.py Mon Mar 02 00:04:55 2009 +0100 @@ -228,7 +228,7 @@ "Return the built-in class with the given 'name' for the given 'node'." - return self.get_builtin(name, node).value + return self.get_builtin(name, node).get_value() def get_builtin(self, name, node): @@ -1226,7 +1226,7 @@ self.dispatch(n) self.record_value() self.new_op(temp) - self.new_op(StoreAttr(Attr(i, None, None, None))) + self.new_op(StoreAttr(Attr(i, None, None))) self.set_source() self.discard_value() diff -r 55c8334f8ef1 -r 2066cb2e583e micropython/data.py --- a/micropython/data.py Sat Feb 28 19:40:15 2009 +0100 +++ b/micropython/data.py Mon Mar 02 00:04:55 2009 +0100 @@ -100,8 +100,7 @@ if name in self.globals: self.module.set(name, value, 0) else: - attr = self._set(name, value) - attr.update(value, single_assignment) + self._set(name, value, single_assignment) def set_module(self, name, value): @@ -112,52 +111,56 @@ module. """ - attr = self._set(name, value) - if attr.assignments is None: - attr.assignments = 1 - attr.assignment_values.add(attr.value) + self._set(name, value, 1) - def _set(self, name, value): + def _set(self, name, attr_or_value, single_assignment=1): """ - The underlying set operation associating 'name' with 'value'. + The underlying set operation associating 'name' with the given + 'attr_or_value'. See: docs/assignment.txt """ - if not self.namespace.has_key(name): + # Add and/or obtain the namespace entry. - # Either accept the attribute as specified. + if not self.namespace.has_key(name): + self.namespace[name] = Attr(None, self, name) - if isinstance(value, Attr): - if value.context is not None: - self.namespace[name] = Attr(None, self, value.context, name, value.value) - return self.namespace[name] + attr = self.namespace[name] - # Or accept the value of the attribute. + # Handle attribute assignment as well as assignment of basic objects. + + if isinstance(attr_or_value, Attr): - else: - value = value.value + # Attempt to fix the context if not explicitly defined. - # Then/or attempt to fix the context. + context_values = self.get_updated_context_values(attr_or_value.context_values) - context = self._context(value) - self.namespace[name] = Attr(None, self, context, name, value) + else: + context_values = self.get_updated_context_values([(None, attr_or_value)]) - return self.namespace[name] + attr.update(context_values, single_assignment) - def _context(self, value): + def get_updated_context_values(self, context_values): """ - Return the context to be used when storing the given 'value'. + Adapt the contexts found in the given 'context_values', returning a new + set. See: docs/assignment.txt """ - # Set the context of instances to themselves. + results = set() + + for context, value in context_values: + + # Set the context of instances to themselves. - if isinstance(value, Instance): - return value - else: - return None + if isinstance(value, Instance): + results.add((value, value)) + else: + results.add((context, value)) + + return results def make_global(self, name): if not self.namespace.has_key(name): @@ -166,12 +169,6 @@ else: return 0 - 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): "Return the attributes in a list." @@ -206,45 +203,52 @@ "An attribute entry having a context." - def __init__(self, position, parent, context, name, value=None, assignments=None): + def __init__(self, position, parent, name): """ Initialise the attribute with the given 'position' within the collection - of attributes of its 'parent', indicating the 'context' or origin of the - attribute (where it was first defined), along with its 'name'. - - An optional 'value' indicates the typical contents of the attribute, and - the optional number of 'assignments' may be used to determine whether - the attribute is effectively constant. + of attributes of its 'parent', indicating its 'name'. """ self.position = position self.parent = parent - self.context = context self.name = name - self.value = value + + self.context_values = set() # Number of assignments per name. - self.assignments = assignments - self.assignment_values = set() + self.assignments = None def set_referenced(self): "Indicate that the contents are referenced via a namespace." - for value in self.assignment_values: - value.set_referenced() + for value in self.get_values(): + if value is not None: + value.set_referenced() + + def get_contexts(self): + return [c for (c, v) in self.context_values] - def update(self, value, single_assignment): + def get_values(self): + return [v for (c, v) in self.context_values] + + def get_context(self): + return len(self.context_values) == 1 and self.get_contexts()[0] or None + + def get_value(self): + return len(self.context_values) == 1 and self.get_values()[0] or None + + def update(self, context_values, 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. + Update the attribute, adding the 'context_values' provided to the + known details 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: @@ -258,36 +262,7 @@ else: self.assignments += AtLeast(1) - if value is not None: - self.assignment_values.add(value) - - def via_instance(self): - - """ - Return either this attribute or a replacement where it is being accessed - via an instance. - """ - - if self.context is not None: - - # Check compatibility of the context with the parent. - # Where the attribute originates within the same hierarchy, use an - # instance as the context. - - if self.defined_within_hierarchy(): - context = Instance() - - # Otherwise, preserve the existing context. - - else: - context = self.context - - return Attr(self.position, self.parent, context, self.name, self.value, self.assignments) - - # Unknown contexts remain in use. - - else: - return self + self.context_values.update(context_values) def is_class_attribute(self): return isinstance(self.parent, Class) @@ -299,22 +274,47 @@ same class hierarchy. """ - return isinstance(self.parent, Class) and isinstance(self.context, Class) and ( - self.context is self.parent or - self.context.has_subclass(self.parent) or - self.parent.has_subclass(self.context)) + # Must be defined within a class. + + if isinstance(self.parent, Class): + + # To be sure, all contexts must be classes and be the same as the + # parent, or be a superclass of the parent, or be a subclass of the + # parent. + + for context in self.get_contexts(): + if not ( + isinstance(context, Class) and ( + context is self.parent or + context.has_subclass(self.parent) or + self.parent.has_subclass(context)) + ): + return 0 + + return 1 + + # Instance attributes are not defined within a hierarchy. + + else: + return 0 def __repr__(self): - return "Attr(%r, %s, %s, %r, %s, %r)" % ( - self.position, shortrepr(self.parent), shortrepr(self.context), - self.name, shortrepr(self.value), self.assignments + return "Attr(%r, %s, %r) # [%s], %r" % ( + self.position, shortrepr(self.parent), self.name, + self._context_values_str(), self.assignments ) + def _context_values_str(self): + l = [] + for (c, v) in self.context_values: + l.append("(c=%s, v=%s)" % (shortrepr(c), shortrepr(v))) + return ", ".join(l) + def as_raw(self, objtable, paramtable): return [ ( - self.context and self.context.location, - self.value and self.value.location + self.get_context() and self.get_context().location, + self.get_value() and self.get_value().location ) ] @@ -357,6 +357,9 @@ Instance.__init__(self) self.value = value + def get_value(self): + return value + def __repr__(self): if self.location is not None: return "Const(%r, location=%r)" % (self.value, self.location) @@ -388,7 +391,7 @@ # Support constants as dictionary keys in order to build constant tables. def __eq__(self, other): - return self.value == other.value and self.value.__class__ is other.value.__class__ + return other is not None and self.value == other.value and self.value.__class__ is other.value.__class__ def __hash__(self): return hash(self.value) @@ -431,10 +434,6 @@ 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 @@ -449,6 +448,10 @@ self.local_usage = 0 self.all_local_usage = 0 + # Add this class to its attributes. + + self.set("__class__", self) + def set_referenced(self): self.referenced = 1 @@ -468,7 +471,8 @@ # Append a template of an instance for use when instantiating classes. call_method = self.get("__call__") - call_method_code_location = call_method and call_method.value.code_location + call_method_value = call_method and call_method.get_value() + call_method_code_location = call_method_value and call_method_value.code_location # NOTE: The instantiator code is the first block of the class. @@ -478,8 +482,8 @@ DataObject( classcode, attrcode, call_method_code_location, ( - call_method and len(call_method.value.positional_names), - call_method and len(call_method.value.defaults) + call_method_value and len(call_method_value.positional_names), + call_method_value and len(call_method_value.defaults) ), 1, self.full_name() @@ -497,26 +501,26 @@ # Namespace-related methods. - def _context(self, value): + def get_updated_context_values(self, context_values): """ - Return the context to be used when storing the given 'value'. + Adapt the contexts found in the given 'context_values', returning a new + set. See: docs/assignment.txt """ - if value is not None: + results = set() + + for context, value in context_values: # Change the ownership of functions. - if isinstance(value, Function): - context = value.parent + if context is None and value is not None and isinstance(value, Function): + results.add((self, value)) + else: + results.add((context, value)) - if isinstance(context, Module): - return self - else: - return context - - return NamespaceDict._context(self, value) + return NamespaceDict.get_updated_context_values(self, results) def finalise_attributes(self): @@ -540,7 +544,7 @@ return self.instantiator def get_init_method(self): - return self.all_class_attributes()["__init__"].value + return self.all_class_attributes()["__init__"].get_value() # Class-specific methods. @@ -734,7 +738,7 @@ d = {} for i, name in enumerate(self._get_position_list(positions)): - d[name] = Attr(i, Instance(), None, name, None) + d[name] = Attr(i, Instance(), name) return d def _cmp_positions(self, a, b): @@ -881,8 +885,8 @@ # Namespace-related methods. def store_default(self, value): - attr = Attr(None, self, None, None, value) - attr.update(value, 1) + attr = Attr(None, self, None) + attr.update([(None, value)], 1) self.default_attrs.append(attr) def make_global(self, name): diff -r 55c8334f8ef1 -r 2066cb2e583e micropython/inspect.py --- a/micropython/inspect.py Sat Feb 28 19:40:15 2009 +0100 +++ b/micropython/inspect.py Mon Mar 02 00:04:55 2009 +0100 @@ -152,19 +152,17 @@ # employed in the final program. if isinstance(value, Attr): - attr_value = value.value # Only remove entries for classes and functions, not methods. - if attr_value is not None: - for attr_value in value.assignment_values: - if (isinstance(attr_value, Function) and not attr_value.is_method() or - isinstance(attr_value, Class)) and not attr_value.referenced: - pass - else: - break + for attr_value in value.get_values(): + if (isinstance(attr_value, Function) and not attr_value.is_method() or + isinstance(attr_value, Class)) and not attr_value.referenced: + pass else: - del self[name] + break + else: + del self[name] # Complain about globals not initialised at the module level. @@ -195,10 +193,10 @@ # assigned object to the class and are unreferenced. if name not in self.importer.names_used and \ - attr.assignments == 1 and isinstance(attr.value, Function) and \ - attr.value.is_method() and not attr.value.referenced: + attr.assignments == 1 and isinstance(attr.get_value(), Function) and \ + attr.get_value().is_method() and not attr.get_value().referenced: - self.all_objects.remove(attr.value) + self.all_objects.remove(attr.get_value()) del obj[name] def finalise(self): @@ -248,12 +246,7 @@ expression. """ - if isinstance(self.expr, Attr): - assigned_value = self.expr.value - else: - assigned_value = self.expr - - module.set(name, assigned_value, 0) + module.set(name, self.expr, 0) def store_class_attr(self, name): @@ -263,13 +256,7 @@ """ if self.in_method and self.namespaces[-2].has_key(name): - - if isinstance(self.expr, Attr): - assigned_value = self.expr.value - else: - assigned_value = self.expr - - self.namespaces[-2].set(name, assigned_value, 0) + self.namespaces[-2].set(name, self.expr, 0) return 1 return 0 @@ -353,10 +340,7 @@ for n in node.defaults: self.expr = self.dispatch(n) - if isinstance(self.expr, Attr): - function.store_default(self.expr.value) - else: - function.store_default(self.expr) + function.store_default(self.expr) # Enter the function. @@ -416,9 +400,9 @@ if expr.name == "self": if not self.store_class_attr(node.attrname): self.store_instance_attr(node.attrname) - elif isinstance(expr.value, Module): - self.store_module_attr(node.attrname, expr.value) - print "Warning: attribute %r of module %r set outside the module." % (node.attrname, expr.value.name) + elif isinstance(expr.get_value(), Module): + self.store_module_attr(node.attrname, expr.get_value()) + print "Warning: attribute %r of module %r set outside the module." % (node.attrname, expr.get_value().name) return None def visitAssList(self, node): @@ -478,7 +462,7 @@ raise InspectError(self.full_name(), node, "Base class %r for %r is not constant." % (base, cls.full_name())) else: - cls.add_base(expr.value) + cls.add_base(expr.get_value()) else: # if expr is None: raise InspectError(self.full_name(), node, "Base class %r for %r is not found: it may be hidden in some way." % (base, cls.full_name())) @@ -488,7 +472,7 @@ if not node.bases and not (self.name == "__builtins__" and node.name == "object") : expr = self.dispatch(compiler.ast.Name("object")) - cls.add_base(expr.value) + cls.add_base(expr.get_value()) # Make a back reference from the node for code generation. @@ -573,9 +557,9 @@ if name != "*": if module is not None and module.namespace.has_key(name): attr = module[name] - self.store(alias or name, attr.value) - if isinstance(attr.value, Module) and not attr.value.loaded: - self.importer.load(attr.value.name) + self.store(alias or name, attr) + if isinstance(attr.get_value(), Module) and not attr.get_value().loaded: + self.importer.load(attr.get_value().name) # Support the import of names from missing modules. @@ -585,9 +569,9 @@ if module is not None: for n in module.namespace.keys(): attr = module[n] - self.store(n, attr.value) - if isinstance(attr.value, Module) and not attr.value.loaded: - self.importer.load(attr.value.name) + self.store(n, attr) + if isinstance(attr.get_value(), Module) and not attr.get_value().loaded: + self.importer.load(attr.get_value().name) return None @@ -607,7 +591,7 @@ attrname = node.attrname if isinstance(expr, Attr): - value = expr.value + value = expr.get_value() if isinstance(value, (Class, Module)): attr = value.namespace.get(attrname) elif isinstance(value, UnresolvedName): diff -r 55c8334f8ef1 -r 2066cb2e583e micropython/opt.py --- a/micropython/opt.py Sat Feb 28 19:40:15 2009 +0100 +++ b/micropython/opt.py Mon Mar 02 00:04:55 2009 +0100 @@ -307,9 +307,11 @@ if isinstance(value.attr, Const): target_name = value.attr.value_type_name() else: - target = value.attr.value + target = value.attr.get_value() - if isinstance(target, Const): + if target is None: + return None # no clearly defined target + elif isinstance(target, Const): return target.value_type_name() elif isinstance(target, Instance): return None # skip production of optimised code @@ -329,12 +331,15 @@ if self.should_optimise_known_target() and self.have_known_target(): value = self.active_value - target = value.attr.value - context = value.attr.context + target = value.attr.get_value() + context = value.attr.get_context() + + # Return target details only if this is clearly defined. - return target, context - else: - return None + if target is not None: + return target, context + + return None def optimise_self_access(self, unit, attrname): diff -r 55c8334f8ef1 -r 2066cb2e583e rsvp.py --- a/rsvp.py Sat Feb 28 19:40:15 2009 +0100 +++ b/rsvp.py Mon Mar 02 00:04:55 2009 +0100 @@ -111,13 +111,13 @@ # Constants. - self.attr_error = objlist.access("__builtins__", "AttributeError").value.location - self.type_error = objlist.access("__builtins__", "TypeError").value.location + self.attr_error = objlist.access("__builtins__", "AttributeError").get_value().location + self.type_error = objlist.access("__builtins__", "TypeError").get_value().location # Native class constants. cls = objlist.access("__builtins__", "int") - self.int_instance_location = cls and cls.value.instance_template_location + self.int_instance_location = cls and cls.get_value() and cls.get_value().instance_template_location # Debugging methods.