1.1 --- a/README.txt Thu Mar 20 01:13:48 2008 +0100
1.2 +++ b/README.txt Fri Mar 21 02:14:30 2008 +0100
1.3 @@ -93,6 +93,8 @@
1.4
1.5 A suitable structure layout might be something like this:
1.6
1.7 + Identifier Address Type Object ...
1.8 +
1.9 0 1 2 3 4
1.10 classcode invocation __class__ attribute ...
1.11 reference reference reference
2.1 --- a/micropython/ast.py Thu Mar 20 01:13:48 2008 +0100
2.2 +++ b/micropython/ast.py Fri Mar 21 02:14:30 2008 +0100
2.3 @@ -237,20 +237,14 @@
2.4
2.5 # NOTE: Only simple cases are used for optimisations.
2.6
2.7 - last = self.last_op()
2.8 - if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1:
2.9 - target = last.attr.value
2.10 - context = last.attr.parent
2.11 - else:
2.12 - target = None
2.13 - context = None
2.14 + target, context = self._optimise_known_target()
2.15
2.16 # Where a target is known and has a known context, avoid generating any
2.17 - # first argument.
2.18 + # first argument. Instance methods do not have a known target since they
2.19 + # are accessed via an instance whose identity cannot generally be known
2.20 + # at compile-time.
2.21
2.22 - if context is not None:
2.23 - pass # NOTE: Class methods should be supported.
2.24 - else:
2.25 + if context is None:
2.26 continue_label = self.new_label()
2.27 self.new_op(LoadContext())
2.28 self.new_op(CheckContext())
2.29 @@ -258,22 +252,23 @@
2.30 self.dispatch(compiler.ast.Name("TypeError"))
2.31 self.new_op(RaiseException())
2.32 self.set_label(continue_label)
2.33 + else:
2.34 + pass # NOTE: Class methods should be supported.
2.35
2.36 # Evaluate the arguments.
2.37
2.38 positional = 1
2.39 start_keywords = None
2.40 employed_keywords = set()
2.41 + extra_keywords = []
2.42
2.43 for i, arg in enumerate(args):
2.44 if isinstance(arg, compiler.ast.Keyword):
2.45 if positional:
2.46 - self.new_op(ReserveFrame(len(args) - i))
2.47 + #self.new_op(ReserveFrame(len(args) - i))
2.48 start_keywords = i
2.49 positional = 0
2.50
2.51 - self.dispatch(arg.expr)
2.52 -
2.53 # Optimise where the target is known now.
2.54
2.55 if target is not None:
2.56 @@ -284,19 +279,19 @@
2.57
2.58 # Look for a callable with the precise target name.
2.59
2.60 - try:
2.61 - table_entry = self.paramtable.table[target_name]
2.62 + table_entry = self.paramtable.table[target_name]
2.63 +
2.64 + # Look the name up in the parameter table entry.
2.65
2.66 - # Where no callable is present, check to see if it is a
2.67 - # class name and find the initialiser instead.
2.68 + try:
2.69 + pos = table_entry[arg.name]
2.70 +
2.71 + # Where no position is found, this could be an extra keyword
2.72 + # argument.
2.73
2.74 except KeyError:
2.75 - if self.objtable.table.has_key(target_name):
2.76 - table_entry = self.paramtable.table[target_name + ".__init__"]
2.77 - else:
2.78 - raise
2.79 -
2.80 - pos = table_entry[arg.name]
2.81 + extra_keywords.append(arg)
2.82 + continue
2.83
2.84 # Test for illegal conditions.
2.85
2.86 @@ -308,6 +303,11 @@
2.87 "Keyword argument %r is repeated, overwriting parameter %r." % (arg.name, pos))
2.88
2.89 employed_keywords.add(pos)
2.90 +
2.91 + # Generate code for the keyword and the positioning
2.92 + # operation.
2.93 +
2.94 + self.dispatch(arg.expr)
2.95 self.new_op(StoreFrame(pos))
2.96
2.97 # Otherwise, generate the code needed to obtain the details of
2.98 @@ -320,9 +320,18 @@
2.99
2.100 try:
2.101 paramindex = self.paramtable.get_index(arg.name)
2.102 +
2.103 + # Where no position is found, this could be an extra keyword
2.104 + # argument.
2.105 +
2.106 except ValueError:
2.107 - raise TranslateError(self.module.full_name(), node, "No parameter definition exists for %r." % arg.name)
2.108 + extra_keywords.append(arg)
2.109 + continue
2.110
2.111 + # Generate code for the keyword and the positioning
2.112 + # operation.
2.113 +
2.114 + self.dispatch(arg.expr)
2.115 self.new_op(StoreFrameIndex(paramindex))
2.116
2.117 # use (callable+0)+paramindex+table
2.118 @@ -332,6 +341,31 @@
2.119 else:
2.120 self.dispatch(arg)
2.121
2.122 + # If any extra keywords were identified, generate them now.
2.123 +
2.124 + for arg in extra_keywords:
2.125 + const = self.module.constant_values[arg.name]
2.126 + self.new_op(LoadConst(const))
2.127 + self.dispatch(arg.expr)
2.128 +
2.129 + # NOTE: Somehow, the above needs to be combined with * and ** arguments.
2.130 +
2.131 + # Either test for a complete set of arguments.
2.132 +
2.133 + if target is not None:
2.134 + nargs = len(target.positional_names)
2.135 + if len(args) < nargs:
2.136 + raise TranslateError(self.module.full_name(), node,
2.137 + "Insufficient arguments for %r: need %d arguments." % (target.name, nargs))
2.138 + elif len(args) > nargs and not target.has_star and not target.has_dstar:
2.139 + raise TranslateError(self.module.full_name(), node,
2.140 + "Too many arguments for %r: need %d arguments." % (target.name, nargs))
2.141 +
2.142 + # Or generate instructions to do this at run-time.
2.143 +
2.144 + else:
2.145 + self.new_op(CheckFrame())
2.146 +
2.147 def _endCallFunc(self):
2.148
2.149 "Make the invocation and tidy up afterwards."
2.150 @@ -412,6 +446,36 @@
2.151 else:
2.152 return 0
2.153
2.154 + def _optimise_known_target(self):
2.155 +
2.156 + """
2.157 + Where the target of an invocation is known, provide information about it
2.158 + and its context. If a class is being invoked and the conditions are
2.159 + appropriate, get information about the specific initialiser.
2.160 + """
2.161 +
2.162 + last = self.last_op()
2.163 + if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1:
2.164 + target = last.attr.value
2.165 + context = last.attr.parent
2.166 +
2.167 + # Handle calls to classes.
2.168 + # NOTE: That the actual invocation target will be a __new__ method
2.169 + # NOTE: which calls the __init__ method, returning the new instance.
2.170 +
2.171 + if isinstance(target, micropython.inspect.Class):
2.172 + target = self.objtable.table[target.full_name()]["__init__"].value
2.173 + context = micropython.inspect.Instance()
2.174 +
2.175 + # A special context is chosen to avoid generating unnecessary
2.176 + # context loading and checking instructions.
2.177 +
2.178 + else:
2.179 + target = None
2.180 + context = None
2.181 +
2.182 + return target, context
2.183 +
2.184 # Visitor methods.
2.185
2.186 def default(self, node, *args):
3.1 --- a/micropython/inspect.py Thu Mar 20 01:13:48 2008 +0100
3.2 +++ b/micropython/inspect.py Fri Mar 21 02:14:30 2008 +0100
3.3 @@ -566,6 +566,13 @@
3.4 self.name = name
3.5 self.parent = parent
3.6 self.argnames = argnames
3.7 + self.positional_names = self.argnames[:]
3.8 + if has_dstar:
3.9 + self.dstar_name = self.positional_names[-1]
3.10 + del self.positional_names[-1]
3.11 + if has_star:
3.12 + self.star_name = self.positional_names[-1]
3.13 + del self.positional_names[-1]
3.14 self.has_star = has_star
3.15 self.has_dstar = has_dstar
3.16 self.node = node
3.17 @@ -916,6 +923,13 @@
3.18 raise InspectError(self.full_name(), node,
3.19 "Base class %r for %r is not found: it may be hidden in some way." % (base, cls.full_name()))
3.20
3.21 + # NOTE: Potentially dubious measure to permit __init__ availability.
3.22 + # If no bases exist, adopt the 'object' class.
3.23 +
3.24 + if not node.bases:
3.25 + expr = self.dispatch(compiler.ast.Name("object"))
3.26 + cls.add_base(expr.value)
3.27 +
3.28 # Make a back reference from the node for code generation.
3.29
3.30 node.unit = cls
3.31 @@ -924,6 +938,8 @@
3.32
3.33 self.store(node.name, cls)
3.34
3.35 + # Process the class body.
3.36 +
3.37 self.namespaces.append(cls)
3.38 self.dispatch(node.code)
3.39 self.namespaces.pop()
3.40 @@ -1074,7 +1090,11 @@
3.41
3.42 visitInvert = NOP
3.43
3.44 - visitKeyword = NOP
3.45 + def visitKeyword(self, node):
3.46 + self.dispatch(node.expr)
3.47 + const = Const(node.name)
3.48 + self.constant_values[node.name] = const
3.49 + return None
3.50
3.51 visitLambda = NOP
3.52
3.53 @@ -1200,7 +1220,10 @@
3.54 'frozenset', 'int', # 'list',
3.55 'long', 'object', 'set', 'slice', 'str',
3.56 'tuple', 'type', 'unicode', 'xrange']:
3.57 - self.store(key, Class(key, self, self))
3.58 +
3.59 + cls = Class(key, self, self)
3.60 + cls.set("__init__", Function("__init__", cls, [], 0, 0, cls))
3.61 + self.store(key, cls)
3.62
3.63 # NOTE: Temporary measure - provide detailed built-ins.
3.64
4.1 --- a/micropython/rsvp.py Thu Mar 20 01:13:48 2008 +0100
4.2 +++ b/micropython/rsvp.py Fri Mar 21 02:14:30 2008 +0100
4.3 @@ -105,6 +105,7 @@
4.4 class DropFrame(Instruction): "Drop an invocation frame."
4.5 class StoreFrame(Instruction): "Store an argument at the given frame location."
4.6 class StoreFrameIndex(Instruction): "Store an argument for the parameter with the given index."
4.7 +class CheckFrame(Instruction): "Check the invocation frame for the target."
4.8
4.9 # Invocation-related instructions.
4.10
4.11 @@ -113,7 +114,7 @@
4.12 class JumpIfTrue(Instruction): "Jump if the last evaluation gave a true result."
4.13 class LoadCallable(Instruction): "Load the target of an invocation."
4.14 class LoadContext(Instruction): "Load the context of an invocation."
4.15 -class CheckContext(Instruction): "Check the context of an invocation against the target."
4.16 +class CheckContext(Instruction): "Check the context of an invocation against the target, potentially discarding the context."
4.17 class RaiseException(Instruction): "Raise an exception."
4.18 class Return(Instruction): "Return a value from a subprogram."
4.19 class CheckException(Instruction): "Check the raised exception against another."
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/tests/call_func_extra.py Fri Mar 21 02:14:30 2008 +0100
5.3 @@ -0,0 +1,21 @@
5.4 +#!/usr/bin/env python
5.5 +
5.6 +def f(a, b, *c):
5.7 + pass
5.8 +
5.9 +f(1, 2, 3)
5.10 +f(1, b=2)
5.11 +f(1, 2, 3, 4)
5.12 +
5.13 +g = f
5.14 +g(1, 2, 3)
5.15 +g(1, b=2)
5.16 +g(1, 2, 3, 4)
5.17 +
5.18 +def g(a, c, *b):
5.19 + pass
5.20 +
5.21 +g(1, c=2)
5.22 +g(1, 2, 3, 4)
5.23 +
5.24 +# vim: tabstop=4 expandtab shiftwidth=4
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/tests/call_func_keyword.py Fri Mar 21 02:14:30 2008 +0100
6.3 @@ -0,0 +1,18 @@
6.4 +#!/usr/bin/env python
6.5 +
6.6 +def f(a, b, **c):
6.7 + pass
6.8 +
6.9 +f(1, 2, 3)
6.10 +f(1, b=2, c=3, d=4)
6.11 +f(c=3, b=2, a=1, d=4)
6.12 +
6.13 +g = f
6.14 +g(1, c=3, b=2, d=4)
6.15 +
6.16 +def g(a, c, b):
6.17 + pass
6.18 +
6.19 +g(1, c=3, b=2, d=4)
6.20 +
6.21 +# vim: tabstop=4 expandtab shiftwidth=4
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/tests/call_method.py Fri Mar 21 02:14:30 2008 +0100
7.3 @@ -0,0 +1,13 @@
7.4 +#!/usr/bin/env python
7.5 +
7.6 +class C:
7.7 + def f(self, a, b, c):
7.8 + pass
7.9 +
7.10 +c = C()
7.11 +c.f(1, 2, 3)
7.12 +
7.13 +f = c.f
7.14 +f(1, 2, 3)
7.15 +
7.16 +# vim: tabstop=4 expandtab shiftwidth=4
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/tests/failure/argument_shortage.py Fri Mar 21 02:14:30 2008 +0100
8.3 @@ -0,0 +1,17 @@
8.4 +#!/usr/bin/env python
8.5 +
8.6 +def f(a, b, c):
8.7 + pass
8.8 +
8.9 +g = f
8.10 +g(1, 2) # uncertain target - not detected
8.11 +
8.12 +def g(a, c, b):
8.13 + pass
8.14 +
8.15 +g(1, a=3, b=2)
8.16 +
8.17 +f(1, 2, 3)
8.18 +f(1, 2)
8.19 +
8.20 +# vim: tabstop=4 expandtab shiftwidth=4
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/tests/failure/argument_surplus.py Fri Mar 21 02:14:30 2008 +0100
9.3 @@ -0,0 +1,17 @@
9.4 +#!/usr/bin/env python
9.5 +
9.6 +def f(a, b, c):
9.7 + pass
9.8 +
9.9 +g = f
9.10 +g(1, 2, 3, 4) # uncertain target - not detected
9.11 +
9.12 +def g(a, c, b):
9.13 + pass
9.14 +
9.15 +g(1, a=3, b=2)
9.16 +
9.17 +f(1, 2, 3)
9.18 +f(1, 2, 3, 4)
9.19 +
9.20 +# vim: tabstop=4 expandtab shiftwidth=4