# HG changeset patch # User Paul Boddie # Date 1243788280 -7200 # Node ID 2ede6db71cebc9604905dfc15df550ac5e25b168 # Parent b8fea09316977c8282a7360dd004bb38580ecdb9 Fixed the structure of "if" statement code. Fixed comparison method definitions for equality and inequality. Added optimisation where exception handlers are pushed and popped without any code defined between these operations. Reviewed exception handling including that employed around operators, introducing PopHandler and ClearException instructions in order to properly maintain exception state. Added RSVP support for some integer comparisons. Split the compare2 test program into separate tests. Added some exception-related documentation. diff -r b8fea0931697 -r 2ede6db71ceb docs/exceptions.txt --- a/docs/exceptions.txt Sat May 30 20:30:28 2009 +0200 +++ b/docs/exceptions.txt Sun May 31 18:44:40 2009 +0200 @@ -1,5 +1,16 @@ Exception Handling ------------------- +================== + +Active Exceptions +================= + +When an exception is raised, an exception instance is defined as the active +exception. This active exception remains in force until either a new exception +is raised in any handling of the exception, or upon exit of a handler where +the active exception has been caught (thus resetting the active exception). + +Handlers +======== An exception handler stack is defined such that when a try...except or try...finally block is entered, a new handler is defined. @@ -13,3 +24,12 @@ If no handler is defined when an exception is raised or re-raised, the program should terminate. This might be done by having a "handler #0" which explicitly terminates the program. + +Instructions +------------ + +PushHandler(block) defines an active handler at the location indicated by the +given block. + +PopHandler removes the active handler at or after the location indicated by +the previously given block. diff -r b8fea0931697 -r 2ede6db71ceb micropython/ast.py --- a/micropython/ast.py Sat May 30 20:30:28 2009 +0200 +++ b/micropython/ast.py Sun May 31 18:44:40 2009 +0200 @@ -783,6 +783,7 @@ # Skip the handler where the call was successful. + self.new_op(PopHandler()) self.new_op(Jump(end_handler_block)) # Enter the exception handler. @@ -803,9 +804,10 @@ self.new_op(RaiseException()) - # After the handler. + # After the handler, clear the exception. self.set_block(end_handler_block) + self.new_op(ClearException()) # Assign to the target. @@ -839,26 +841,31 @@ def visitIf(self, node): first = 1 + next_block = None exit_block = self.new_block() clauses = node.tests + [(None, node.else_)] - last_clause = clauses[-1] for clause in clauses: test, body = clause if body is None: break + if not first: - self.set_block(next_block) + self.new_op(Jump(exit_block)) # finish last body + self.set_block(next_block) # start next test + next_block = None + if test is not None: self.dispatch(test) next_block = self.new_block() self.new_op(JumpIfFalse(next_block)) + self.dispatch(body) - if clause is not last_clause: - self.new_op(Jump(exit_block)) + first = 0 - first = 0 + if next_block is not None: + self.set_block(next_block) self.set_block(exit_block) @@ -921,7 +928,6 @@ # Start of handlers. self.set_block(handler_block) - self.new_op(PopHandler()) for name, assignment, handler in node.handlers: @@ -964,7 +970,10 @@ self.dispatch(node.else_) + # Clear the exception. + self.set_block(exit_block) + self.new_op(ClearException()) self.drop_exception_blocks() def visitTryFinally(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "TryFinally") diff -r b8fea0931697 -r 2ede6db71ceb micropython/common.py --- a/micropython/common.py Sat May 30 20:30:28 2009 +0200 +++ b/micropython/common.py Sun May 31 18:44:40 2009 +0200 @@ -74,8 +74,8 @@ # Useful data. comparison_methods = { - "==" : ("__eq__", "__ne__"), - "!=" : ("__ne__", "__eq__"), + "==" : ("__eq__", "__eq__"), + "!=" : ("__ne__", "__ne__"), "<" : ("__lt__", "__gt__"), "<=" : ("__le__", "__ge__"), ">=" : ("__ge__", "__le__"), diff -r b8fea0931697 -r 2ede6db71ceb micropython/opt.py --- a/micropython/opt.py Sat May 30 20:30:28 2009 +0200 +++ b/micropython/opt.py Sun May 31 18:44:40 2009 +0200 @@ -31,6 +31,7 @@ # Code generation optimisations: "constant_storage", "constant_accessor", "known_target", "self_access", "temp_storage", "load_operations", "no_operations", "unused_results", + "unused_handlers", # Inspection optimisations: "unused_objects" ] @@ -142,6 +143,9 @@ def should_optimise_unused_results(self): return "unused_results" in self.optimisations + def should_optimise_unused_handlers(self): + return "unused_handlers" in self.optimisations + # Simple tests. def is_constant_input(self, instruction): @@ -286,6 +290,15 @@ return 0 + def have_empty_handler(self, instruction): + + """ + Return whether the active instruction defined a handler for exceptions + which is then discarded by the given 'instruction'. + """ + + return isinstance(self.translation.last_op(), PushHandler) and isinstance(instruction, PopHandler) + # Optimisation methods. See the supported_optimisations class attribute. def optimise_constant_storage(self): @@ -419,4 +432,14 @@ if self.should_optimise_unused_results() and self.have_simple_input(): self.remove_active_value() + def optimise_unused_handlers(self, instruction): + + "Discard handlers which will not be used." + + if self.should_optimise_unused_handlers() and self.have_empty_handler(instruction): + self.translation.remove_op() + return 1 + else: + return 0 + # vim: tabstop=4 expandtab shiftwidth=4 diff -r b8fea0931697 -r 2ede6db71ceb micropython/rsvp.py --- a/micropython/rsvp.py Sat May 30 20:30:28 2009 +0200 +++ b/micropython/rsvp.py Sun May 31 18:44:40 2009 +0200 @@ -229,6 +229,7 @@ class LoadException(Instruction): "Load the raised exception." class StoreException(Instruction): "Store the current object in the exception register." +class ClearException(Instruction): "Reset the exception register." class RaiseException(Instruction): "Raise an exception, jumping to the active handler." class PushHandler(Address): "Push an exception handler onto the handler stack." class PopHandler(Instruction): "Pop an exception handler from the handler stack." diff -r b8fea0931697 -r 2ede6db71ceb micropython/trans.py --- a/micropython/trans.py Sat May 30 20:30:28 2009 +0200 +++ b/micropython/trans.py Sun May 31 18:44:40 2009 +0200 @@ -254,18 +254,22 @@ def new_op(self, op): - "Add 'op' to the generated code." + """ + Add 'op' to the generated code, returning a true value if an instruction + was added. + """ # Optimise load operations employed by this instruction. self.optimiser.optimise_load_operations(op) - if self.optimiser.optimise_away_no_operations(op): - return + if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): + return 0 # Add the operation to the current block. self.blocks[-1].code.append(op) self.optimiser.set_new(op) + return 1 def remove_op(self): @@ -962,7 +966,8 @@ method = unary_methods[node.__class__.__name__] - type_error_block = self.new_block() + handler_block = self.new_block() + else_block = self.new_block() end_block = self.new_block() # Evaluate and store the operand in temporary storage. @@ -972,12 +977,18 @@ self.new_op(temp) + # Try to get the attribute, handling exceptions. + + self.new_op(PushHandler(handler_block)) + # Get the method on temp. self._generateAttr(node, method, self.attribute_load_instructions) temp_method = self.optimiser.optimise_temp_storage() - self._handleAttributeError(node, type_error_block) + # Finish handling any attribute access exceptions. + + have_handler = self.new_op(PopHandler()) # Add arguments. # NOTE: No support for defaults. @@ -994,17 +1005,24 @@ temp_out = self.get_temp() self.new_op(Jump(end_block)) + # End method attempt. + + self.set_block(handler_block) + + if have_handler: + self.new_op(PopHandler()) + self._handleAttributeError(node, temp_method, else_block) + # Raise a TypeError. - self.set_block(type_error_block) + self.set_block(else_block) self.make_exception("TypeError", node) self.new_op(StoreException()) self.new_op(RaiseException()) - self.set_block(end_block) - # Produce the result. + self.set_block(end_block) self.new_op(temp_out) # Compilation duties... @@ -1066,22 +1084,26 @@ """ right_block = self.new_block() - type_error_block = self.new_block() + left_else_block = self.new_block() + right_else_block = self.new_block() end_block = self.new_block() # Left method. - temp_out = self._generateOpMethod(node, temp1, temp2, left_method, right_block, end_block) + temp_out = self._generateOpMethod(node, temp1, temp2, left_method, left_else_block, right_block, end_block) self.discard_temp(temp_out) # NOTE: Will re-use the same storage. + self.set_block(left_else_block) + self.new_op(ClearException()) + # Right method. self.set_block(right_block) - temp_out = self._generateOpMethod(node, temp2, temp1, right_method, type_error_block, end_block) + temp_out = self._generateOpMethod(node, temp2, temp1, right_method, right_else_block, right_else_block, end_block) # Raise a TypeError. - self.set_block(type_error_block) + self.set_block(right_else_block) self.make_exception("TypeError", node) self.new_op(StoreException()) self.new_op(RaiseException()) @@ -1089,19 +1111,23 @@ self.set_block(end_block) return temp_out - def _generateOpMethod(self, node, temp1, temp2, method_name, next_method_block, end_block): + def _generateOpMethod(self, node, temp1, temp2, method_name, handled_block, next_method_block, end_block): """ For the given 'node', generate the operator method invocation using the operands 'temp1' and 'temp2', employing the given 'method_name', and - jumping appropriately to 'next_method_block' where a NotImplemented - result is returned, or to 'end_block' if the method call was successful. + jumping appropriately to 'handled_block' where an AttributeError was + handled, to 'next_method_block' where a NotImplemented result is + returned, or to 'end_block' if the method call was successful. A temporary storage reference is returned from this method. """ - end_attempt_block = self.new_block() + handler_block = self.new_block() + # Try to get the attribute, handling exceptions. + + self.new_op(PushHandler(handler_block)) self.new_op(temp1) # Get method on temp1. @@ -1109,7 +1135,9 @@ self._generateAttr(node, method_name, self.attribute_load_instructions) temp_method = self.optimiser.optimise_temp_storage() - self._handleAttributeError(node, end_attempt_block) + # Finish handling any attribute access exceptions. + + have_handler = self.new_op(PopHandler()) # Add arguments. # NOTE: No support for defaults. @@ -1136,20 +1164,26 @@ # End method attempt. - self.set_block(end_attempt_block) + self.set_block(handler_block) + + if have_handler: + self.new_op(PopHandler()) + self._handleAttributeError(node, temp_method, handled_block) + return temp_out - def _handleAttributeError(self, node, end_call_block): + def _handleAttributeError(self, node, temp_method, handled_block): """ Add exception handling to the method acquisition instructions where the attribute access cannot be resolved at compile-time. """ - if not self.optimiser.optimise_known_target(): + if not (self.optimiser.should_optimise_known_target() and self.optimiser.is_constant_input(temp_method)): self.load_builtin("AttributeError", node) self.new_op(CheckException()) - self.new_op(JumpIfTrue(end_call_block)) + self.new_op(JumpIfTrue(handled_block)) + self.new_op(RaiseException()) def _generateSequence(self, sequence_type, node): diff -r b8fea0931697 -r 2ede6db71ceb rsvp.py --- a/rsvp.py Sat May 30 20:30:28 2009 +0200 +++ b/rsvp.py Sun May 31 18:44:40 2009 +0200 @@ -53,6 +53,7 @@ """ from micropython.program import DataObject # for creating "nice" new objects +import operator class IllegalInstruction(Exception): pass @@ -650,6 +651,9 @@ def StoreException(self): self.exception = self.value[1] + def ClearException(self): + self.exception = None + def RaiseException(self): # NOTE: Adding the program counter as the first attribute. self.save(self.exception + 1, self.pc) @@ -734,8 +738,9 @@ right_context, right = self.frame_stack[frame + 1] # Test operand suitability. + # NOTE: Support other types. - if not self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class): + if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)): self.exception = self._MakeObject(2, self.type_error_instance) return self.RaiseException() @@ -814,6 +819,50 @@ self.result = addr, addr + def builtins_int_op(self, op, true_if_incompatible): + frame = self.local_sp_stack[-1] + + # Get operands addresses. + + left_context, left = self.frame_stack[frame] + right_context, right = self.frame_stack[frame + 1] + + # Test operand suitability. + # NOTE: Support other types. + # NOTE: Handle comparisons of incompatible types more appropriately. + + if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)): + if true_if_incompatible: + self.result = self.true_constant, self.true_constant + else: + self.result = self.false_constant, self.false_constant + return + + # NOTE: Assume single location for data. + + left_data = left + 1 + right_data = right + 1 + + # Test the data. + # NOTE: The data is considered ready to use. + + if op(self.load(left_data), self.load(right_data)): + self.result = self.true_constant, self.true_constant + else: + self.result = self.false_constant, self.false_constant + + def builtins_int_lt(self): + return self.builtins_int_op(operator.lt, 0) + + def builtins_int_gt(self): + return self.builtins_int_op(operator.gt, 0) + + def builtins_int_eq(self): + return self.builtins_int_op(operator.eq, 0) + + def builtins_int_ne(self): + return self.builtins_int_op(operator.ne, 1) + def builtins_bool_bool(self): frame = self.local_sp_stack[-1] @@ -867,6 +916,10 @@ "__builtins__.int.__add__" : builtins_int_add, "__builtins__.int.__bool__" : builtins_int_bool, "__builtins__.int.__neg__" : builtins_int_neg, + "__builtins__.int.__lt__" : builtins_int_lt, + "__builtins__.int.__gt__" : builtins_int_gt, + "__builtins__.int.__eq__" : builtins_int_eq, + "__builtins__.int.__ne__" : builtins_int_ne, "__builtins__.bool.__bool__" : builtins_bool_bool, "__builtins__.list" : builtins_list_new, "__builtins__.list.__getitem__" : builtins_list_getitem, diff -r b8fea0931697 -r 2ede6db71ceb tests/compare.py --- a/tests/compare.py Sat May 30 20:30:28 2009 +0200 +++ b/tests/compare.py Sun May 31 18:44:40 2009 +0200 @@ -4,6 +4,12 @@ b = 2 c = 3 -a < b < c +result_1 = 0 +result_2 = 0 + +if a < b < c: + result_1 = 1 +if c > b > a: + result_2 = 2 # vim: tabstop=4 expandtab shiftwidth=4 diff -r b8fea0931697 -r 2ede6db71ceb tests/compare2.py --- a/tests/compare2.py Sat May 30 20:30:28 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#!/usr/bin/env python - -a = 1 -b = 2 -c = 3 - -class X: - def __contains__(self, other): - return 1 - -x = X() - -a == x != b -a is x is not b -a in x - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r b8fea0931697 -r 2ede6db71ceb tests/compare_equality.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/compare_equality.py Sun May 31 18:44:40 2009 +0200 @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +a = 1 +b = 2 + +class X: + def __contains__(self, other): + return 1 + +x = X() + +result_1 = 0 +result_2 = 2 + +if a != x != b: + result_1 = 1 + +if a == x != b: + result_2 = 0 + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r b8fea0931697 -r 2ede6db71ceb tests/compare_equality_simple.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/compare_equality_simple.py Sun May 31 18:44:40 2009 +0200 @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +a = 1 +b = 2 +c = 3 + +result_1 = 0 +result_2 = 2 + +if a != b != c: + result_1 = 1 + +if a == b != c: + result_2 = 0 + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r b8fea0931697 -r 2ede6db71ceb tests/compare_identity.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/compare_identity.py Sun May 31 18:44:40 2009 +0200 @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +a = 1 +b = 2 + +class X: + def __contains__(self, other): + return 1 + +x = X() + +result_3 = 0 +result_4 = 4 + +if a is not x is not b: + result_3 = 3 + +if a is not x is b: + result_4 = 0 + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r b8fea0931697 -r 2ede6db71ceb tests/compare_membership.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/compare_membership.py Sun May 31 18:44:40 2009 +0200 @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +a = 1 + +class X: + def __contains__(self, other): + return 1 + +x = X() + +result_5 = 0 +result_6 = 6 + +if a in x: + result_5 = 5 + +if a not in x: + result_6 = 0 + +# vim: tabstop=4 expandtab shiftwidth=4