# HG changeset patch # User Paul Boddie # Date 1245542573 -7200 # Node ID af6a631171bcfceb26d2bb179030d1e7e95ca86b # Parent f6f367f0005e0f727097946175cba75780543f2d Changed the list representation to use an interchangeable "fragment" object which holds the contents of each list. Added a MakeFragment instruction and RSVP support for some list methods. Added built-in implementations of len and range. Added more test script and RSVP options. Improved test coverage. diff -r f6f367f0005e -r af6a631171bc lib/builtins.py --- a/lib/builtins.py Mon Jun 15 00:40:25 2009 +0200 +++ b/lib/builtins.py Sun Jun 21 02:02:53 2009 +0200 @@ -336,7 +336,13 @@ def isinstance(obj, cls_or_tuple): pass def issubclass(obj, cls_or_tuple): pass def iter(collection_or_callable, sentinel=None): pass -def len(obj): pass + +def len(obj): + + "Implementation of len." + + return obj.__len__() + def locals(): pass def map(function, *args): pass def max(*args): pass @@ -346,7 +352,16 @@ def ord(c): pass def pow(x, y, z=None): pass def property(fget=None, fset=None, fdel=None, doc=None): pass -def range(start_or_end, end=None, step=None): pass + +def range(start_or_end, end=None, step=1): + + "Implementation of range." + + l = [] + for i in xrange(start_or_end, end, step): + l.append(i) + return l + def raw_input(prompt=None): pass def reduce(function, sequence, initial=None): pass def reload(module): pass diff -r f6f367f0005e -r af6a631171bc micropython/ast.py --- a/micropython/ast.py Mon Jun 15 00:40:25 2009 +0200 +++ b/micropython/ast.py Sun Jun 21 02:02:53 2009 +0200 @@ -412,7 +412,7 @@ self._visitAttr(node, self.attribute_load_instructions) def visitList(self, node): - self._generateSequence("list", node) + self._generateList(node) def visitListComp(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "ListComp") @@ -438,7 +438,7 @@ self._endCallFunc(temp_target, target, temp_context) def visitTuple(self, node): - self._generateSequence("tuple", node) + self._generateTuple(node) # Definitions. diff -r f6f367f0005e -r af6a631171bc micropython/program.py --- a/micropython/program.py Mon Jun 15 00:40:25 2009 +0200 +++ b/micropython/program.py Sun Jun 21 02:02:53 2009 +0200 @@ -44,6 +44,17 @@ (self.classcode, self.attrcode, self.codeaddr, self.funccode, self.size), self.name ) +class FragmentObject: + + "A representation of a list fragment, used by list instances." + + def __init__(self, occupied_size, allocated_size): + self.occupied_size = occupied_size + self.allocated_size = allocated_size + + def __repr__(self): + return "%r" % ((self.occupied_size, self.allocated_size),) + class Context: """ diff -r f6f367f0005e -r af6a631171bc micropython/rsvp.py --- a/micropython/rsvp.py Mon Jun 15 00:40:25 2009 +0200 +++ b/micropython/rsvp.py Sun Jun 21 02:02:53 2009 +0200 @@ -174,7 +174,8 @@ class LoadAddressContextCond(Address): """Load the current value from the given fixed attribute address, only using the current value as context if the attribute is compatible.""" -class MakeInstance(Immediate): "Make a new instance." +class MakeInstance(Immediate): "Make a new instance using the current value as a reference to a template." +class MakeFragment(Immediate): "Make a new list fragment." # Access to address-relative data. (LoadAttrIndexContext not defined.) diff -r f6f367f0005e -r af6a631171bc micropython/trans.py --- a/micropython/trans.py Mon Jun 15 00:40:25 2009 +0200 +++ b/micropython/trans.py Sun Jun 21 02:02:53 2009 +0200 @@ -1220,12 +1220,44 @@ self.new_op(JumpIfTrue(handled_block)) self.new_op(RaiseException()) - def _generateSequence(self, sequence_type, node): + def _generateTuple(self, node): + + "Make a tuple using the given program 'node'." + + self.make_instance(self.get_builtin_class("tuple", node), len(node.nodes)) + temp = self.get_temp() + self._populateSequence(temp, node) + self.new_op(temp) + self.discard_temp(temp) + + def _generateList(self, node): + + "Make a list using the given program 'node'." + + # Make a fragment containing the list elements. - "Make a sequence of 'sequence_type' for the given program 'node'." + self.new_op(MakeFragment(len(node.nodes) + 1)) + temp = self.get_temp() + self._populateSequence(temp, node) + self.new_op(temp) + self.record_value() - self.make_instance(self.get_builtin_class(sequence_type, node), len(node.nodes)) - temp = self.get_temp() + self.make_instance(self.get_builtin_class("list", node), 1) + list_temp = self.get_temp() + self.new_op(list_temp) + self.new_op(StoreAttr(Attr(0, None, None))) + self.set_source() + self.discard_value() + + self.new_op(list_temp) + self.discard_temp(temp) + self.discard_temp(list_temp) + + def _populateSequence(self, temp, node): + + """ + Populate a sequence using the given 'temp' reference and program 'node'. + """ for i, n in enumerate(node.nodes): self.dispatch(n) @@ -1235,9 +1267,6 @@ self.set_source() self.discard_value() - self.new_op(temp) - self.discard_temp(temp) - def _generateTestBoolean(self, node, temp): """ diff -r f6f367f0005e -r af6a631171bc rsvp.py --- a/rsvp.py Mon Jun 15 00:40:25 2009 +0200 +++ b/rsvp.py Sun Jun 21 02:02:53 2009 +0200 @@ -52,7 +52,7 @@ current callable """ -from micropython.program import ReplaceableContext, PlaceholderContext +from micropython.program import ReplaceableContext, PlaceholderContext, FragmentObject import operator class IllegalInstruction(Exception): @@ -79,7 +79,7 @@ "A really simple virtual processor." - def __init__(self, memory, objlist, paramlist, true_constant, false_constant, pc=None, debug=0): + def __init__(self, memory, objlist, paramlist, true_constant, false_constant, pc=None, debug=0, abort_upon_exception=0): """ Initialise the processor with a 'memory' (a list of values containing @@ -98,6 +98,7 @@ self.pc = pc or 0 self.debug = debug + self.abort_upon_exception = abort_upon_exception # Stacks. @@ -138,7 +139,10 @@ self.int_class = cls.location self.int_instance = cls.instance_template_location cls = self._get_class("__builtins__", "list") + self.list_class = cls.location self.list_instance = cls.instance_template_location + cls = self._get_class("__builtins__", "tuple") + self.tuple_class = cls.location # Debugging attributes. @@ -447,6 +451,12 @@ # Introduce object as context for the new object. self.value = addr, addr + def MakeFragment(self): + size = self.operand + addr = self._MakeFragment(size) + # NOTE: Context is not relevant for fragments. + self.value = None, addr + def LoadAttr(self): context, ref = self.value # Retrieved context should already be appropriate for the instance. @@ -682,6 +692,8 @@ # NOTE: Adding the program counter as the first attribute. self.save(self.exception + 1, self.pc) # Jumping to the current handler. + if self.abort_upon_exception: + raise Exception return self.handler_stack[-1] def PushHandler(self): @@ -743,6 +755,13 @@ self.save(addr, data.with_size(size)) return addr + def _MakeFragment(self, size): + # Reserve twice the amount of space. + addr = self.new(size * 2) + # Save the header, overriding the size. + self.save(addr, FragmentObject(size, size * 2)) + return addr + def _LoadAddressContextCond(self, context, ref, inst_ref): # Check the instance context against the target's context. # This provides the context overriding for methods. @@ -915,26 +934,56 @@ # NOTE: Specific copying of tuples/lists. args_context, args = self.frame_stack[frame + 1] - header = self.load(args) + + # Test operand suitability. - list = self._MakeObject(header.size, self.list_instance) - for i in range(1, header.size): - self.save(list + i, self.load(args + i)) + if self._CheckInstance(args, self.list_class): + _x, sequence = self.load(args + 1) + header = self.load(sequence) + size = header.occupied_size + elif self._CheckInstance(args, self.tuple_class): + sequence = args + header = self.load(sequence) + size = header.size + else: + self.exception = self._MakeObject(2, self.type_error_instance) + return self.RaiseException() - self.result = list, list + # Copy the sequence contents. + + new_fragment = self._MakeFragment(size) + for i in range(1, size): + self.save(new_fragment + i, self.load(sequence + i)) + + # Make the list instance. + + addr = self._MakeObject(2, self.list_instance) + self.save(addr + 1, (None, new_fragment)) + + self.result = addr, addr def builtins_list_getitem(self): frame = self.local_sp_stack[-1] - # Get operands addresses. + # Get the operand address. - obj_context, obj = self.frame_stack[frame] item_context, item = self.frame_stack[frame + 1] - header = self.load(obj) - nelements = header.size - 1 + # Get the list address. + + obj_context, obj = self.frame_stack[frame] + + # Get the fragment address. + # NOTE: Assume single location for header. - # NOTE: Assume single location for data. + _x, fragment = self.load(obj + 1) + + # Get the fragment header. + + header = self.load(fragment) + nelements = header.occupied_size - 1 + + # NOTE: Assume single location for data and header. item_pos = self.load(item + 1) if item_pos >= 0 and item_pos < nelements: @@ -945,7 +994,84 @@ self.exception = self._MakeObject(2, self.index_error_instance) return self.RaiseException() - self.result = self.load(obj + 1 + item_pos) + # NOTE: Assume single location for header. + + self.result = self.load(fragment + 1 + item_pos) + + def builtins_list_len(self): + frame = self.local_sp_stack[-1] + + # Get the list address. + + obj_context, obj = self.frame_stack[frame] + + # Get the fragment address. + # NOTE: Assume single location for header. + + _x, fragment = self.load(obj + 1) + + # Get the fragment header. + + header = self.load(fragment) + nelements = header.occupied_size - 1 + + # Make a new object. + + addr = self._MakeObject(2, self.int_instance) + + # Store the result. + # NOTE: The data is considered ready to use. + + self.save(addr + 1, nelements) + + # Return the new object. + # Introduce object as context for the new object. + + self.result = addr, addr + + def builtins_list_append(self): + frame = self.local_sp_stack[-1] + + # Get operand address. + + arg_context, arg = self.frame_stack[frame + 1] + + # Get the list address. + + obj_context, obj = self.frame_stack[frame] + + # Get the fragment address. + # NOTE: Assume single location for header. + + _x, fragment = self.load(obj + 1) + + # Get the fragment header. + + header = self.load(fragment) + + # Attempt to add the reference. + + if header.occupied_size < header.allocated_size: + self.save(fragment + header.occupied_size, (arg_context, arg)) + header.occupied_size += 1 + else: + + # Make a new fragment, maintaining more space than currently + # occupied in order to avoid reallocation. + + new_fragment = self._MakeFragment(header.allocated_size) + + # Copy existing elements. + + for i in range(1, header.allocated_size): + self.save(new_fragment + i, self.load(fragment + i)) + + self.save(new_fragment + header.allocated_size, (arg_context, arg)) + + # Set the new fragment in the object. + # NOTE: The old fragment could be deallocated. + + self.save(obj + 1, (None, new_fragment)) def builtins_object_init(self): pass @@ -968,6 +1094,8 @@ "__builtins__.int.__ne__" : builtins_int_ne, "__builtins__.bool.__bool__" : builtins_bool_bool, "__builtins__.list.__getitem__" : builtins_list_getitem, + "__builtins__.list.__len__" : builtins_list_len, + "__builtins__.list.append" : builtins_list_append, "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. @@ -978,7 +1106,7 @@ # Convenience functions. -def machine(program, with_builtins=0, debug=0): +def machine(program, with_builtins=0, debug=0, abort_upon_exception=0): print "Making the image..." code = program.get_image(with_builtins) print "Getting raw structures..." @@ -992,7 +1120,7 @@ importer = program.get_importer() true_constant = importer.get_constant(True).location false_constant = importer.get_constant(False).location - rm = RSVPMachine(rc, objlist, paramlist, true_constant, false_constant, debug=debug) + rm = RSVPMachine(rc, objlist, paramlist, true_constant, false_constant, debug=debug, abort_upon_exception=abort_upon_exception) rm.pc = program.code_location print "Returning program occupying %d locations." % len(rm.memory) return rm diff -r f6f367f0005e -r af6a631171bc test.py --- a/test.py Mon Jun 15 00:40:25 2009 +0200 +++ b/test.py Sun Jun 21 02:02:53 2009 +0200 @@ -56,7 +56,7 @@ pt = p.get_parameter_table() if "-m" in args or "-t" in args: - rm = rsvp.machine(p) + rm = rsvp.machine(p, debug=("-g" in args), abort_upon_exception=("-x" in args)) if "-t" in args: success = rm.test(m) print "Test successful?", success diff -r f6f367f0005e -r af6a631171bc tests/globals_vs_locals.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/globals_vs_locals.py Sun Jun 21 02:02:53 2009 +0200 @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +b = 2 + +def f(x): + b = x + +def g(x): + global b + b = x + +f(3) +result_2 = b +g(4) +result_4 = b + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r f6f367f0005e -r af6a631171bc tests/methods.py --- a/tests/methods.py Mon Jun 15 00:40:25 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -#!/usr/bin/env python - -class B: - def f(self): - pass - -def f(self): - pass - -b = B() - - # on A on a -class A: - f1 = f # unbound (A) bound (a) - f2 = B.f # unbound (B) unbound (B) - f3 = b.f # bound (b) bound (b) - - def __init__(self): - self.f4 = f # N/A function - self.f5 = B.f # N/A unbound (B) - self.f6 = b.f # N/A bound (b) - - def m(self): - x = self.f1 # should use optimised attribute access - x = self.f2 - x = self.f3 - x = self.f4 - x = self.f5 - x = self.f6 - -a = A() -a.m() - -A.f1 -A.f2 -A.f3 -a.f1 -a.f2 -a.f3 -a.f4 -a.f5 -a.f6 - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r f6f367f0005e -r af6a631171bc tests/mixins.py --- a/tests/mixins.py Mon Jun 15 00:40:25 2009 +0200 +++ b/tests/mixins.py Sun Jun 21 02:02:53 2009 +0200 @@ -2,14 +2,14 @@ class A: def a(self): - return self.x + return self.x # x not defined in this class, provided in subclasses class B(A): - x = 123 + x = 123 # x provided here class C(A): def __init__(self, x): - self.x = x + self.x = x # x provided here b = B() c = C(456) @@ -17,4 +17,7 @@ p = b.a() q = c.a() +result_123 = p +result_456 = q + # vim: tabstop=4 expandtab shiftwidth=4 diff -r f6f367f0005e -r af6a631171bc tests/names.py --- a/tests/names.py Mon Jun 15 00:40:25 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -a = 1 -a -b = 2 - -def f(x): - b = 3 - -def g(x): - global b - b = 4 - -class C: - value = "hello" - for x in range(a, b): - subvalue = "world" - -C.value - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r f6f367f0005e -r af6a631171bc tests/op_add.py --- a/tests/op_add.py Mon Jun 15 00:40:25 2009 +0200 +++ b/tests/op_add.py Sun Jun 21 02:02:53 2009 +0200 @@ -5,4 +5,7 @@ c = a + b d = a + b + c +result_3 = c +result_6 = d + # vim: tabstop=4 expandtab shiftwidth=4 diff -r f6f367f0005e -r af6a631171bc tests/op_add_call.py --- a/tests/op_add_call.py Mon Jun 15 00:40:25 2009 +0200 +++ b/tests/op_add_call.py Sun Jun 21 02:02:53 2009 +0200 @@ -6,6 +6,6 @@ a = 1 b = 2 c = 3 -d = f(a + b, b + c) +result_8 = f(a + b, b + c) # vim: tabstop=4 expandtab shiftwidth=4 diff -r f6f367f0005e -r af6a631171bc tests/op_add_default.py --- a/tests/op_add_default.py Mon Jun 15 00:40:25 2009 +0200 +++ b/tests/op_add_default.py Sun Jun 21 02:02:53 2009 +0200 @@ -5,6 +5,6 @@ a = 10 # used in f default b = 20 # used in f default -x = f(1, 2) +result_33 = f(1, 2) # vim: tabstop=4 expandtab shiftwidth=4 diff -r f6f367f0005e -r af6a631171bc tests/range.py --- a/tests/range.py Mon Jun 15 00:40:25 2009 +0200 +++ b/tests/range.py Sun Jun 21 02:02:53 2009 +0200 @@ -1,7 +1,11 @@ #!/usr/bin/env python a = 1 -b = 2 -range(a, b) +b = 20 +l = range(a, b) + +result1_19 = len(l) +result_1 = l[0] +result2_19 = l[-1] # vim: tabstop=4 expandtab shiftwidth=4