# HG changeset patch # User Paul Boddie # Date 1206062070 -3600 # Node ID 04456ecc182e64f4e555144bd4f097c912b6f23c # Parent 999f14fecba913a74f2d353fdae1d561c452b427 Attempted to improve argument handling and to test argument list compatibility with function parameter lists where possible. Added __init__ methods to built-in classes and made all classes at least inherit from object. diff -r 999f14fecba9 -r 04456ecc182e README.txt --- a/README.txt Thu Mar 20 01:13:48 2008 +0100 +++ b/README.txt Fri Mar 21 02:14:30 2008 +0100 @@ -93,6 +93,8 @@ A suitable structure layout might be something like this: + Identifier Address Type Object ... + 0 1 2 3 4 classcode invocation __class__ attribute ... reference reference reference diff -r 999f14fecba9 -r 04456ecc182e micropython/ast.py --- a/micropython/ast.py Thu Mar 20 01:13:48 2008 +0100 +++ b/micropython/ast.py Fri Mar 21 02:14:30 2008 +0100 @@ -237,20 +237,14 @@ # NOTE: Only simple cases are used for optimisations. - last = self.last_op() - if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: - target = last.attr.value - context = last.attr.parent - else: - target = None - context = None + target, context = self._optimise_known_target() # Where a target is known and has a known context, avoid generating any - # first argument. + # first argument. Instance methods do not have a known target since they + # are accessed via an instance whose identity cannot generally be known + # at compile-time. - if context is not None: - pass # NOTE: Class methods should be supported. - else: + if context is None: continue_label = self.new_label() self.new_op(LoadContext()) self.new_op(CheckContext()) @@ -258,22 +252,23 @@ self.dispatch(compiler.ast.Name("TypeError")) self.new_op(RaiseException()) self.set_label(continue_label) + else: + pass # NOTE: Class methods should be supported. # Evaluate the arguments. positional = 1 start_keywords = None employed_keywords = set() + extra_keywords = [] for i, arg in enumerate(args): if isinstance(arg, compiler.ast.Keyword): if positional: - self.new_op(ReserveFrame(len(args) - i)) + #self.new_op(ReserveFrame(len(args) - i)) start_keywords = i positional = 0 - self.dispatch(arg.expr) - # Optimise where the target is known now. if target is not None: @@ -284,19 +279,19 @@ # Look for a callable with the precise target name. - try: - table_entry = self.paramtable.table[target_name] + table_entry = self.paramtable.table[target_name] + + # Look the name up in the parameter table entry. - # Where no callable is present, check to see if it is a - # class name and find the initialiser instead. + try: + pos = table_entry[arg.name] + + # Where no position is found, this could be an extra keyword + # argument. except KeyError: - if self.objtable.table.has_key(target_name): - table_entry = self.paramtable.table[target_name + ".__init__"] - else: - raise - - pos = table_entry[arg.name] + extra_keywords.append(arg) + continue # Test for illegal conditions. @@ -308,6 +303,11 @@ "Keyword argument %r is repeated, overwriting parameter %r." % (arg.name, pos)) employed_keywords.add(pos) + + # Generate code for the keyword and the positioning + # operation. + + self.dispatch(arg.expr) self.new_op(StoreFrame(pos)) # Otherwise, generate the code needed to obtain the details of @@ -320,9 +320,18 @@ try: paramindex = self.paramtable.get_index(arg.name) + + # Where no position is found, this could be an extra keyword + # argument. + except ValueError: - raise TranslateError(self.module.full_name(), node, "No parameter definition exists for %r." % arg.name) + extra_keywords.append(arg) + continue + # Generate code for the keyword and the positioning + # operation. + + self.dispatch(arg.expr) self.new_op(StoreFrameIndex(paramindex)) # use (callable+0)+paramindex+table @@ -332,6 +341,31 @@ else: self.dispatch(arg) + # If any extra keywords were identified, generate them now. + + for arg in extra_keywords: + const = self.module.constant_values[arg.name] + self.new_op(LoadConst(const)) + self.dispatch(arg.expr) + + # NOTE: Somehow, the above needs to be combined with * and ** arguments. + + # Either test for a complete set of arguments. + + if target is not None: + nargs = len(target.positional_names) + if len(args) < nargs: + raise TranslateError(self.module.full_name(), node, + "Insufficient arguments for %r: need %d arguments." % (target.name, nargs)) + elif len(args) > nargs and not target.has_star and not target.has_dstar: + raise TranslateError(self.module.full_name(), node, + "Too many arguments for %r: need %d arguments." % (target.name, nargs)) + + # Or generate instructions to do this at run-time. + + else: + self.new_op(CheckFrame()) + def _endCallFunc(self): "Make the invocation and tidy up afterwards." @@ -412,6 +446,36 @@ else: return 0 + def _optimise_known_target(self): + + """ + Where the target of an invocation is known, provide information about it + and its context. If a class is being invoked and the conditions are + appropriate, get information about the specific initialiser. + """ + + last = self.last_op() + if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: + target = last.attr.value + context = last.attr.parent + + # Handle calls to classes. + # NOTE: That the actual invocation target will be a __new__ method + # NOTE: which calls the __init__ method, returning the new instance. + + if isinstance(target, micropython.inspect.Class): + target = self.objtable.table[target.full_name()]["__init__"].value + context = micropython.inspect.Instance() + + # A special context is chosen to avoid generating unnecessary + # context loading and checking instructions. + + else: + target = None + context = None + + return target, context + # Visitor methods. def default(self, node, *args): diff -r 999f14fecba9 -r 04456ecc182e micropython/inspect.py --- a/micropython/inspect.py Thu Mar 20 01:13:48 2008 +0100 +++ b/micropython/inspect.py Fri Mar 21 02:14:30 2008 +0100 @@ -566,6 +566,13 @@ self.name = name self.parent = parent self.argnames = argnames + 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] self.has_star = has_star self.has_dstar = has_dstar self.node = node @@ -916,6 +923,13 @@ 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())) + # NOTE: Potentially dubious measure to permit __init__ availability. + # If no bases exist, adopt the 'object' class. + + if not node.bases: + expr = self.dispatch(compiler.ast.Name("object")) + cls.add_base(expr.value) + # Make a back reference from the node for code generation. node.unit = cls @@ -924,6 +938,8 @@ self.store(node.name, cls) + # Process the class body. + self.namespaces.append(cls) self.dispatch(node.code) self.namespaces.pop() @@ -1074,7 +1090,11 @@ visitInvert = NOP - visitKeyword = NOP + def visitKeyword(self, node): + self.dispatch(node.expr) + const = Const(node.name) + self.constant_values[node.name] = const + return None visitLambda = NOP @@ -1200,7 +1220,10 @@ 'frozenset', 'int', # 'list', 'long', 'object', 'set', 'slice', 'str', 'tuple', 'type', 'unicode', 'xrange']: - self.store(key, Class(key, self, self)) + + cls = Class(key, self, self) + cls.set("__init__", Function("__init__", cls, [], 0, 0, cls)) + self.store(key, cls) # NOTE: Temporary measure - provide detailed built-ins. diff -r 999f14fecba9 -r 04456ecc182e micropython/rsvp.py --- a/micropython/rsvp.py Thu Mar 20 01:13:48 2008 +0100 +++ b/micropython/rsvp.py Fri Mar 21 02:14:30 2008 +0100 @@ -105,6 +105,7 @@ class DropFrame(Instruction): "Drop an invocation frame." class StoreFrame(Instruction): "Store an argument at the given frame location." class StoreFrameIndex(Instruction): "Store an argument for the parameter with the given index." +class CheckFrame(Instruction): "Check the invocation frame for the target." # Invocation-related instructions. @@ -113,7 +114,7 @@ class JumpIfTrue(Instruction): "Jump if the last evaluation gave a true result." class LoadCallable(Instruction): "Load the target of an invocation." class LoadContext(Instruction): "Load the context of an invocation." -class CheckContext(Instruction): "Check the context of an invocation against the target." +class CheckContext(Instruction): "Check the context of an invocation against the target, potentially discarding the context." class RaiseException(Instruction): "Raise an exception." class Return(Instruction): "Return a value from a subprogram." class CheckException(Instruction): "Check the raised exception against another." diff -r 999f14fecba9 -r 04456ecc182e tests/call_func_extra.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_func_extra.py Fri Mar 21 02:14:30 2008 +0100 @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +def f(a, b, *c): + pass + +f(1, 2, 3) +f(1, b=2) +f(1, 2, 3, 4) + +g = f +g(1, 2, 3) +g(1, b=2) +g(1, 2, 3, 4) + +def g(a, c, *b): + pass + +g(1, c=2) +g(1, 2, 3, 4) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 999f14fecba9 -r 04456ecc182e tests/call_func_keyword.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_func_keyword.py Fri Mar 21 02:14:30 2008 +0100 @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +def f(a, b, **c): + pass + +f(1, 2, 3) +f(1, b=2, c=3, d=4) +f(c=3, b=2, a=1, d=4) + +g = f +g(1, c=3, b=2, d=4) + +def g(a, c, b): + pass + +g(1, c=3, b=2, d=4) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 999f14fecba9 -r 04456ecc182e tests/call_method.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_method.py Fri Mar 21 02:14:30 2008 +0100 @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +class C: + def f(self, a, b, c): + pass + +c = C() +c.f(1, 2, 3) + +f = c.f +f(1, 2, 3) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 999f14fecba9 -r 04456ecc182e tests/failure/argument_shortage.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/failure/argument_shortage.py Fri Mar 21 02:14:30 2008 +0100 @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +def f(a, b, c): + pass + +g = f +g(1, 2) # uncertain target - not detected + +def g(a, c, b): + pass + +g(1, a=3, b=2) + +f(1, 2, 3) +f(1, 2) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 999f14fecba9 -r 04456ecc182e tests/failure/argument_surplus.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/failure/argument_surplus.py Fri Mar 21 02:14:30 2008 +0100 @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +def f(a, b, c): + pass + +g = f +g(1, 2, 3, 4) # uncertain target - not detected + +def g(a, c, b): + pass + +g(1, a=3, b=2) + +f(1, 2, 3) +f(1, 2, 3, 4) + +# vim: tabstop=4 expandtab shiftwidth=4