1.1 --- a/docs/structures.txt Wed Aug 13 19:42:59 2008 +0200
1.2 +++ b/docs/structures.txt Sun Aug 17 02:16:35 2008 +0200
1.3 @@ -39,8 +39,8 @@
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 +Acquiring Values
1.10 +----------------
1.11
1.12 According to the table describing value acquisition, different instructions
1.13 must implement different operations when acquiring values:
1.14 @@ -52,31 +52,75 @@
1.15 module, constant object
1.16
1.17 LoadAddress Load attribute from Classes, functions and modules
1.18 - known object stored as cause the loaded attribute to be
1.19 - an attribute retrieved unchanged; whereas
1.20 + known object cause the loaded attribute to be
1.21 + retrieved unchanged; whereas
1.22 constants (representing instances)
1.23 cause the constant to override the
1.24 attribute's own context (since all
1.25 attributes should belong to the
1.26 constant's class hierarchy)
1.27
1.28 + LoadAddressContext Override loaded context with a
1.29 + predetermined object
1.30 +
1.31 LoadAttr Load attribute from Attributes with null contexts or
1.32 - instance stored as an contexts compatible with the
1.33 - attribute instance cause loaded attributes
1.34 + instance contexts compatible with the
1.35 + instance cause loaded attributes
1.36 to combine the instance as context
1.37 with the object from the
1.38 attribute; other attributes have
1.39 their context preserved
1.40
1.41 LoadAttrIndex Load attribute from Classes, functions and modules as
1.42 - unknown object stored the unknown object accessor cause
1.43 - as an attribute the loaded attribute to be
1.44 + unknown object the unknown object accessor cause
1.45 + the loaded attribute to be
1.46 retrieved unchanged; whereas
1.47 instances cause the LoadAttr rules
1.48 to apply
1.49
1.50 -Consequently, a certain amount of run-time testing is required for both
1.51 -LoadAttr and LoadAttrIndex.
1.52 +A certain amount of run-time testing might be required for both LoadAttr and
1.53 +LoadAttrIndex instructions. However, with certain restrictions in place around
1.54 +class attributes, some simplifications are possible:
1.55 +
1.56 + * Since only class-originating attributes may cause context overriding, and
1.57 + since class attributes may only be defined within class definitions, the
1.58 + attributes whose context may be modified should be known at compile-time.
1.59 +
1.60 + * By recording a special context value for attributes whose context can be
1.61 + overridden, this value can be tested efficiently at run-time where the
1.62 + appropriate conditions are satisfied.
1.63 +
1.64 + * It should be possible to move the instance compatibility condition testing
1.65 + to compile-time by testing the compatibility of the origin of an attribute
1.66 + with the class on which it is stored.
1.67 +
1.68 +Storing Values
1.69 +--------------
1.70 +
1.71 +According to the table describing value acquisition, different instructions
1.72 +must implement different operations when acquiring values:
1.73 +
1.74 + Instruction Purpose Context Operations
1.75 + ----------- ------- ------------------
1.76 +
1.77 + StoreAddress Store attribute in a Preserve context; note that no
1.78 + known object test for class attribute
1.79 + assignment should be necessary
1.80 + since this instruction should only
1.81 + be generated for module globals
1.82 +
1.83 + StoreAttr Store attribute in an Preserve context; note that no
1.84 + instance test for class attribute
1.85 + assignment should be necessary
1.86 + since this instruction should only
1.87 + be generated for self accesses
1.88 +
1.89 + StoreAttrIndex Store attribute in an Preserve context; since the index
1.90 + unknown object lookup could yield a class
1.91 + attribute, a test of the nature of
1.92 + the nature of the structure is
1.93 + necessary in order to prevent
1.94 + assignments to classes
1.95
1.96 Objects
1.97 -------
2.1 --- a/micropython/ast.py Wed Aug 13 19:42:59 2008 +0200
2.2 +++ b/micropython/ast.py Sun Aug 17 02:16:35 2008 +0200
2.3 @@ -477,15 +477,29 @@
2.4
2.5 except KeyError:
2.6 attr = self.unit.parent.all_attributes()[attrname]
2.7 - new_attr = attr.via_instance()
2.8 -
2.9 - # Only permit loading (not storing) of class attributes via self.
2.10 -
2.11 - if AddressContextInstruction is not None:
2.12 - self.new_op(AddressContextInstruction(new_attr))
2.13 +
2.14 + # Switch the context if the class attribute is compatible with
2.15 + # the instance.
2.16 +
2.17 + if attr.defined_within_hierarchy():
2.18 +
2.19 + # Only permit loading (not storing) of class attributes via self.
2.20 +
2.21 + if AddressContextInstruction is not None:
2.22 + self.new_op(AddressContextInstruction(attr))
2.23 + else:
2.24 + raise TranslateError(self.module.full_name(), node,
2.25 + "Storing of class attribute %r via self not permitted." % attrname)
2.26 +
2.27 + # Preserve the context if the class attribute comes from an
2.28 + # incompatible class.
2.29 +
2.30 else:
2.31 - raise TranslateError(self.module.full_name(), node,
2.32 - "Storing of class attribute %r via self not permitted." % attrname)
2.33 + if AddressInstruction is not None:
2.34 + self.new_op(AddressInstruction(attr))
2.35 + else:
2.36 + raise TranslateError(self.module.full_name(), node,
2.37 + "Storing of class attribute %r via self not permitted." % attrname)
2.38
2.39 return 1
2.40 else:
3.1 --- a/micropython/data.py Wed Aug 13 19:42:59 2008 +0200
3.2 +++ b/micropython/data.py Sun Aug 17 02:16:35 2008 +0200
3.3 @@ -117,7 +117,11 @@
3.4
3.5 def _context(self, value):
3.6
3.7 - "Return the context to be used when storing the given 'value'."
3.8 + """
3.9 + Return the context to be used when storing the given 'value'.
3.10 + NOTE: This context is not likely to be useful when preparing an image
3.11 + NOTE: since only instance contexts have significant effects at run-time.
3.12 + """
3.13
3.14 if value is not None:
3.15 return value.parent
3.16 @@ -186,6 +190,17 @@
3.17 "An attribute entry having a context."
3.18
3.19 def __init__(self, position, parent, context, name, value=None, assignments=None):
3.20 +
3.21 + """
3.22 + Initialise the attribute with the given 'position' within the collection
3.23 + of attributes of its 'parent', indicating the 'context' or origin of the
3.24 + attribute (where it was first defined), along with its 'name'.
3.25 +
3.26 + An optional 'value' indicates the typical contents of the attribute, and
3.27 + the optional number of 'assignments' may be used to determine whether
3.28 + the attribute is effectively constant.
3.29 + """
3.30 +
3.31 self.position = position
3.32 self.parent = parent
3.33 self.context = context
3.34 @@ -235,11 +250,7 @@
3.35 # Where the attribute originates within the same hierarchy, use an
3.36 # instance as the context.
3.37
3.38 - if isinstance(self.parent, Class) and isinstance(self.context, Class) and (
3.39 - self.context is self.parent or
3.40 - self.context in self.parent.descendants or
3.41 - self.parent in self.context.descendants):
3.42 -
3.43 + if self.defined_within_hierarchy():
3.44 context = Instance()
3.45
3.46 # Otherwise, preserve the existing context.
3.47 @@ -254,6 +265,18 @@
3.48 else:
3.49 return self
3.50
3.51 + def defined_within_hierarchy(self):
3.52 +
3.53 + """
3.54 + Return whether the parent and context of the attribute belong to the
3.55 + same class hierarchy.
3.56 + """
3.57 +
3.58 + return isinstance(self.parent, Class) and isinstance(self.context, Class) and (
3.59 + self.context is self.parent or
3.60 + self.context in self.parent.descendants or
3.61 + self.parent in self.context.descendants)
3.62 +
3.63 def __repr__(self):
3.64 return "Attr(%r, %s, %s, %r, %s, %r)" % (
3.65 self.position, shortrepr(self.parent), shortrepr(self.context),
3.66 @@ -270,6 +293,10 @@
3.67 def __init__(self):
3.68 self.parent = None
3.69
3.70 + # Image generation details.
3.71 +
3.72 + self.location = None
3.73 +
3.74 def __repr__(self):
3.75 return "Instance()"
3.76
3.77 @@ -289,10 +316,6 @@
3.78 self.value = value
3.79 self.parent = None
3.80
3.81 - # Image generation details.
3.82 -
3.83 - self.location = None
3.84 -
3.85 def __repr__(self):
3.86 if self.location is not None:
3.87 return "Const(%r, location=%r)" % (self.value, self.location)
3.88 @@ -372,7 +395,11 @@
3.89
3.90 def _context(self, value):
3.91
3.92 - "Return the context to be used when storing the given 'value'."
3.93 + """
3.94 + Return the context to be used when storing the given 'value'.
3.95 + NOTE: This context is not likely to be useful when preparing an image
3.96 + NOTE: since only instance contexts have significant effects at run-time.
3.97 + """
3.98
3.99 if value is not None:
3.100 context = value.parent
4.1 --- a/micropython/rsvp.py Wed Aug 13 19:42:59 2008 +0200
4.2 +++ b/micropython/rsvp.py Sun Aug 17 02:16:35 2008 +0200
4.3 @@ -26,7 +26,7 @@
4.4 new_code = []
4.5 for item in code:
4.6 if isinstance(item, Attr):
4.7 - new_code.append((item.context and item.context.location, item.value and item.value.location))
4.8 + new_code.append((None, item.value and item.value.location)) # no useful context is provided
4.9 else:
4.10 new_code.append(item)
4.11 return new_code
5.1 --- a/micropython/table.py Wed Aug 13 19:42:59 2008 +0200
5.2 +++ b/micropython/table.py Sun Aug 17 02:16:35 2008 +0200
5.3 @@ -42,6 +42,9 @@
5.4 def __len__(self):
5.5 return len(self.displaced)
5.6
5.7 + def __getitem__(self, i):
5.8 + return self.displaced[i]
5.9 +
5.10 # Simulation methods.
5.11
5.12 def access(self, objname, attrname):
6.1 --- a/rsvp.py Wed Aug 13 19:42:59 2008 +0200
6.2 +++ b/rsvp.py Sun Aug 17 02:16:35 2008 +0200
6.3 @@ -81,6 +81,8 @@
6.4
6.5 # Registers.
6.6
6.7 + self.instruction = None
6.8 + self.operand = None
6.9 self.value = None
6.10 self.status = None
6.11 self.source = None
6.12 @@ -145,35 +147,45 @@
6.13
6.14 "Execute code in the memory at the current PC address."
6.15
6.16 - instruction = self.load(self.pc).__class__.__name__
6.17 + self.instruction = self.load(self.pc)
6.18 + self.operand = self.instruction.get_operand()
6.19 +
6.20 + instruction_name = self.instruction.__class__.__name__
6.21 if self.debug:
6.22 - print "%8d %s" % (self.pc, instruction)
6.23 + print "%8d %s" % (self.pc, instruction_name)
6.24
6.25 - method = self.get_method(instruction)
6.26 + method = self.get_method(instruction_name)
6.27
6.28 # Process any inputs of the instruction.
6.29
6.30 - self.process_inputs(instruction)
6.31 - method()
6.32 + self.process_inputs()
6.33 + next_pc = method()
6.34
6.35 - def get_method(self, instruction):
6.36 + # Update the program counter.
6.37
6.38 - "Return the handler method for the given 'instruction'."
6.39 + if next_pc is None:
6.40 + self.pc += 1
6.41 + else:
6.42 + self.pc = next_pc
6.43
6.44 - method = getattr(self, instruction, None)
6.45 + def get_method(self, instruction_name):
6.46 +
6.47 + "Return the handler method for the given 'instruction_name'."
6.48 +
6.49 + method = getattr(self, instruction_name, None)
6.50 if method is None:
6.51 - raise IllegalInstruction, (self.pc, instruction)
6.52 + raise IllegalInstruction, (self.pc, instruction_name)
6.53 return method
6.54
6.55 - def process_inputs(self, instruction):
6.56 + def process_inputs(self):
6.57
6.58 """
6.59 - Process any inputs of the 'instruction'. This permits any directly
6.60 + Process any inputs of the current instruction. This permits any directly
6.61 connected sub-instructions to produce the effects that separate
6.62 instructions would otherwise have.
6.63 """
6.64
6.65 - for input in (instruction.input, instruction.source):
6.66 + for input in (self.instruction.input, self.instruction.source):
6.67 if input is not None:
6.68 method = self.get_method(input)
6.69 method()
6.70 @@ -188,88 +200,87 @@
6.71
6.72 if isinstance(addr, str):
6.73 getattr(self, addr)()
6.74 - self.pc = next
6.75 + return next
6.76 else:
6.77 self.push_pc(self.pc + 2)
6.78 - self.pc = addr
6.79 + return addr
6.80
6.81 # Instructions.
6.82
6.83 def LoadConst(self):
6.84 - op = self.load(self.pc)
6.85 - addr = op.get_operand()
6.86 - self.value = (None, addr)
6.87 - self.pc += 1
6.88 + self.value = None, self.operand
6.89
6.90 def LoadName(self):
6.91 - n = self.load(self.pc).get_operand()
6.92 frame = self.local_sp_stack[-1]
6.93 - self.value = self.frame_stack[frame + n]
6.94 - self.pc += 1
6.95 + self.value = self.frame_stack[frame + self.operand]
6.96
6.97 def StoreName(self):
6.98 - n = self.load(self.pc).get_operand()
6.99 frame = self.local_sp_stack[-1]
6.100 - self.frame_stack[frame + n] = self.value
6.101 - self.pc += 1
6.102 + self.frame_stack[frame + self.operand] = self.value
6.103
6.104 LoadTemp = LoadName
6.105 StoreTemp = StoreName
6.106
6.107 def LoadAddress(self):
6.108 - addr = self.load(self.pc).get_operand()
6.109 - self.value = self.load(addr)
6.110 - self.pc += 1
6.111 + # Preserve context (potentially null).
6.112 + self.value = self.load(self.operand)
6.113
6.114 def LoadAddressContext(self):
6.115 - addr = self.load(self.pc).get_operand()
6.116 - value = self.load(addr)
6.117 - self.value = (self.value, value[1]) # replace the context with the current value
6.118 - self.pc += 1
6.119 + value = self.load(self.operand)
6.120 + # Replace the context with the current value.
6.121 + self.value = self.value[1], value[1]
6.122
6.123 def StoreAddress(self):
6.124 - addr = self.load(self.pc).get_operand()
6.125 - self.save(addr, self.value)
6.126 - self.pc += 1
6.127 + # Preserve context.
6.128 + self.save(self.operand, self.value)
6.129
6.130 def MakeObject(self):
6.131 - n = self.load(self.pc).get_operand()
6.132 - self.value = self.new(n)
6.133 - self.pc += 1
6.134 + # Introduce null context for new object.
6.135 + self.value = None, self.new(self.operand)
6.136
6.137 def LoadAttr(self):
6.138 - n = self.load(self.pc).get_operand()
6.139 context, ref = self.value
6.140 - self.value = self.load(ref + n)
6.141 - self.pc += 1
6.142 + loaded_context, loaded_ref = self.load(ref + self.operand)
6.143 + if loaded_context is None:
6.144 + # Override null context with owning instance.
6.145 + self.value = ref, loaded_ref
6.146 + else:
6.147 + self.value = loaded_context, loaded_ref
6.148
6.149 def StoreAttr(self):
6.150 - n = self.load(self.pc).get_operand()
6.151 + context, ref = self.value
6.152 + self.save(ref + self.operand, self.source)
6.153 +
6.154 + def LoadAttrIndex(self):
6.155 context, ref = self.value
6.156 - self.save(ref + n, self.source)
6.157 - self.pc += 1
6.158 -
6.159 - def LoadAttrIndex(self): pass
6.160 + code = self.load(ref) # + 0 (the classcode)
6.161 + element = self.objtable[code + self.operand]
6.162 + found_code, found_attr = element
6.163 + if found_code == code:
6.164 + # NOTE: The found context should be tested against the object's context.
6.165 + # NOTE: Compatibility between contexts should be stored in the table, or
6.166 + # NOTE: an incompatible context should be preserved.
6.167 + self.value = ref, found_ref
6.168 + else:
6.169 + # NOTE: This should cause an attribute error.
6.170 + self.value = None
6.171
6.172 def StoreAttrIndex(self): pass
6.173
6.174 def MakeFrame(self):
6.175 - n = self.load(self.pc).get_operand()
6.176 self.invocation_sp_stack.append(len(self.frame_stack))
6.177 - self.frame_stack.extend([None] * n)
6.178 - self.pc += 1
6.179 + self.frame_stack.extend([None] * self.operand)
6.180
6.181 def JumpWithFrame(self):
6.182 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame
6.183 context, ref = self.value
6.184 - self.jump(ref.code_location, self.pc + 1) # return to the instruction after this one
6.185 + return self.jump(ref.code_location, self.pc + 1) # return to the instruction after this one
6.186
6.187 def DropFrame(self):
6.188 result = self.pull()
6.189 self.local_sp_stack.pop()
6.190 frame = self.invocation_sp_stack.pop()
6.191 self.frame_stack = self.frame_stack[:frame] # reset stack before call
6.192 - self.pc += 1
6.193
6.194 def CheckFrame(self): pass
6.195
6.196 @@ -278,7 +289,6 @@
6.197 def LoadContext(self):
6.198 context, ref = self.value
6.199 self.push((None, context))
6.200 - self.pc += 1
6.201
6.202 def CheckSelf(self): pass
6.203
6.204 @@ -303,27 +313,18 @@
6.205 self.value = self.status
6.206
6.207 def Jump(self):
6.208 - addr = self.load(self.pc).get_operand()
6.209 - self.pc = addr
6.210 + return self.operand
6.211
6.212 def JumpIfTrue(self):
6.213 - addr = self.load(self.pc).get_operand()
6.214 if self.status:
6.215 - self.pc = addr
6.216 - else:
6.217 - self.pc += 1
6.218 + return self.operand
6.219
6.220 def JumpIfFalse(self):
6.221 - addr = self.load(self.pc).get_operand()
6.222 if not self.status:
6.223 - self.pc = addr
6.224 - else:
6.225 - self.pc += 1
6.226 + return self.operand
6.227
6.228 def StoreFrame(self):
6.229 - pos = self.load(self.pc).get_operand()
6.230 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame
6.231 - self.frame_stack[frame + pos] = self.value
6.232 - self.pc += 1
6.233 + self.frame_stack[frame + self.operand] = self.value
6.234
6.235 # vim: tabstop=4 expandtab shiftwidth=4