# HG changeset patch # User Paul Boddie # Date 1218932195 -7200 # Node ID f89d98f99ab1cdb30424343006d156b97f973a71 # Parent 15c15963cf292294bb78dc97149044398fafb141 Attempted to define the RSVP instructions more thoroughly. Distinguished between the origin of attributes as context and the eventual context likely to be used in a running program. Attempted to apply attribute context compatibility tests when loading and storing/defining attributes. diff -r 15c15963cf29 -r f89d98f99ab1 docs/structures.txt --- a/docs/structures.txt Wed Aug 13 19:42:59 2008 +0200 +++ b/docs/structures.txt Sun Aug 17 02:16:35 2008 +0200 @@ -39,8 +39,8 @@ There may be some scope for simplifying the above, to the detriment of Python compatibility, since the unbound vs. bound methods situation can be confusing. -Manipulating Values -------------------- +Acquiring Values +---------------- According to the table describing value acquisition, different instructions must implement different operations when acquiring values: @@ -52,31 +52,75 @@ module, constant object LoadAddress Load attribute from Classes, functions and modules - known object stored as cause the loaded attribute to be - an attribute retrieved unchanged; whereas + known object cause the loaded attribute to be + retrieved unchanged; whereas constants (representing instances) cause the constant to override the attribute's own context (since all attributes should belong to the constant's class hierarchy) + LoadAddressContext Override loaded context with a + predetermined object + LoadAttr Load attribute from Attributes with null contexts or - instance stored as an contexts compatible with the - attribute instance cause loaded attributes + instance contexts compatible with the + instance cause loaded attributes to combine the instance as context with the object from the attribute; other attributes have their context preserved LoadAttrIndex Load attribute from Classes, functions and modules as - unknown object stored the unknown object accessor cause - as an attribute the loaded attribute to be + unknown object the unknown object accessor cause + the loaded attribute to be retrieved unchanged; whereas instances cause the LoadAttr rules to apply -Consequently, a certain amount of run-time testing is required for both -LoadAttr and LoadAttrIndex. +A certain amount of run-time testing might be required for both LoadAttr and +LoadAttrIndex instructions. However, with certain restrictions in place around +class attributes, some simplifications are possible: + + * Since only class-originating attributes may cause context overriding, and + since class attributes may only be defined within class definitions, the + attributes whose context may be modified should be known at compile-time. + + * By recording a special context value for attributes whose context can be + overridden, this value can be tested efficiently at run-time where the + appropriate conditions are satisfied. + + * It should be possible to move the instance compatibility condition testing + to compile-time by testing the compatibility of the origin of an attribute + with the class on which it is stored. + +Storing Values +-------------- + +According to the table describing value acquisition, different instructions +must implement different operations when acquiring values: + + Instruction Purpose Context Operations + ----------- ------- ------------------ + + StoreAddress Store attribute in a Preserve context; note that no + known object test for class attribute + assignment should be necessary + since this instruction should only + be generated for module globals + + StoreAttr Store attribute in an Preserve context; note that no + instance test for class attribute + assignment should be necessary + since this instruction should only + be generated for self accesses + + StoreAttrIndex Store attribute in an Preserve context; since the index + unknown object lookup could yield a class + attribute, a test of the nature of + the nature of the structure is + necessary in order to prevent + assignments to classes Objects ------- diff -r 15c15963cf29 -r f89d98f99ab1 micropython/ast.py --- a/micropython/ast.py Wed Aug 13 19:42:59 2008 +0200 +++ b/micropython/ast.py Sun Aug 17 02:16:35 2008 +0200 @@ -477,15 +477,29 @@ except KeyError: attr = self.unit.parent.all_attributes()[attrname] - new_attr = attr.via_instance() - - # Only permit loading (not storing) of class attributes via self. - - if AddressContextInstruction is not None: - self.new_op(AddressContextInstruction(new_attr)) + + # Switch the context if the class attribute is compatible with + # the instance. + + if attr.defined_within_hierarchy(): + + # Only permit loading (not storing) of class attributes via self. + + if AddressContextInstruction is not None: + self.new_op(AddressContextInstruction(attr)) + else: + raise TranslateError(self.module.full_name(), node, + "Storing of class attribute %r via self not permitted." % attrname) + + # Preserve the context if the class attribute comes from an + # incompatible class. + else: - raise TranslateError(self.module.full_name(), node, - "Storing of class attribute %r via self not permitted." % attrname) + if AddressInstruction is not None: + self.new_op(AddressInstruction(attr)) + else: + raise TranslateError(self.module.full_name(), node, + "Storing of class attribute %r via self not permitted." % attrname) return 1 else: diff -r 15c15963cf29 -r f89d98f99ab1 micropython/data.py --- a/micropython/data.py Wed Aug 13 19:42:59 2008 +0200 +++ b/micropython/data.py Sun Aug 17 02:16:35 2008 +0200 @@ -117,7 +117,11 @@ def _context(self, value): - "Return the context to be used when storing the given 'value'." + """ + Return the context to be used when storing the given 'value'. + NOTE: This context is not likely to be useful when preparing an image + NOTE: since only instance contexts have significant effects at run-time. + """ if value is not None: return value.parent @@ -186,6 +190,17 @@ "An attribute entry having a context." def __init__(self, position, parent, context, name, value=None, assignments=None): + + """ + Initialise the attribute with the given 'position' within the collection + of attributes of its 'parent', indicating the 'context' or origin of the + attribute (where it was first defined), along with its 'name'. + + An optional 'value' indicates the typical contents of the attribute, and + the optional number of 'assignments' may be used to determine whether + the attribute is effectively constant. + """ + self.position = position self.parent = parent self.context = context @@ -235,11 +250,7 @@ # Where the attribute originates within the same hierarchy, use an # instance as the context. - if isinstance(self.parent, Class) and isinstance(self.context, Class) and ( - self.context is self.parent or - self.context in self.parent.descendants or - self.parent in self.context.descendants): - + if self.defined_within_hierarchy(): context = Instance() # Otherwise, preserve the existing context. @@ -254,6 +265,18 @@ else: return self + def defined_within_hierarchy(self): + + """ + Return whether the parent and context of the attribute belong to the + same class hierarchy. + """ + + return isinstance(self.parent, Class) and isinstance(self.context, Class) and ( + self.context is self.parent or + self.context in self.parent.descendants or + self.parent in self.context.descendants) + def __repr__(self): return "Attr(%r, %s, %s, %r, %s, %r)" % ( self.position, shortrepr(self.parent), shortrepr(self.context), @@ -270,6 +293,10 @@ def __init__(self): self.parent = None + # Image generation details. + + self.location = None + def __repr__(self): return "Instance()" @@ -289,10 +316,6 @@ self.value = value self.parent = None - # Image generation details. - - self.location = None - def __repr__(self): if self.location is not None: return "Const(%r, location=%r)" % (self.value, self.location) @@ -372,7 +395,11 @@ def _context(self, value): - "Return the context to be used when storing the given 'value'." + """ + Return the context to be used when storing the given 'value'. + NOTE: This context is not likely to be useful when preparing an image + NOTE: since only instance contexts have significant effects at run-time. + """ if value is not None: context = value.parent diff -r 15c15963cf29 -r f89d98f99ab1 micropython/rsvp.py --- a/micropython/rsvp.py Wed Aug 13 19:42:59 2008 +0200 +++ b/micropython/rsvp.py Sun Aug 17 02:16:35 2008 +0200 @@ -26,7 +26,7 @@ new_code = [] for item in code: if isinstance(item, Attr): - new_code.append((item.context and item.context.location, item.value and item.value.location)) + new_code.append((None, item.value and item.value.location)) # no useful context is provided else: new_code.append(item) return new_code diff -r 15c15963cf29 -r f89d98f99ab1 micropython/table.py --- a/micropython/table.py Wed Aug 13 19:42:59 2008 +0200 +++ b/micropython/table.py Sun Aug 17 02:16:35 2008 +0200 @@ -42,6 +42,9 @@ def __len__(self): return len(self.displaced) + def __getitem__(self, i): + return self.displaced[i] + # Simulation methods. def access(self, objname, attrname): diff -r 15c15963cf29 -r f89d98f99ab1 rsvp.py --- a/rsvp.py Wed Aug 13 19:42:59 2008 +0200 +++ b/rsvp.py Sun Aug 17 02:16:35 2008 +0200 @@ -81,6 +81,8 @@ # Registers. + self.instruction = None + self.operand = None self.value = None self.status = None self.source = None @@ -145,35 +147,45 @@ "Execute code in the memory at the current PC address." - instruction = self.load(self.pc).__class__.__name__ + self.instruction = self.load(self.pc) + self.operand = self.instruction.get_operand() + + instruction_name = self.instruction.__class__.__name__ if self.debug: - print "%8d %s" % (self.pc, instruction) + print "%8d %s" % (self.pc, instruction_name) - method = self.get_method(instruction) + method = self.get_method(instruction_name) # Process any inputs of the instruction. - self.process_inputs(instruction) - method() + self.process_inputs() + next_pc = method() - def get_method(self, instruction): + # Update the program counter. - "Return the handler method for the given 'instruction'." + if next_pc is None: + self.pc += 1 + else: + self.pc = next_pc - method = getattr(self, instruction, None) + def get_method(self, instruction_name): + + "Return the handler method for the given 'instruction_name'." + + method = getattr(self, instruction_name, None) if method is None: - raise IllegalInstruction, (self.pc, instruction) + raise IllegalInstruction, (self.pc, instruction_name) return method - def process_inputs(self, instruction): + def process_inputs(self): """ - Process any inputs of the 'instruction'. This permits any directly + Process any inputs of the current instruction. This permits any directly connected sub-instructions to produce the effects that separate instructions would otherwise have. """ - for input in (instruction.input, instruction.source): + for input in (self.instruction.input, self.instruction.source): if input is not None: method = self.get_method(input) method() @@ -188,88 +200,87 @@ if isinstance(addr, str): getattr(self, addr)() - self.pc = next + return next else: self.push_pc(self.pc + 2) - self.pc = addr + return addr # Instructions. def LoadConst(self): - op = self.load(self.pc) - addr = op.get_operand() - self.value = (None, addr) - self.pc += 1 + self.value = None, self.operand def LoadName(self): - n = self.load(self.pc).get_operand() frame = self.local_sp_stack[-1] - self.value = self.frame_stack[frame + n] - self.pc += 1 + self.value = self.frame_stack[frame + self.operand] def StoreName(self): - n = self.load(self.pc).get_operand() frame = self.local_sp_stack[-1] - self.frame_stack[frame + n] = self.value - self.pc += 1 + self.frame_stack[frame + self.operand] = self.value LoadTemp = LoadName StoreTemp = StoreName def LoadAddress(self): - addr = self.load(self.pc).get_operand() - self.value = self.load(addr) - self.pc += 1 + # Preserve context (potentially null). + self.value = self.load(self.operand) def LoadAddressContext(self): - addr = self.load(self.pc).get_operand() - value = self.load(addr) - self.value = (self.value, value[1]) # replace the context with the current value - self.pc += 1 + value = self.load(self.operand) + # Replace the context with the current value. + self.value = self.value[1], value[1] def StoreAddress(self): - addr = self.load(self.pc).get_operand() - self.save(addr, self.value) - self.pc += 1 + # Preserve context. + self.save(self.operand, self.value) def MakeObject(self): - n = self.load(self.pc).get_operand() - self.value = self.new(n) - self.pc += 1 + # Introduce null context for new object. + self.value = None, self.new(self.operand) def LoadAttr(self): - n = self.load(self.pc).get_operand() context, ref = self.value - self.value = self.load(ref + n) - self.pc += 1 + loaded_context, loaded_ref = self.load(ref + self.operand) + if loaded_context is None: + # Override null context with owning instance. + self.value = ref, loaded_ref + else: + self.value = loaded_context, loaded_ref def StoreAttr(self): - n = self.load(self.pc).get_operand() + context, ref = self.value + self.save(ref + self.operand, self.source) + + def LoadAttrIndex(self): context, ref = self.value - self.save(ref + n, self.source) - self.pc += 1 - - def LoadAttrIndex(self): pass + code = self.load(ref) # + 0 (the classcode) + element = self.objtable[code + self.operand] + found_code, found_attr = element + if found_code == code: + # NOTE: The found context should be tested against the object's context. + # NOTE: Compatibility between contexts should be stored in the table, or + # NOTE: an incompatible context should be preserved. + self.value = ref, found_ref + else: + # NOTE: This should cause an attribute error. + self.value = None def StoreAttrIndex(self): pass def MakeFrame(self): - n = self.load(self.pc).get_operand() self.invocation_sp_stack.append(len(self.frame_stack)) - self.frame_stack.extend([None] * n) - self.pc += 1 + self.frame_stack.extend([None] * self.operand) def JumpWithFrame(self): self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame context, ref = self.value - self.jump(ref.code_location, self.pc + 1) # return to the instruction after this one + return self.jump(ref.code_location, self.pc + 1) # return to the instruction after this one def DropFrame(self): result = self.pull() self.local_sp_stack.pop() frame = self.invocation_sp_stack.pop() self.frame_stack = self.frame_stack[:frame] # reset stack before call - self.pc += 1 def CheckFrame(self): pass @@ -278,7 +289,6 @@ def LoadContext(self): context, ref = self.value self.push((None, context)) - self.pc += 1 def CheckSelf(self): pass @@ -303,27 +313,18 @@ self.value = self.status def Jump(self): - addr = self.load(self.pc).get_operand() - self.pc = addr + return self.operand def JumpIfTrue(self): - addr = self.load(self.pc).get_operand() if self.status: - self.pc = addr - else: - self.pc += 1 + return self.operand def JumpIfFalse(self): - addr = self.load(self.pc).get_operand() if not self.status: - self.pc = addr - else: - self.pc += 1 + return self.operand def StoreFrame(self): - pos = self.load(self.pc).get_operand() frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame - self.frame_stack[frame + pos] = self.value - self.pc += 1 + self.frame_stack[frame + self.operand] = self.value # vim: tabstop=4 expandtab shiftwidth=4