1.1 --- a/README.txt Sun May 11 21:50:30 2008 +0200
1.2 +++ b/README.txt Mon May 12 23:54:16 2008 +0200
1.3 @@ -64,6 +64,41 @@
1.4 There may be some scope for simplifying the above, to the detriment of Python
1.5 compatibility, since the unbound vs. bound methods situation can be confusing.
1.6
1.7 +Manipulating Values
1.8 +-------------------
1.9 +
1.10 +According to the table describing value acquisition, different instructions
1.11 +must implement different operations when acquiring values:
1.12 +
1.13 + Instruction Purpose Context Operations
1.14 + ----------- ------- ------------------
1.15 +
1.16 + LoadConst Load class, function, Combine null context with loaded
1.17 + module, constant object
1.18 +
1.19 + LoadAddress Load attribute from Classes, functions and modules
1.20 + known object stored as cause the loaded attribute to be
1.21 + an attribute retrieved unchanged...
1.22 + whereas constants (representing
1.23 + instances) cause the constant to
1.24 + override the attribute's own
1.25 + context
1.26 +
1.27 + LoadAttr Load attribute from Combine the instance as context
1.28 + instance stored as an with the object from the attribute
1.29 + attribute
1.30 +
1.31 + LoadAttrIndex Load attribute from Classes, functions and modules as
1.32 + unknown object stored the unknown object accessor cause
1.33 + as an attribute the loaded attribute to be
1.34 + retrieved unchanged...
1.35 + whereas instances cause the
1.36 + accessor to override the
1.37 + attribute's own context
1.38 +
1.39 +Note that of the above context operations, only the decision logic in
1.40 +connection with LoadAttrIndex is performed at run-time.
1.41 +
1.42 Objects
1.43 -------
1.44
2.1 --- a/micropython/ast.py Sun May 11 21:50:30 2008 +0200
2.2 +++ b/micropython/ast.py Mon May 12 23:54:16 2008 +0200
2.3 @@ -265,44 +265,50 @@
2.4
2.5 if isinstance(target, Const):
2.6 target_name = target.value_type_name()
2.7 + elif isinstance(target, Instance):
2.8 + target_name = None # skip production of optimised code
2.9 else:
2.10 target_name = target.full_name()
2.11
2.12 - # Access the object table to get the attribute position.
2.13 + # Only try and discover the position if the target can be resolved.
2.14
2.15 - try:
2.16 - table_entry = self.objtable.table[target_name]
2.17 - except KeyError:
2.18 - raise TranslateError(self.module.full_name(), node,
2.19 - "No object entry exists for target %r." % target_name)
2.20 + if target_name is not None:
2.21 +
2.22 + # Access the object table to get the attribute position.
2.23
2.24 - try:
2.25 - pos = table_entry[attrname]
2.26 - except KeyError:
2.27 - raise TranslateError(self.module.full_name(), node,
2.28 - "No attribute entry exists for name %r in target %r." % (attrname, target_name))
2.29 + try:
2.30 + table_entry = self.objtable.table[target_name]
2.31 + except KeyError:
2.32 + raise TranslateError(self.module.full_name(), node,
2.33 + "No object entry exists for target %r." % target_name)
2.34
2.35 - # Produce a suitable instruction.
2.36 + try:
2.37 + pos = table_entry[attrname]
2.38 + except KeyError:
2.39 + raise TranslateError(self.module.full_name(), node,
2.40 + "No attribute entry exists for name %r in target %r." % (attrname, target_name))
2.41
2.42 - self.replace_op(AddressInstruction(pos))
2.43 + # Produce a suitable instruction.
2.44 +
2.45 + self.replace_op(AddressInstruction(pos))
2.46 + return
2.47
2.48 # Where the last operation involves the special 'self' name, check to
2.49 # see if the attribute is acceptably positioned and produce a direct
2.50 # access to the attribute.
2.51
2.52 - elif self._optimise_self_access(attrname, AttrInstruction):
2.53 - pass
2.54 + elif self._optimise_self_access(attrname, (AddressInstruction, AttrInstruction)):
2.55 + return
2.56
2.57 # Otherwise, perform a normal operation.
2.58
2.59 - else:
2.60 - try:
2.61 - index = self.objtable.get_index(attrname)
2.62 - except self.objtable.TableError:
2.63 - raise TranslateError(self.module.full_name(), node,
2.64 - "No attribute entry exists for name %r." % attrname)
2.65 + try:
2.66 + index = self.objtable.get_index(attrname)
2.67 + except self.objtable.TableError:
2.68 + raise TranslateError(self.module.full_name(), node,
2.69 + "No attribute entry exists for name %r." % attrname)
2.70
2.71 - self.new_op(AttrIndexInstruction(index))
2.72 + self.new_op(AttrIndexInstruction(index))
2.73
2.74 def _startCallFunc(self):
2.75
2.76 @@ -312,7 +318,30 @@
2.77
2.78 def _generateCallFunc(self, args, node):
2.79
2.80 - # NOTE: Only simple cases are used for optimisations.
2.81 + """
2.82 + Support a generic function invocation using the given 'args', occurring
2.83 + on the given 'node', where the expression providing the invocation
2.84 + target has just been generated.
2.85 +
2.86 + In other situations, the invocation is much simpler and does not need to
2.87 + handle the full flexibility of a typical Python invocation. Internal
2.88 + invocations, such as those employed by operators and certain
2.89 + control-flow mechanisms, use predetermined arguments and arguably do not
2.90 + need to support the same things as the more general invocations.
2.91 + """
2.92 +
2.93 + target, context, temp = self._generateCallFuncContext()
2.94 + self._generateCallFuncArgs(target, context, args, node)
2.95 + return temp
2.96 +
2.97 + def _generateCallFuncContext(self):
2.98 +
2.99 + """
2.100 + Produce code which loads and checks the context of the current
2.101 + invocation, the instructions for whose target have already been
2.102 + produced, returning a list of instructions which reference the
2.103 + invocation target.
2.104 + """
2.105
2.106 t = self._optimise_known_target()
2.107 if t:
2.108 @@ -334,17 +363,39 @@
2.109 self.new_op(LoadContext())
2.110 self.new_op(CheckContext())
2.111 self.new_op(JumpIfTrue(continue_label))
2.112 +
2.113 + # Where the context is inappropriate, drop the incomplete frame and
2.114 + # raise an exception.
2.115 +
2.116 + self.new_op(DropFrame())
2.117 self.dispatch(compiler.ast.Name("TypeError"))
2.118 self.new_op(RaiseException())
2.119 self.set_label(continue_label)
2.120 else:
2.121 pass # NOTE: Class methods should be supported.
2.122
2.123 + return target, context, temp
2.124 +
2.125 + def _generateCallFuncArgs(self, target, context, args, node):
2.126 +
2.127 + """
2.128 + Given invocation 'target' and 'context' information, a list of nodes
2.129 + representing the 'args' (arguments), generate instructions which load
2.130 + the arguments for the invocation defined by the given 'node'.
2.131 + """
2.132 +
2.133 # Evaluate the arguments.
2.134
2.135 employed_positions = set()
2.136 extra_keywords = []
2.137
2.138 + # NOTE: Fix context for self-accessed methods.
2.139 +
2.140 + if context is not None and isinstance(context, Instance):
2.141 + ncontext = 1
2.142 + else:
2.143 + ncontext = 0
2.144 +
2.145 for frame_pos, arg in enumerate(args):
2.146
2.147 # Handle positional and keyword arguments separately.
2.148 @@ -385,8 +436,8 @@
2.149
2.150 # Add space for arguments appearing before this one.
2.151
2.152 - if frame_pos < pos:
2.153 - self.new_op(ReserveFrame(pos - frame_pos))
2.154 + if frame_pos + ncontext < pos:
2.155 + self.new_op(ReserveFrame(pos - frame_pos - ncontext))
2.156
2.157 # Generate code for the keyword and the positioning
2.158 # operation.
2.159 @@ -396,7 +447,7 @@
2.160 # If the position corresponds to the current frame element,
2.161 # skip generating the store instruction.
2.162
2.163 - if frame_pos > pos:
2.164 + if frame_pos + ncontext > pos:
2.165 self.new_op(StoreFrame(pos))
2.166
2.167 # Otherwise, generate the code needed to obtain the details of
2.168 @@ -429,9 +480,9 @@
2.169
2.170 else:
2.171 self.dispatch(arg)
2.172 - employed_positions.add(frame_pos)
2.173 + employed_positions.add(frame_pos + ncontext)
2.174
2.175 - frame_pos = len(args)
2.176 + frame_pos = len(args) + ncontext
2.177
2.178 # NOTE: Extra keywords are not supported.
2.179 # NOTE: Somehow, the above needs to be combined with * arguments.
2.180 @@ -446,7 +497,7 @@
2.181 ndefaults = len(target.defaults)
2.182 nargs_min = nargs_max - ndefaults
2.183
2.184 - for i in range(0, nargs_min):
2.185 + for i in range(ncontext, nargs_min):
2.186 if i not in employed_positions:
2.187 raise TranslateError(self.module.full_name(), node,
2.188 "Argument %r not supplied for %r: need at least %d arguments." % (i+1, target.name, nargs_min))
2.189 @@ -481,8 +532,6 @@
2.190 else:
2.191 self.new_op(CheckFrame())
2.192
2.193 - return temp
2.194 -
2.195 def _endCallFunc(self, temp):
2.196
2.197 "Make the invocation and tidy up afterwards."
2.198 @@ -659,7 +708,7 @@
2.199 if self._should_optimise_known_target() and self._have_known_target():
2.200 last = self.last_op()
2.201 target = last.attr.value
2.202 - context = last.attr.parent
2.203 + context = last.context
2.204
2.205 # Handle calls to classes.
2.206
2.207 @@ -674,19 +723,25 @@
2.208 else:
2.209 return None
2.210
2.211 - def _optimise_self_access(self, attrname, instruction):
2.212 + def _optimise_self_access(self, attrname, classes):
2.213
2.214 """
2.215 Where the provided 'attrname' accesses an attribute which occupies the
2.216 same position in all possible objects which can be accessed, generate an
2.217 - 'instruction' accessing the attribute directly.
2.218 + instruction using one of the given 'classes', accessing the attribute
2.219 + directly.
2.220 """
2.221
2.222 + AddressInstruction, AttrInstruction = classes
2.223 +
2.224 if self._should_optimise_self_access() and self._have_self_input() and \
2.225 not self.unit.is_relocated(attrname):
2.226
2.227 attr = self.unit.parent.all_attributes()[attrname]
2.228 - self.new_op(instruction(attr))
2.229 + if isinstance(attr.parent, Instance):
2.230 + self.new_op(AttrInstruction(attr))
2.231 + else:
2.232 + self.new_op(AddressInstruction(attr, Instance()))
2.233 return 1
2.234 else:
2.235 return 0
2.236 @@ -737,6 +792,7 @@
2.237 temp_method = self._optimise_temp_storage()
2.238
2.239 # Add arguments.
2.240 + # NOTE: No support for defaults.
2.241
2.242 self.new_ops(temp) # Explicit context as first argument.
2.243 self._endCallFunc(temp_method)
2.244 @@ -812,6 +868,7 @@
2.245 temp_method = self._optimise_temp_storage()
2.246
2.247 # Add arguments.
2.248 + # NOTE: No support for defaults.
2.249
2.250 self.new_ops(temp1) # Explicit context as first argument.
2.251 self.new_ops(temp2)
2.252 @@ -852,6 +909,7 @@
2.253 temp_method = self._optimise_temp_storage()
2.254
2.255 # Add arguments.
2.256 + # NOTE: No support for defaults.
2.257
2.258 self.new_ops(temp2) # Explicit context as first argument.
2.259 self.new_ops(temp1)
3.1 --- a/micropython/data.py Sun May 11 21:50:30 2008 +0200
3.2 +++ b/micropython/data.py Mon May 12 23:54:16 2008 +0200
3.3 @@ -197,13 +197,23 @@
3.4 def __repr__(self):
3.5 return "Attr(%r, %r, %r, %r, %r)" % (self.position, self.parent, self.name, self.value, self.assignments)
3.6
3.7 +# Instances are special in that they need to be wrapped together with context in
3.8 +# a running program, but they are not generally constant.
3.9 +
3.10 +class Instance:
3.11 +
3.12 + "A placeholder indicating the involvement of an instance."
3.13 +
3.14 + def __repr__(self):
3.15 + return "Instance()"
3.16 +
3.17 class Constant:
3.18
3.19 "A superclass for all constant or context-free structures."
3.20
3.21 pass
3.22
3.23 -class Const(Constant):
3.24 +class Const(Constant, Instance):
3.25
3.26 "A constant object with no context."
3.27
3.28 @@ -774,14 +784,4 @@
3.29
3.30 return dict(self)
3.31
3.32 -# Instances are special in that they need to be wrapped together with context in
3.33 -# a running program, but they are not generally constant.
3.34 -
3.35 -class Instance:
3.36 -
3.37 - "A placeholder indicating the involvement of an instance."
3.38 -
3.39 - def __repr__(self):
3.40 - return "Instance()"
3.41 -
3.42 # vim: tabstop=4 expandtab shiftwidth=4
4.1 --- a/micropython/rsvp.py Sun May 11 21:50:30 2008 +0200
4.2 +++ b/micropython/rsvp.py Mon May 12 23:54:16 2008 +0200
4.3 @@ -27,8 +27,12 @@
4.4
4.5 stack_usage = 0
4.6
4.7 - def __init__(self, attr=None):
4.8 + def __init__(self, attr=None, context=None):
4.9 self.attr = attr
4.10 + if self.attr is not None and isinstance(self.attr, Attr):
4.11 + self.context = context or attr.parent
4.12 + else:
4.13 + self.context = context
4.14
4.15 def __repr__(self):
4.16 if self.attr is not None:
4.17 @@ -114,10 +118,13 @@
4.18
4.19 # Instructions operating on the value stack.
4.20
4.21 -class LoadConst(StackAdd, Address): "Load the constant, class, function, module from the specified location."
4.22 class Duplicate(StackAdd, Instruction): "Duplicate the top of the stack."
4.23 class Pop(StackRemove, Immediate): "Pop entries from the top of the stack."
4.24
4.25 +# Access to stored constant data.
4.26 +
4.27 +class LoadConst(StackAdd, Address): "Load the constant, class, function, module from the specified location."
4.28 +
4.29 # Access within an invocation frame.
4.30
4.31 class LoadName(StackAdd, SR): "Load the object from the given local attribute/variable."
4.32 @@ -127,8 +134,7 @@
4.33
4.34 # Access to address-relative data.
4.35
4.36 -class MakeObject(StackAdd, Instruction): "Make a new object."
4.37 -# ... DropObject not defined: Assume garbage collection.
4.38 +class MakeObject(StackAdd, Instruction): "Make a new object. There isn't a complementary DropObject."
4.39 class LoadAttr(AR): "Load the object from the given attribute."
4.40 class StoreAttr(StackRemove2, AR): "Store an object in the given attribute."
4.41 class LoadAttrIndex(Immediate): "Load the object for the attribute with the given index."
5.1 --- a/rsvp.py Sun May 11 21:50:30 2008 +0200
5.2 +++ b/rsvp.py Mon May 12 23:54:16 2008 +0200
5.3 @@ -236,9 +236,10 @@
5.4 """
5.5 LoadConst addr
5.6 Load the reference to memory: get the address addr, push the value onto
5.7 - the stack.
5.8 + the stack as a value without context.
5.9
5.10 This is useful for loading constants.
5.11 + NOTE: This assumes that constants are encoded with context.
5.12 """
5.13
5.14 addr = self.load(self.pc + 1)
6.1 --- a/tests/attributes.py Sun May 11 21:50:30 2008 +0200
6.2 +++ b/tests/attributes.py Mon May 12 23:54:16 2008 +0200
6.3 @@ -6,4 +6,7 @@
6.4 def __init__(self, value):
6.5 self.instattr = value
6.6
6.7 +C
6.8 +C.clsattr
6.9 +
6.10 # vim: tabstop=4 expandtab shiftwidth=4
7.1 --- a/tests/call_method.py Sun May 11 21:50:30 2008 +0200
7.2 +++ b/tests/call_method.py Mon May 12 23:54:16 2008 +0200
7.3 @@ -2,6 +2,12 @@
7.4
7.5 class C:
7.6 def f(self, a, b, c):
7.7 + self.g(a)
7.8 +
7.9 + def g(self, x):
7.10 + C.h(self, x)
7.11 +
7.12 + def h(self, p):
7.13 pass
7.14
7.15 c = C()