# HG changeset patch # User Paul Boddie # Date 1213038577 -7200 # Node ID ae889a05bc32b987e52d60f667d96cabe7f15eac # Parent dd32f05e3a8c9f63fd0d654b20f887f79f85f56e Added some more notes about invocations. Attempted to improve the invocation code, tidying up the argument preparation somewhat. Fixed constant registration in the inspection code. Prevented repeated attribute finalisation in the data classes. Tidied up the RSVP instructions and attempted to support some more of them. diff -r dd32f05e3a8c -r ae889a05bc32 docs/invocation.txt --- a/docs/invocation.txt Sat Jun 07 21:55:24 2008 +0200 +++ b/docs/invocation.txt Mon Jun 09 21:09:37 2008 +0200 @@ -11,7 +11,7 @@ Note that f is never fixed before run-time in Python. -Comparison to C: +Comparison to invocations in C: f(1, 2, 3) # positional, f known at compile-time f(1, 2, 3) # positional, f is appropriate function pointer @@ -46,3 +46,28 @@ These cases require additional structures to be created, potentially at run-time. + +Methods vs. functions: + + f(obj, 1, 2) # f known as function at compile-time: + # f(obj, 1, 2) + # f known as C.m at compile-time: + # m(obj "assert isinstance(obj, C)", 1, 2) + # f not known at compiler-time: + # f(, obj, 1, 2) for instance-accessed methods + # f(obj, 1, 2) for class-accessed methods + # f(obj, 1, 2) for functions + + (Could either have universal context usage even for functions, which would + ignore them, or attempt to remove contexts when functions are called.) + +Functions as methods: + + def f(x, y, z): ... + class C: + m = f + c = C() + ... + f(obj, 1, 2) # no restrictions on obj + obj.m(1, 2) # f(obj, 1, 2) + C.m(obj, 1, 2) # f(obj "assert isinstance(obj, C)", 1, 2) diff -r dd32f05e3a8c -r ae889a05bc32 micropython/ast.py --- a/micropython/ast.py Sat Jun 07 21:55:24 2008 +0200 +++ b/micropython/ast.py Mon Jun 09 21:09:37 2008 +0200 @@ -346,19 +346,12 @@ # Where a target or context are not known, load the target and context. if context is None: - continue_label = self.new_label() self.new_ops(temp) self.new_op(LoadContext()) - self.new_op(CheckContext()) - self.new_op(JumpIfTrue(continue_label)) - # Where the context is inappropriate, drop the incomplete frame and - # raise an exception. + # Check to see if the context is needed for the target. - self.new_op(DropFrame()) - self.dispatch(compiler.ast.Name("TypeError")) - self.new_op(RaiseException()) - self.set_label(continue_label) + self.new_op(CheckContext()) # Where an instance is known to be the context, load the context. @@ -393,7 +386,10 @@ else: ncontext = 0 - for frame_pos, arg in enumerate(args): + first = 1 + frame_pos = ncontext + + for arg in args: # Handle positional and keyword arguments separately. @@ -433,8 +429,9 @@ # Add space for arguments appearing before this one. - if frame_pos + ncontext < pos: - self.new_op(ReserveFrame(pos - frame_pos - ncontext)) + if frame_pos < pos: + self.new_op(ReserveFrame(pos - frame_pos)) + frame_pos = pos # Generate code for the keyword and the positioning # operation. @@ -444,7 +441,7 @@ # If the position corresponds to the current frame element, # skip generating the store instruction. - if frame_pos + ncontext > pos: + if frame_pos > pos: self.new_op(StoreFrame(pos)) # Otherwise, generate the code needed to obtain the details of @@ -479,7 +476,23 @@ self.dispatch(arg) employed_positions.add(frame_pos + ncontext) - frame_pos = len(args) + ncontext + # Check to see if the first argument is appropriate (compatible with + # the # target where methods are being invoked via classes). + + if first and context is None: + continue_label = self.new_label() + self.new_op(CheckSelf()) + self.new_op(JumpIfTrue(continue_label)) + + # Where the context is inappropriate, drop the incomplete frame and + # raise an exception. + + self.new_op(DropFrame()) + self.dispatch(compiler.ast.Name("TypeError")) + self.new_op(RaiseException()) + self.set_label(continue_label) + + frame_pos += 1 # NOTE: Extra keywords are not supported. # NOTE: Somehow, the above needs to be combined with * arguments. diff -r dd32f05e3a8c -r ae889a05bc32 micropython/data.py --- a/micropython/data.py Sat Jun 07 21:55:24 2008 +0200 +++ b/micropython/data.py Mon Jun 09 21:09:37 2008 +0200 @@ -40,8 +40,8 @@ These specific methods are useful in certain situations. -The above classes also provide a 'node' attribute, indicating the AST node where -each such object is defined. +The above classes also provide an 'astnode' attribute, indicating the AST node +where each such object is defined. """ from micropython.common import * @@ -62,6 +62,7 @@ self.namespace = {} self.globals = set() self.global_namespace = global_namespace + self.finalised = 0 def __getitem__(self, name): return self.namespace[name] @@ -155,6 +156,9 @@ return None def attributes_as_list(self): + + "Return the attributes in a list." + self.finalise_attributes() l = [None] * len(self.keys()) for attr in self.values(): @@ -165,11 +169,16 @@ "Make sure all attributes are fully defined." + if self.finalised: + return + # The default action is to assign attribute positions sequentially. for i, attr in enumerate(self.values()): attr.position = i + self.finalised = 1 + # Program data structures. There are two separate kinds of structures: those # with context, which are the values manipulated by programs, and those without # context, which are typically constant things which are stored alongside the @@ -381,8 +390,12 @@ "Make sure that all attributes are fully defined." + if self.finalised: + return + self.finalise_class_attributes() self.finalise_instance_attributes() + self.finalised = 1 def get_instantiator(self): @@ -783,6 +796,7 @@ attr.position = i + j self.stack_local_usage = i + self.finalised = 1 def function_from_method(self): diff -r dd32f05e3a8c -r ae889a05bc32 micropython/inspect.py --- a/micropython/inspect.py Sat Jun 07 21:55:24 2008 +0200 +++ b/micropython/inspect.py Mon Jun 09 21:09:37 2008 +0200 @@ -224,9 +224,6 @@ self.dispatch(n) return Instance() - def _visitConst(self, node): - return self._make_constant(node.value) - def _make_constant(self, value): if not self.constant_values.has_key(value): const = Const(value) @@ -390,7 +387,7 @@ visitCompare = OP def visitConst(self, node): - return self._visitConst(node) + return self._make_constant(node.value) visitContinue = NOP @@ -502,7 +499,7 @@ def visitKeyword(self, node): self.dispatch(node.expr) - self._visitConst(node) + self._make_constant(node.name) self.keyword_names.add(node.name) return None diff -r dd32f05e3a8c -r ae889a05bc32 micropython/rsvp.py --- a/micropython/rsvp.py Sat Jun 07 21:55:24 2008 +0200 +++ b/micropython/rsvp.py Mon Jun 09 21:09:37 2008 +0200 @@ -155,7 +155,7 @@ # Instructions operating on the value stack. class Duplicate(StackAdd, Instruction): "Duplicate the top of the stack." -class Pop(StackRemove, Immediate): "Pop entries from the top of the stack." +class Pop(StackRemove, Instruction): "Pop entries from the top of the stack." # Access to stored constant data. @@ -197,6 +197,7 @@ class LoadContext(Instruction): "Load the context of an invocation." class CheckContext(Instruction): """Check the context of an invocation against the target, potentially discarding the context.""" +class CheckSelf(Instruction): "Check the first argument of an invocation against the target." class RaiseException(Instruction): "Raise an exception." class Return(Instruction): "Return a value from a subprogram." class CheckException(Instruction): "Check the raised exception against another." diff -r dd32f05e3a8c -r ae889a05bc32 rsvp.py --- a/rsvp.py Sat Jun 07 21:55:24 2008 +0200 +++ b/rsvp.py Mon Jun 09 21:09:37 2008 +0200 @@ -50,7 +50,7 @@ class EmptyPCStack(Exception): pass -class EmptyMetadataStack(Exception): +class EmptyFrameStack(Exception): pass class RSVPMachine: @@ -149,7 +149,7 @@ self.frame_sp -= 1 return self.frame_stack.pop() except IndexError: - raise EmptyMetadataStack + raise EmptyFrameStack def run(self): @@ -170,7 +170,7 @@ print "%8d %s" % (self.pc, instruction) method = getattr(self, instruction, None) if method is None: - raise IllegalInstruction, self.pc + raise IllegalInstruction, (self.pc, instruction) else: method() @@ -191,11 +191,22 @@ # Instructions. + def Pop(self): + self.pull() + self.pc += 1 + def MakeFrame(self): top = len(self.value_stack) self.add_frame(top) self.pc += 1 + def ReserveFrame(self): + n = self.load(self.pc).get_operand() + while n > 0: + self.push(None) + n -= 1 + self.pc += 1 + def DropFrame(self): result = self.pull() frame = self.pull_frame() @@ -204,9 +215,34 @@ self.pc += 1 def JumpWithFrame(self): - addr = self.pull() + attr = self.pull() self.frame_sp += 1 # adopt the added frame - self.jump(addr, self.pc + 1) # return to the instruction after this one + target_location = attr[0] + target = self.load(target_location) + self.jump(target.code_location, self.pc + 1) # return to the instruction after this one + + def LoadContext(self): + + """ + LoadContext + Load context from top of stack: get the context from the value on the + top of the stack, pushing it onto the stack. + """ + + attr = self.value_stack[-1] + self.push((attr[1], None)) + self.pc += 1 + + def CheckContext(self): + + """ + CheckContext + Check the context: check the context on the top of the stack + """ + + attr = self.value_stack[-1] + # NOTE: To be written. + self.pc += 1 def LoadName(self): @@ -249,7 +285,7 @@ """ addr = self.load(self.pc).get_operand() - value = (None, addr) + value = (addr, None) self.push(value) self.pc += 1