# HG changeset patch # User Paul Boddie # Date 1243816917 -7200 # Node ID 8abc113da24ca0e7fb646a1942edda4620609758 # Parent 2ede6db71cebc9604905dfc15df550ac5e25b168 Fixed StoreCallable using a new DataObject method which replaces the address of the callable. Made a cmd module to handle common compilation and processing activities. Renamed MakeObject to MakeInstance. Changed FillDefaults to use the current value as a reference to the container holding the defaults. Removed the extra temporary storage slot previously used when adjusting frames. Changed lambda functions to use a context parameter, changing the image to not reserve space for defaults immediately after the header of such functions. Added notes about the issues with positioning keyword arguments. Expanded and improved the tests. diff -r 2ede6db71ceb -r 8abc113da24c docs/concepts.txt --- a/docs/concepts.txt Sun May 31 18:44:40 2009 +0200 +++ b/docs/concepts.txt Mon Jun 01 02:41:57 2009 +0200 @@ -399,6 +399,26 @@ g 1 2 3 h 1 2 +Confusion can occur when functions are adopted as methods, since the context +then occupies the first slot in the invocation frame: + + def f(x, y, z): + pass + + f(x=1, y=2, z=3) -> f(, 1, 2, 3) + -> f(1, 2, 3) + + class C: + f = f + + def g(x, y, z): + pass + + c = C() + + c.f(y=2, z=3) -> f(, 2, 3) + c.g(y=2, z=3) -> C.g(, 2, 3) + Just as with parameter tables, a displacement list can be prepared from a parameter table: diff -r 2ede6db71ceb -r 8abc113da24c docs/exceptions.txt --- a/docs/exceptions.txt Sun May 31 18:44:40 2009 +0200 +++ b/docs/exceptions.txt Mon Jun 01 02:41:57 2009 +0200 @@ -9,6 +9,19 @@ is raised in any handling of the exception, or upon exit of a handler where the active exception has been caught (thus resetting the active exception). +Instructions +------------ + +StoreException records the current value reference using the exception +register. + +LoadException obtains the current exception and puts it in the value register. + +CheckException checks the current exception against a class referenced by the +current value. + +ClearException resets the exception register. + Handlers ======== diff -r 2ede6db71ceb -r 8abc113da24c docs/invocation.txt --- a/docs/invocation.txt Sun May 31 18:44:40 2009 +0200 +++ b/docs/invocation.txt Mon Jun 01 02:41:57 2009 +0200 @@ -163,6 +163,23 @@ Approach #2 - add arguments, add defaults while checking frame +Defaults for lambda functions: + + f = lambda x, y=default: ... + + Defines instance of f with method: + + def (, x, y=default): + ... + + Where default is attribute #1. + + f(obj) # f not known at compile-time + + f -> f + -> load context for argument #1 (f, since an instance is referenced) + obj -> argument #2 + Functions as methods: def f(x, y, z): ... diff -r 2ede6db71ceb -r 8abc113da24c micropython/__init__.py --- a/micropython/__init__.py Sun May 31 18:44:40 2009 +0200 +++ b/micropython/__init__.py Mon Jun 01 02:41:57 2009 +0200 @@ -259,10 +259,15 @@ if not with_builtins and item.module.name == "__builtins__": item.code_location = item.full_name() - # Skip any defaults. + # Skip any defaults for named functions. + + elif item.name is not None: + item.code_location = pos + len(item.defaults) + + # Skip any defaults for lambda functions. else: - item.code_location = pos + len(item.defaults) + item.code_location = pos current_function = item @@ -307,7 +312,8 @@ # Check the code location only where the code has been generated. assert (not with_builtins and item.module.name == "__builtins__") or \ - item.code_location == len(self.raw_code) + len(item.defaults) + item.name is not None and item.code_location == len(self.raw_code) + len(item.defaults) or \ + item.name is None and item.code_location == len(self.raw_code) elif isinstance(item, micropython.data.Module): assert item.location == len(self.raw_code) @@ -743,23 +749,4 @@ continue module.set_module(submodule, self.add_module(module.name + "." + submodule)) -# Convenience functions. - -def program(filename, path, requested_optimisations, verbose=0): - - """ - Return the program object for the program specified by the given 'filename', - module search 'path' and 'requested_optimisations'. - """ - - i = micropython.Importer(path, verbose, requested_optimisations) - p = micropython.Program(i, requested_optimisations) - - i.load_from_file("lib/builtins.py", "__builtins__") - - if filename is not None: - i.load_from_file(filename) - - return p - # vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c micropython/ast.py --- a/micropython/ast.py Sun May 31 18:44:40 2009 +0200 +++ b/micropython/ast.py Mon Jun 01 02:41:57 2009 +0200 @@ -170,7 +170,7 @@ # Make an object and store it in the unused first slot. - self.make_object(cls, len(cls.instance_attributes())) + self.make_instance(cls, len(cls.instance_attributes())) self.new_op(StoreTemp(0)) # Invoke the appropriate initialiser. @@ -596,7 +596,7 @@ fn.body_block = self.new_block() check_block = self.new_block() - self._generateFunctionContextTest(node, check_block, fn.body_block) + self._generateFunctionContextTest(node, check_block) # Check the number of parameters and defaults. @@ -604,7 +604,8 @@ self.new_op(CheckFrame((nparams, ndefaults, fn.has_star))) if ndefaults > 0: - self.new_op(FillDefaults((nparams, ndefaults, fn))) + self.new_op(LoadFunction(fn)) + self.new_op(FillDefaults((nparams, ndefaults))) # Produce the body. @@ -673,7 +674,7 @@ fn.body_block = self.new_block() check_block = self.new_block() - self._generateFunctionContextTest(node, check_block, fn.body_block) + self._generateFunctionContextTest(node, check_block) # Check the number of parameters and defaults. @@ -681,7 +682,8 @@ self.new_op(CheckFrame((nparams, ndefaults, fn.has_star))) if ndefaults > 0: - self.new_op(FillDefaults((nparams, ndefaults, fn))) + self.new_op(LoadTemp(0)) # context provides storage + self.new_op(FillDefaults((nparams, ndefaults))) # Produce the body. diff -r 2ede6db71ceb -r 8abc113da24c micropython/cmd.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micropython/cmd.py Mon Jun 01 02:41:57 2009 +0200 @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +""" +Command option processing and other utilities for compiling and testing +programs. +""" + +import micropython + +def parse_optimisations(args): + + "Parse 'args' for optimisation flags." + + if "-omax" in args: + requested_optimisations = micropython.Program.supported_optimisations + else: + requested_optimisations = [] + for arg in args: + if arg.startswith("-o"): + for arg_part in arg[2:].split(","): + requested_optimisations.append(arg_part) + + return requested_optimisations + +def program(filename, path, requested_optimisations, verbose=0): + + """ + Return the program object for the program specified by the given 'filename', + module search 'path' and 'requested_optimisations'. + """ + + i = micropython.Importer(path, verbose, requested_optimisations) + p = micropython.Program(i, requested_optimisations) + + i.load_from_file("lib/builtins.py", "__builtins__") + + if filename is not None: + i.load_from_file(filename) + + return p + +# Convenience functions. + +def get_program(filename, path, args): + + """ + Return the program object for the program specified by the given 'filename', + module search 'path' and command 'args'. + """ + + requested_optimisations = parse_optimisations(args) + return program(filename, path, requested_optimisations, "-v" in args) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c micropython/data.py --- a/micropython/data.py Sun May 31 18:44:40 2009 +0200 +++ b/micropython/data.py Mon Jun 01 02:41:57 2009 +0200 @@ -899,6 +899,11 @@ self.astnode = node self.referenced = 0 + # For lambda functions with defaults, add a context argument. + + if name is None and defaults: + self.argnames.insert(0, "") + # Initialise the positional names. self.positional_names = self.argnames[:] @@ -970,7 +975,7 @@ objtable.get_index("__builtins__.function"), # is instance self.code_location, "__builtins__.function", - len(self.defaults) + 1, # size + len(self.defaults) + 1, # size (not accurate for lambda functions before instantiation) paramtable.as_list().get_code(self.full_name()) # funccode ) ] diff -r 2ede6db71ceb -r 8abc113da24c micropython/inspect.py --- a/micropython/inspect.py Sun May 31 18:44:40 2009 +0200 +++ b/micropython/inspect.py Mon Jun 01 02:41:57 2009 +0200 @@ -321,6 +321,8 @@ None. """ + # Define the function object. + function = Function( name, self.get_parent(), @@ -332,6 +334,8 @@ node ) + self.add_object(function, any_scope=1) + # Make a back reference from the node for code generation. node.unit = function @@ -377,7 +381,6 @@ if node.defaults: return Instance() # indicates no known target - self.add_object(function, any_scope=1) return function # Specific handler methods. diff -r 2ede6db71ceb -r 8abc113da24c micropython/opt.py --- a/micropython/opt.py Sun May 31 18:44:40 2009 +0200 +++ b/micropython/opt.py Mon Jun 01 02:41:57 2009 +0200 @@ -193,7 +193,8 @@ StoreAddressContext, # as the context LoadCallable, TestIdentity, TestIdentityAddress, CheckSelf, # as one of the operands - CheckException, CheckFrame, MakeObject, + CheckException, CheckFrame, FillDefaults, + MakeInstance, CheckContext, CheckClassContext, LoadContext # as the object providing the result )) diff -r 2ede6db71ceb -r 8abc113da24c micropython/program.py --- a/micropython/program.py Sun May 31 18:44:40 2009 +0200 +++ b/micropython/program.py Mon Jun 01 02:41:57 2009 +0200 @@ -36,6 +36,9 @@ def with_size(self, size): return DataObject(self.classcode, self.attrcode, self.codeaddr, self.name, size, self.funccode) + def with_callable(self, codeaddr): + return DataObject(self.classcode, self.attrcode, codeaddr, self.name, self.size, self.funccode) + def __repr__(self): return "%r # %s" % ( (self.classcode, self.attrcode, self.codeaddr, self.funccode, self.size), self.name diff -r 2ede6db71ceb -r 8abc113da24c micropython/rsvp.py --- a/micropython/rsvp.py Sun May 31 18:44:40 2009 +0200 +++ b/micropython/rsvp.py Mon Jun 01 02:41:57 2009 +0200 @@ -173,7 +173,7 @@ 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 MakeObject(Immediate): "Make a new object. There isn't a complementary DropObject." +class MakeInstance(Immediate): "Make a new instance." # Access to address-relative data. @@ -249,7 +249,7 @@ LoadAddress, LoadAddressContext, LoadAddressContextCond, LoadAttr, LoadAttrIndex, LoadAttrIndexContextCond, LoadCallable, LoadContext, LoadResult, - LoadException, MakeObject + LoadException, MakeInstance ) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c micropython/trans.py --- a/micropython/trans.py Sun May 31 18:44:40 2009 +0200 +++ b/micropython/trans.py Mon Jun 01 02:41:57 2009 +0200 @@ -30,19 +30,20 @@ # Allocation-related methods. - def make_object(self, cls, n): + def make_instance(self, cls, n): """ - Request a new object with the given class 'cls' and with 'n' attributes. + Request a new instance using the given class 'cls' and with 'n' + attributes. """ # Load the class in order to locate the instance template. self.new_op(LoadConst(cls)) - # NOTE: Object headers are one location. + # NOTE: Instance headers are one location. - self.new_op(MakeObject(n + 1)) + self.new_op(MakeInstance(n + 1)) def make_exception(self, name, node): @@ -50,7 +51,7 @@ # NOTE: Reserving only one attribute. - self.make_object(self.get_builtin_class(name, node), 1) + self.make_instance(self.get_builtin_class(name, node), 1) # Name-related methods. @@ -247,7 +248,7 @@ on the 'extend' instruction. """ - ntemp = self.max_temp_position + 2 # include space for instantiators to expand backwards + ntemp = self.max_temp_position + 1 extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. # Code writing methods. @@ -842,7 +843,7 @@ dynamic = function.name is None if dynamic: - self.make_object(self.get_builtin_class("function", function), len(attr_to_default)) + self.make_instance(self.get_builtin_class("function", function), len(attr_to_default)) temp = self.get_temp() for attr, default in attr_to_default: @@ -862,11 +863,11 @@ else: return None - def _generateFunctionContextTest(self, node, check_block, body_block): + def _generateFunctionContextTest(self, node, check_block): """ Generate code to test the context for 'node', jumping to 'check_block' - and 'body_block' from this code. + from this code. """ adjust_block = self.new_block() @@ -1189,7 +1190,7 @@ "Make a sequence of 'sequence_type' for the given program 'node'." - self.make_object(self.get_builtin_class(sequence_type, node), len(node.nodes)) + self.make_instance(self.get_builtin_class(sequence_type, node), len(node.nodes)) temp = self.get_temp() for i, n in enumerate(node.nodes): diff -r 2ede6db71ceb -r 8abc113da24c rsvp.py --- a/rsvp.py Sun May 31 18:44:40 2009 +0200 +++ b/rsvp.py Mon Jun 01 02:41:57 2009 +0200 @@ -52,7 +52,6 @@ current callable """ -from micropython.program import DataObject # for creating "nice" new objects import operator class IllegalInstruction(Exception): @@ -432,7 +431,7 @@ context = source_context self.save(self.operand, (context, source_ref)) - def MakeObject(self): + def MakeInstance(self): size = self.operand context, ref = self.value # NOTE: Referencing the instance template. @@ -538,7 +537,7 @@ context, ref = self.value # NOTE: Should improve the representation and permit direct saving. data = self.load(ref) - self.save(ref, (data.classcode, data.attrcode) + self.callable) + self.save(ref, data.with_callable(self.callable)) def LoadContext(self): context, ref = self.value @@ -575,9 +574,8 @@ return self.RaiseException() def FillDefaults(self): - # NOTE: Make the get_operand method of the instruction provide the - # NOTE: function location. - (nargs, ndefaults, fn) = self.operand + context, ref = self.value + (nargs, ndefaults) = self.operand # The frame is actually installed as the locals. @@ -590,7 +588,6 @@ default = nlocals - (nargs - ndefaults) self.frame_stack.extend([None] * (nargs - nlocals)) pos = nlocals - ref = fn.location while pos < nargs: self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header @@ -914,6 +911,7 @@ native_functions = { "__builtins__.int.__add__" : builtins_int_add, + "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. "__builtins__.int.__bool__" : builtins_int_bool, "__builtins__.int.__neg__" : builtins_int_neg, "__builtins__.int.__lt__" : builtins_int_lt, diff -r 2ede6db71ceb -r 8abc113da24c test.py --- a/test.py Sun May 31 18:44:40 2009 +0200 +++ b/test.py Mon Jun 01 02:41:57 2009 +0200 @@ -1,6 +1,6 @@ #!/usr/bin/env python -import micropython +import micropython.cmd from micropython.graph import get_graph import rsvp import sys @@ -20,23 +20,6 @@ for name, attr in obj.items(): print name, attr -# Command option processing. - -def parse_optimisations(args): - - "Parse 'args' for optimisation flags." - - if "-omax" in args: - requested_optimisations = micropython.Program.supported_optimisations - else: - requested_optimisations = [] - for arg in args: - if arg.startswith("-o"): - for arg_part in arg[2:].split(","): - requested_optimisations.append(arg_part) - - return requested_optimisations - # Main program. if __name__ == "__main__": @@ -49,12 +32,10 @@ else: filename = None - requested_optimisations = parse_optimisations(args) - # Make the program. try: - p = micropython.program(filename, path, requested_optimisations, "-v" in args) + p = micropython.cmd.get_program(filename, path, args) if filename is None: print "Loading module micropython ..." m = p.get_importer().load("micropython") diff -r 2ede6db71ceb -r 8abc113da24c test_all.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test_all.py Mon Jun 01 02:41:57 2009 +0200 @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +import micropython.cmd +import rsvp +import sys +import os +from glob import glob + +# Main program. + +if __name__ == "__main__": + args = sys.argv[1:] + path = sys.path[:] + path.append("tests") + + # Process all tests. + + try: + _f = args.index("-f") + filenames = args[_f+1:] + except ValueError: + filenames = glob(os.path.join("tests", "*.py")) + + filenames.sort() + + results = [] + + for filename in filenames: + print "Processing", filename + + try: + p = micropython.cmd.get_program(filename, path, args) + m = p.get_importer().get_module("__main__") + + # Report any errors. + + except micropython.ProcessingError, exc: + print repr(exc) + if "-tb" in args: + raise + + else: + rm = rsvp.machine(p) + success = rm.test(m) + print "Test successful?", success + results.append((filename, success)) + + print + for result in results: + print result + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/attributes_class_bind_function_unbound.py --- a/tests/attributes_class_bind_function_unbound.py Sun May 31 18:44:40 2009 +0200 +++ b/tests/attributes_class_bind_function_unbound.py Mon Jun 01 02:41:57 2009 +0200 @@ -11,6 +11,6 @@ d = D() q = d.e # unbound C.e -result_321 = q(321) +result_321 = q(C(), 321) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/attributes_class_bind_function_unbound_fake_self.py --- a/tests/attributes_class_bind_function_unbound_fake_self.py Sun May 31 18:44:40 2009 +0200 +++ b/tests/attributes_class_bind_function_unbound_fake_self.py Mon Jun 01 02:41:57 2009 +0200 @@ -11,6 +11,6 @@ d = D() q = d.e # unbound C.e -result_321 = q(123, 321) +result_321 = q(C(), 321) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/attributes_instance_bind_function.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/attributes_instance_bind_function.py Mon Jun 01 02:41:57 2009 +0200 @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +def e(self, x): + return x + +class C: + def __init__(self, e): + self.e = e + +c = C(e) +p = c.e # unbound e +result_321 = p(123, 321) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/call_func_dstar_parameter.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_func_dstar_parameter.py Mon Jun 01 02:41:57 2009 +0200 @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +def f(a, b, **c): + return c + +r1 = f(1, 2, 3) +r2 = f(1, b=2, c=3, d=4) +r3 = f(c=3, b=2, a=1, d=4) + +g = f + +r4 = g(1, c=3, b=2, d=4) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/call_func_keyword.py --- a/tests/call_func_keyword.py Sun May 31 18:44:40 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -#!/usr/bin/env python - -def f(a, b, **c): - return c - -r1 = f(1, 2, 3) -r2 = f(1, b=2, c=3, d=4) -r3 = f(c=3, b=2, a=1, d=4) - -g = f - -r4 = g(1, c=3, b=2, d=4) - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/call_method_keywords.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_method_keywords.py Mon Jun 01 02:41:57 2009 +0200 @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +class C: + def m(self, x): + return x + +c = C() +result_123 = c.m(x=123) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/failure/attributes_class_bind_function_unbound.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/failure/attributes_class_bind_function_unbound.py Mon Jun 01 02:41:57 2009 +0200 @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +def e(self, x): + return x + +class C: + e = e + +class D: + e = C.e + +d = D() +q = d.e # unbound C.e +result_321 = q(321) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/failure/attributes_class_bind_function_unbound_fake_self.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/failure/attributes_class_bind_function_unbound_fake_self.py Mon Jun 01 02:41:57 2009 +0200 @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +def e(self, x): + return x + +class C: + e = e + +class D: + e = C.e + +d = D() +q = d.e # unbound C.e +result_321 = q(123, 321) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/failure/attributes_instance_bind_function.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/failure/attributes_instance_bind_function.py Mon Jun 01 02:41:57 2009 +0200 @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +def e(self, x): + return x + +class C: + def __init__(self, e): + self.e = e + +c = C(e) +p = c.e # unbound e +result_321 = p(321) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 2ede6db71ceb -r 8abc113da24c tests/lambda.py --- a/tests/lambda.py Sun May 31 18:44:40 2009 +0200 +++ b/tests/lambda.py Mon Jun 01 02:41:57 2009 +0200 @@ -9,10 +9,10 @@ def g(f, x): return f(x) -identity(1) -add_2(1) -g(identity, 1) -g(add_2, 1) -g(f(3), 1) +result_1 = identity(1) +result_3 = add_2(1) +result2_1 = g(identity, 1) +result2_3 = g(add_2, 1) +result_4 = g(f(3), 1) # vim: tabstop=4 expandtab shiftwidth=4