1.1 --- a/micropython/ast.py Thu Mar 20 01:13:48 2008 +0100
1.2 +++ b/micropython/ast.py Fri Mar 21 02:14:30 2008 +0100
1.3 @@ -237,20 +237,14 @@
1.4
1.5 # NOTE: Only simple cases are used for optimisations.
1.6
1.7 - last = self.last_op()
1.8 - if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1:
1.9 - target = last.attr.value
1.10 - context = last.attr.parent
1.11 - else:
1.12 - target = None
1.13 - context = None
1.14 + target, context = self._optimise_known_target()
1.15
1.16 # Where a target is known and has a known context, avoid generating any
1.17 - # first argument.
1.18 + # first argument. Instance methods do not have a known target since they
1.19 + # are accessed via an instance whose identity cannot generally be known
1.20 + # at compile-time.
1.21
1.22 - if context is not None:
1.23 - pass # NOTE: Class methods should be supported.
1.24 - else:
1.25 + if context is None:
1.26 continue_label = self.new_label()
1.27 self.new_op(LoadContext())
1.28 self.new_op(CheckContext())
1.29 @@ -258,22 +252,23 @@
1.30 self.dispatch(compiler.ast.Name("TypeError"))
1.31 self.new_op(RaiseException())
1.32 self.set_label(continue_label)
1.33 + else:
1.34 + pass # NOTE: Class methods should be supported.
1.35
1.36 # Evaluate the arguments.
1.37
1.38 positional = 1
1.39 start_keywords = None
1.40 employed_keywords = set()
1.41 + extra_keywords = []
1.42
1.43 for i, arg in enumerate(args):
1.44 if isinstance(arg, compiler.ast.Keyword):
1.45 if positional:
1.46 - self.new_op(ReserveFrame(len(args) - i))
1.47 + #self.new_op(ReserveFrame(len(args) - i))
1.48 start_keywords = i
1.49 positional = 0
1.50
1.51 - self.dispatch(arg.expr)
1.52 -
1.53 # Optimise where the target is known now.
1.54
1.55 if target is not None:
1.56 @@ -284,19 +279,19 @@
1.57
1.58 # Look for a callable with the precise target name.
1.59
1.60 - try:
1.61 - table_entry = self.paramtable.table[target_name]
1.62 + table_entry = self.paramtable.table[target_name]
1.63 +
1.64 + # Look the name up in the parameter table entry.
1.65
1.66 - # Where no callable is present, check to see if it is a
1.67 - # class name and find the initialiser instead.
1.68 + try:
1.69 + pos = table_entry[arg.name]
1.70 +
1.71 + # Where no position is found, this could be an extra keyword
1.72 + # argument.
1.73
1.74 except KeyError:
1.75 - if self.objtable.table.has_key(target_name):
1.76 - table_entry = self.paramtable.table[target_name + ".__init__"]
1.77 - else:
1.78 - raise
1.79 -
1.80 - pos = table_entry[arg.name]
1.81 + extra_keywords.append(arg)
1.82 + continue
1.83
1.84 # Test for illegal conditions.
1.85
1.86 @@ -308,6 +303,11 @@
1.87 "Keyword argument %r is repeated, overwriting parameter %r." % (arg.name, pos))
1.88
1.89 employed_keywords.add(pos)
1.90 +
1.91 + # Generate code for the keyword and the positioning
1.92 + # operation.
1.93 +
1.94 + self.dispatch(arg.expr)
1.95 self.new_op(StoreFrame(pos))
1.96
1.97 # Otherwise, generate the code needed to obtain the details of
1.98 @@ -320,9 +320,18 @@
1.99
1.100 try:
1.101 paramindex = self.paramtable.get_index(arg.name)
1.102 +
1.103 + # Where no position is found, this could be an extra keyword
1.104 + # argument.
1.105 +
1.106 except ValueError:
1.107 - raise TranslateError(self.module.full_name(), node, "No parameter definition exists for %r." % arg.name)
1.108 + extra_keywords.append(arg)
1.109 + continue
1.110
1.111 + # Generate code for the keyword and the positioning
1.112 + # operation.
1.113 +
1.114 + self.dispatch(arg.expr)
1.115 self.new_op(StoreFrameIndex(paramindex))
1.116
1.117 # use (callable+0)+paramindex+table
1.118 @@ -332,6 +341,31 @@
1.119 else:
1.120 self.dispatch(arg)
1.121
1.122 + # If any extra keywords were identified, generate them now.
1.123 +
1.124 + for arg in extra_keywords:
1.125 + const = self.module.constant_values[arg.name]
1.126 + self.new_op(LoadConst(const))
1.127 + self.dispatch(arg.expr)
1.128 +
1.129 + # NOTE: Somehow, the above needs to be combined with * and ** arguments.
1.130 +
1.131 + # Either test for a complete set of arguments.
1.132 +
1.133 + if target is not None:
1.134 + nargs = len(target.positional_names)
1.135 + if len(args) < nargs:
1.136 + raise TranslateError(self.module.full_name(), node,
1.137 + "Insufficient arguments for %r: need %d arguments." % (target.name, nargs))
1.138 + elif len(args) > nargs and not target.has_star and not target.has_dstar:
1.139 + raise TranslateError(self.module.full_name(), node,
1.140 + "Too many arguments for %r: need %d arguments." % (target.name, nargs))
1.141 +
1.142 + # Or generate instructions to do this at run-time.
1.143 +
1.144 + else:
1.145 + self.new_op(CheckFrame())
1.146 +
1.147 def _endCallFunc(self):
1.148
1.149 "Make the invocation and tidy up afterwards."
1.150 @@ -412,6 +446,36 @@
1.151 else:
1.152 return 0
1.153
1.154 + def _optimise_known_target(self):
1.155 +
1.156 + """
1.157 + Where the target of an invocation is known, provide information about it
1.158 + and its context. If a class is being invoked and the conditions are
1.159 + appropriate, get information about the specific initialiser.
1.160 + """
1.161 +
1.162 + last = self.last_op()
1.163 + if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1:
1.164 + target = last.attr.value
1.165 + context = last.attr.parent
1.166 +
1.167 + # Handle calls to classes.
1.168 + # NOTE: That the actual invocation target will be a __new__ method
1.169 + # NOTE: which calls the __init__ method, returning the new instance.
1.170 +
1.171 + if isinstance(target, micropython.inspect.Class):
1.172 + target = self.objtable.table[target.full_name()]["__init__"].value
1.173 + context = micropython.inspect.Instance()
1.174 +
1.175 + # A special context is chosen to avoid generating unnecessary
1.176 + # context loading and checking instructions.
1.177 +
1.178 + else:
1.179 + target = None
1.180 + context = None
1.181 +
1.182 + return target, context
1.183 +
1.184 # Visitor methods.
1.185
1.186 def default(self, node, *args):