# HG changeset patch # User Paul Boddie # Date 1207527621 -7200 # Node ID f5dff4743a7714ebf8f2807da8300264daafc3b7 # Parent 24df65a47aa8ef4220e7fe70697e972661dd0898 Added support for default parameter value initialisation, tidying up some related invocation issues along the way. diff -r 24df65a47aa8 -r f5dff4743a77 README.txt --- a/README.txt Sun Apr 06 21:26:06 2008 +0200 +++ b/README.txt Mon Apr 07 02:20:21 2008 +0200 @@ -284,6 +284,14 @@ 2. That no keyword argument overwrites an existing positional parameter. +Default Arguments +----------------- + +Some arguments may have default values which are used if no value is provided +in an invocation. Such defaults are initialised when the function itself is +initialised, and are used to fill in any invocation frames that are known at +compile-time. + Tuples, Frames and Allocation ----------------------------- diff -r 24df65a47aa8 -r f5dff4743a77 micropython/__init__.py --- a/micropython/__init__.py Sun Apr 06 21:26:06 2008 +0200 +++ b/micropython/__init__.py Mon Apr 07 02:20:21 2008 +0200 @@ -175,6 +175,11 @@ image.append(obj) pos += 1 + # Append any default values to the image. + + image += obj.default_attrs + pos += len(obj.default_attrs) + # Append the function code to the image. obj.code_location = pos @@ -182,6 +187,12 @@ image += code pos += len(code) + # Generate the default initialisation code. + + code = trans.get_default_code(obj) + image += code + pos += len(code) + # Remember the position of the module code. module.code_location = pos diff -r 24df65a47aa8 -r f5dff4743a77 micropython/ast.py --- a/micropython/ast.py Sun Apr 06 21:26:06 2008 +0200 +++ b/micropython/ast.py Mon Apr 07 02:20:21 2008 +0200 @@ -103,6 +103,17 @@ self.dispatch(unit.node) return self.code + def get_default_code(self, unit): + + "Return the code for the defaults in the given 'unit'." + + self.code = [] + for attr, default in zip(unit.default_attrs, unit.defaults): + self.dispatch(default) + self.new_op(LoadConst(unit)) + self.new_op(StoreAttr(attr)) + return self.code + def __repr__(self): return "Translation(%r)" % self.module @@ -290,11 +301,17 @@ employed_keywords = set() extra_keywords = [] - for i, arg in enumerate(args): + for frame_pos, arg in enumerate(args): + + # Handle positional and keyword arguments separately. + if isinstance(arg, compiler.ast.Keyword): + + # Note the start of keyword arguments. + if positional: - #self.new_op(ReserveFrame(len(args) - i)) - start_keywords = i + #self.new_op(ReserveFrame(len(args) - frame_pos)) + start_keywords = frame_pos positional = 0 # Optimise where the target is known now. @@ -336,7 +353,12 @@ # operation. self.dispatch(arg.expr) - self.new_op(StoreFrame(pos)) + + # If the position corresponds to the current frame element, + # skip generating the instruction. + + if frame_pos != pos: + self.new_op(StoreFrame(pos)) # Otherwise, generate the code needed to obtain the details of # the parameter location. @@ -369,28 +391,57 @@ else: self.dispatch(arg) + frame_pos = len(args) + # 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) + frame_pos += 1 # NOTE: Somehow, the above needs to be combined with * and ** arguments. # Either test for a complete set of arguments. if target is not None: + + # Make sure that enough arguments have been given. + + ndefaults = len(target.defaults) nargs_max = len(target.positional_names) - nargs_min = nargs_max - len(target.defaults) - if len(args) < nargs_min: + nargs_min = nargs_max - ndefaults + nargs = len(args) + + if nargs < nargs_min: raise TranslateError(self.module.full_name(), node, "Insufficient arguments for %r: need at least %d arguments." % (target.name, nargs_min)) - elif len(args) > nargs_max and not target.has_star and not target.has_dstar: + elif nargs > nargs_max and not target.has_star and not target.has_dstar: raise TranslateError(self.module.full_name(), node, - "Too many arguments for %r: need at most %d arguments." % (target.name, nargs)) + "Too many arguments for %r: need at most %d arguments." % (target.name, nargs_max)) + + # Where defaults are involved, put them into the frame. + # Here, we use negative index values to visit the right hand end of + # the defaults list. + + for i in range(nargs - nargs_max, 0): + self.new_op(LoadConst(target)) + self.new_op(LoadAttr(target.default_attrs[ndefaults + i])) + + # If the position corresponds to the current frame element, + # skip generating the instruction. + + pos = nargs_max + i + + if frame_pos != pos: + self.new_op(StoreFrame(pos)) + + frame_pos += 1 # Or generate instructions to do this at run-time. + # NOTE: CheckFrame has to check the number of arguments and to fill in + # NOTE: defaults. else: self.new_op(CheckFrame()) diff -r 24df65a47aa8 -r f5dff4743a77 micropython/inspect.py --- a/micropython/inspect.py Sun Apr 06 21:26:06 2008 +0200 +++ b/micropython/inspect.py Mon Apr 07 02:20:21 2008 +0200 @@ -576,11 +576,26 @@ "An inspected function." def __init__(self, name, parent, argnames, defaults, has_star, has_dstar, global_namespace=None, node=None): + + """ + Initialise the function with the given 'name', 'parent', list of + 'argnames', list of 'defaults', the 'has_star' flag (indicating the + presence of a * parameter), the 'has_dstar' flag (indicating the + presence of a ** parameter), optional 'global_namespace', and optional + AST 'node'. + """ + NamespaceDict.__init__(self, global_namespace) self.name = name self.parent = parent self.argnames = argnames self.defaults = defaults + self.has_star = has_star + self.has_dstar = has_dstar + self.node = node + + # Initialise the positional names. + self.positional_names = self.argnames[:] if has_dstar: self.dstar_name = self.positional_names[-1] @@ -588,9 +603,10 @@ 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 + + # Initialise default storage. + + self.default_attrs = [] # Caches. @@ -622,6 +638,11 @@ self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar ) + def store_default(self, value): + attr = Attr(None, self, None, value) + attr.update(value, 1) + self.default_attrs.append(attr) + def make_global(self, name): if name not in self.argnames and not self.has_key(name): self.globals.add(name) @@ -677,7 +698,14 @@ def finalise_attributes(self): - "Make sure all attributes are fully defined." + """ + Make sure all attributes (locals) are fully defined. Note that locals + are not attributes in the sense of class, module or instance attributes. + Defaults are also finalised by this method. + """ + + for i, default in enumerate(self.default_attrs): + default.position = i i = None for i, name in enumerate(self.argnames): @@ -688,6 +716,8 @@ for i, attr in enumerate(self.locals().values()): attr.position = i + j + # NOTE: May also have temporary variables. + def function_from_method(self): "Make a function from a method." @@ -1055,6 +1085,17 @@ node.unit = function + # Process the defaults. + + 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) + + # Enter the function. + self.namespaces.append(function) # Current namespace is the function. diff -r 24df65a47aa8 -r f5dff4743a77 tests/call_func_default.py --- a/tests/call_func_default.py Sun Apr 06 21:26:06 2008 +0200 +++ b/tests/call_func_default.py Mon Apr 07 02:20:21 2008 +0200 @@ -1,5 +1,7 @@ #!/usr/bin/env python +x = 123 + def f(a, b, c=4): pass @@ -18,4 +20,10 @@ g(1, c=3, b=2) g(1, 3) +def h(a, b, c=f(1, 2, 3)): + pass + +h(1, 2, 3) +h(1, 2) + # vim: tabstop=4 expandtab shiftwidth=4