# HG changeset patch # User Paul Boddie # Date 1212868524 -7200 # Node ID dd32f05e3a8c9f63fd0d654b20f887f79f85f56e # Parent b1ef174ec4a23aa3b60885f54689cdc317dbd868 Renamed the node attribute to astnode in order to avoid attribute naming conflicts with the compiler.ast classes. Improved default argument loading by using LoadAddress. Added some documentation to the inspect module. Changed the inspection process so that instance attributes are detected in all methods belonging to a class, not just the __init__ method. Changed constant definition so that constants are not redefined using new objects. Fixed attribute assignment during inspection to respect the presence of class attributes when considering possible instance attribute assignment. Added a name function to the micropython.rsvp module in order to simplify __repr__ methods. Added more tests. diff -r b1ef174ec4a2 -r dd32f05e3a8c micropython/ast.py --- a/micropython/ast.py Mon May 19 00:22:00 2008 +0200 +++ b/micropython/ast.py Sat Jun 07 21:55:24 2008 +0200 @@ -105,8 +105,8 @@ self.code = [] self.temp_position = self.unit.stack_local_usage - if unit.node is not None: - self.dispatch(unit.node) + if unit.astnode is not None: + self.dispatch(unit.astnode) self.calculate_stack_usage() return self.code @@ -511,14 +511,15 @@ for pos in range(nargs_min, nargs_max): if pos not in employed_positions: - self.new_op(LoadConst(target)) - self.new_op(LoadAttr(target.default_attrs[pos - nargs_min])) + #self.new_op(LoadConst(target)) + #self.new_op(LoadAttr(target.default_attrs[pos - nargs_min])) + self.new_op(LoadAddress(target.default_attrs[pos - nargs_min])) - # If the position corresponds to the current frame element, - # skip generating the instruction. + # If the position corresponds to the current frame element, + # skip generating the instruction. - if frame_pos != pos: - self.new_op(StoreFrame(pos)) + if frame_pos != pos: + self.new_op(StoreFrame(pos)) frame_pos += 1 diff -r b1ef174ec4a2 -r dd32f05e3a8c micropython/common.py --- a/micropython/common.py Mon May 19 00:22:00 2008 +0200 +++ b/micropython/common.py Sat Jun 07 21:55:24 2008 +0200 @@ -50,11 +50,11 @@ def __init__(self, unit_name, node, message): self.unit_name = unit_name - self.node = node + self.astnode = node self.message = message def __repr__(self): - return "Error in %r at line %d: %s" % (self.unit_name, self.node.lineno, self.message) + return "Error in %r at line %d: %s" % (self.unit_name, self.astnode.lineno, self.message) def __str__(self): return repr(self) diff -r b1ef174ec4a2 -r dd32f05e3a8c micropython/data.py --- a/micropython/data.py Mon May 19 00:22:00 2008 +0200 +++ b/micropython/data.py Sat Jun 07 21:55:24 2008 +0200 @@ -146,7 +146,7 @@ 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())) + raise InspectError(self.full_name(), self.astnode, "Name %r is both global and local in %r" % (name, self.full_name())) def get_assignments(self, name): if self.assignments.has_key(name): @@ -319,7 +319,7 @@ NamespaceDict.__init__(self, global_namespace) self.name = name self.parent = parent - self.node = node + self.astnode = node # Superclasses, descendants and attributes. @@ -641,7 +641,7 @@ self.defaults = defaults self.has_star = has_star self.has_dstar = has_dstar - self.node = node + self.astnode = node # Initialise the positional names. @@ -709,7 +709,7 @@ 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())) + raise InspectError(self.full_name(), self.astnode, "Name %r is global and local in %r" % (name, self.full_name())) def parameters(self): @@ -789,7 +789,7 @@ "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) + self.has_star, self.has_dstar, self.global_namespace, self.astnode) function.default_attrs = self.default_attrs return function @@ -831,7 +831,7 @@ # Original location details. - self.node = None + self.astnode = None # Complete lists of classes and functions. diff -r b1ef174ec4a2 -r dd32f05e3a8c micropython/inspect.py --- a/micropython/inspect.py Mon May 19 00:22:00 2008 +0200 +++ b/micropython/inspect.py Sat Jun 07 21:55:24 2008 +0200 @@ -17,6 +17,58 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . + +-------- + +The results of inspecting a module are as follows: + +Constants +--------- + +All constants identified within the code shall be registered. + +Classes +------- + +All global classes shall be registered; local classes (within functions) or +nested classes (within classes) are not currently registered. + +Base classes must be detected and constant. + +All classes without bases are made to inherit from __builtins__.object in order +to support some standard methods. + +Functions +--------- + +All functions and lambda definitions shall be registered. + +Namespaces +---------- + +Modules define their own "global" namespace, within which classes, functions +and lambda definitions establish a hierarchy of namespaces. + +Only local, global and built-in namespaces are recognised; closures are not +supported. + +Assignments +----------- + +Name assignment and attribute assignment involving modules and classes cause +names to be associated with values within namespaces. + +Any assignments within loops are considered to cause the targets of such +assignments to provide non-constant values. + +Assignments to names are only really considered to cause the targets of such +assignments to provide constant values if the targets reside in the module +namespace or in class namespaces, subject to the above conditions. + +Assignments to names within functions are not generally considered to cause the +targets of such assignments to provide constant values since functions can be +invoked many times with different inputs. However, there may be benefits in +considering a local to be constant within a single invocation. """ from micropython.common import * @@ -44,6 +96,8 @@ Module.__init__(self, name) self.visitor = self + # Import machinery links. + self.importer = importer self.builtins = self.importer.modules.get("__builtins__") self.loaded = 0 @@ -59,6 +113,7 @@ # Namespace state. self.in_init = 0 # Find instance attributes in __init__ methods. + self.in_method = 0 # Find instance attributes in all methods. self.in_loop = 0 # Note loop "membership", affecting assignments. self.namespaces = [] self.module = None @@ -74,7 +129,7 @@ "Process the given 'module'." - self.node = self.module = module + self.astnode = self.module = module processed = self.dispatch(module) if self.has_key("__all__"): all = self["__all__"] @@ -113,13 +168,32 @@ self.all_objects.add(obj) def store_lambda(self, obj): + + "Store a lambda function 'obj'." + self.all_objects.add(obj) + def store_class_attr(self, name): + + "Record class attribute 'name' in the current class." + + 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) + return 1 + + return 0 + def store_instance_attr(self, name): "Record instance attribute 'name' in the current class." - if self.in_init: + if self.in_method: # Current namespace is the function. # Previous namespace is the class. @@ -150,6 +224,15 @@ self.dispatch(n) return Instance() + def _visitConst(self, node): + return self._make_constant(node.value) + + def _make_constant(self, value): + if not self.constant_values.has_key(value): + const = Const(value) + self.constant_values[value] = const + return self.constant_values[value] + def _visitFunction(self, node, name): """ @@ -189,11 +272,14 @@ # Current namespace is the function. # Previous namespace is the class. - if name == "__init__" and isinstance(self.namespaces[-2], Class): - self.in_init = 1 + if len(self.namespaces) > 1 and isinstance(self.namespaces[-2], Class): + if name == "__init__": + self.in_init = 1 + self.in_method = 1 self.dispatch(node.code) self.in_init = 0 + self.in_method = 0 self.namespaces.pop() if name is not None: @@ -218,7 +304,8 @@ def visitAssAttr(self, node): expr = self.dispatch(node.expr) if isinstance(expr, Attr) and expr.name == "self": - self.store_instance_attr(node.attrname) + if not self.store_class_attr(node.attrname): + self.store_instance_attr(node.attrname) return None def visitAssList(self, node): @@ -250,6 +337,12 @@ visitCallFunc = OP def visitClass(self, node): + + """ + Register the class at the given 'node' subject to the restrictions + mentioned in the module docstring. + """ + if self.namespaces: print "Class %r in %r is not global: ignored." % (node.name, self.namespaces[-1].full_name()) return None @@ -297,9 +390,7 @@ visitCompare = OP def visitConst(self, node): - const = Const(node.value) - self.constant_values[node.value] = const - return const + return self._visitConst(node) visitContinue = NOP @@ -411,8 +502,7 @@ def visitKeyword(self, node): self.dispatch(node.expr) - const = Const(node.name) - self.constant_values[node.name] = const + self._visitConst(node) self.keyword_names.add(node.name) return None @@ -439,9 +529,7 @@ def visitName(self, node): name = node.name if name == "None": - const = Const(None) - self.constant_values[None] = const - return const + return self._make_constant(None) elif self.namespaces and self.namespaces[-1].has_key(name): return self.namespaces[-1][name] elif self.has_key(name): diff -r b1ef174ec4a2 -r dd32f05e3a8c micropython/rsvp.py --- a/micropython/rsvp.py Mon May 19 00:22:00 2008 +0200 +++ b/micropython/rsvp.py Sat Jun 07 21:55:24 2008 +0200 @@ -31,6 +31,12 @@ new_code.append(item) return new_code +def name(attr): + if isinstance(attr, Attr): + return attr.name + else: + return attr + class Instruction: "A generic instruction." @@ -63,7 +69,11 @@ "An instruction accessing an object's attribute." def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self.get_operand()) + position = self.get_operand() + if position is not None: + return "%s(%r) # %s" % (self.__class__.__name__, position, name(self.attr)) + else: + return "%s(%r)" % (self.__class__.__name__, name(self.attr)) def get_operand(self): return self.attr.position @@ -77,9 +87,11 @@ def __repr__(self): location, position, result = self.get_operands() if location is not None: - return "%s(%r, %r -> %r)" % (self.__class__.__name__, location, position, result) + return "%s(%r) # %r, %r (%s)" % (self.__class__.__name__, result, location, position, name(self.attr)) + elif result is not None: + return "%s(%r) # %s" % (self.__class__.__name__, result, name(self.attr)) else: - return "%s(%r)" % (self.__class__.__name__, result) + return "%s(...) # %s" % (self.__class__.__name__, name(self.attr)) def get_operands(self): if isinstance(self.attr, Attr): diff -r b1ef174ec4a2 -r dd32f05e3a8c tests/attributes.py --- a/tests/attributes.py Mon May 19 00:22:00 2008 +0200 +++ b/tests/attributes.py Sat Jun 07 21:55:24 2008 +0200 @@ -5,6 +5,11 @@ def __init__(self, value): self.instattr = value + self.clsattr + + def update(self, value): + self.attr = value + self.clsattr = value C C.clsattr diff -r b1ef174ec4a2 -r dd32f05e3a8c tests/range.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/range.py Sat Jun 07 21:55:24 2008 +0200 @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +a = 1 +b = 2 +range(a, b) + +# vim: tabstop=4 expandtab shiftwidth=4