1.1 --- a/docs/exceptions.txt Sat May 30 20:30:28 2009 +0200
1.2 +++ b/docs/exceptions.txt Sun May 31 18:44:40 2009 +0200
1.3 @@ -1,5 +1,16 @@
1.4 Exception Handling
1.5 -------------------
1.6 +==================
1.7 +
1.8 +Active Exceptions
1.9 +=================
1.10 +
1.11 +When an exception is raised, an exception instance is defined as the active
1.12 +exception. This active exception remains in force until either a new exception
1.13 +is raised in any handling of the exception, or upon exit of a handler where
1.14 +the active exception has been caught (thus resetting the active exception).
1.15 +
1.16 +Handlers
1.17 +========
1.18
1.19 An exception handler stack is defined such that when a try...except or
1.20 try...finally block is entered, a new handler is defined.
1.21 @@ -13,3 +24,12 @@
1.22 If no handler is defined when an exception is raised or re-raised, the program
1.23 should terminate. This might be done by having a "handler #0" which explicitly
1.24 terminates the program.
1.25 +
1.26 +Instructions
1.27 +------------
1.28 +
1.29 +PushHandler(block) defines an active handler at the location indicated by the
1.30 +given block.
1.31 +
1.32 +PopHandler removes the active handler at or after the location indicated by
1.33 +the previously given block.
2.1 --- a/micropython/ast.py Sat May 30 20:30:28 2009 +0200
2.2 +++ b/micropython/ast.py Sun May 31 18:44:40 2009 +0200
2.3 @@ -783,6 +783,7 @@
2.4
2.5 # Skip the handler where the call was successful.
2.6
2.7 + self.new_op(PopHandler())
2.8 self.new_op(Jump(end_handler_block))
2.9
2.10 # Enter the exception handler.
2.11 @@ -803,9 +804,10 @@
2.12
2.13 self.new_op(RaiseException())
2.14
2.15 - # After the handler.
2.16 + # After the handler, clear the exception.
2.17
2.18 self.set_block(end_handler_block)
2.19 + self.new_op(ClearException())
2.20
2.21 # Assign to the target.
2.22
2.23 @@ -839,26 +841,31 @@
2.24
2.25 def visitIf(self, node):
2.26 first = 1
2.27 + next_block = None
2.28 exit_block = self.new_block()
2.29
2.30 clauses = node.tests + [(None, node.else_)]
2.31 - last_clause = clauses[-1]
2.32
2.33 for clause in clauses:
2.34 test, body = clause
2.35 if body is None:
2.36 break
2.37 +
2.38 if not first:
2.39 - self.set_block(next_block)
2.40 + self.new_op(Jump(exit_block)) # finish last body
2.41 + self.set_block(next_block) # start next test
2.42 + next_block = None
2.43 +
2.44 if test is not None:
2.45 self.dispatch(test)
2.46 next_block = self.new_block()
2.47 self.new_op(JumpIfFalse(next_block))
2.48 +
2.49 self.dispatch(body)
2.50 - if clause is not last_clause:
2.51 - self.new_op(Jump(exit_block))
2.52 + first = 0
2.53
2.54 - first = 0
2.55 + if next_block is not None:
2.56 + self.set_block(next_block)
2.57
2.58 self.set_block(exit_block)
2.59
2.60 @@ -921,7 +928,6 @@
2.61 # Start of handlers.
2.62
2.63 self.set_block(handler_block)
2.64 -
2.65 self.new_op(PopHandler())
2.66
2.67 for name, assignment, handler in node.handlers:
2.68 @@ -964,7 +970,10 @@
2.69
2.70 self.dispatch(node.else_)
2.71
2.72 + # Clear the exception.
2.73 +
2.74 self.set_block(exit_block)
2.75 + self.new_op(ClearException())
2.76 self.drop_exception_blocks()
2.77
2.78 def visitTryFinally(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "TryFinally")
3.1 --- a/micropython/common.py Sat May 30 20:30:28 2009 +0200
3.2 +++ b/micropython/common.py Sun May 31 18:44:40 2009 +0200
3.3 @@ -74,8 +74,8 @@
3.4 # Useful data.
3.5
3.6 comparison_methods = {
3.7 - "==" : ("__eq__", "__ne__"),
3.8 - "!=" : ("__ne__", "__eq__"),
3.9 + "==" : ("__eq__", "__eq__"),
3.10 + "!=" : ("__ne__", "__ne__"),
3.11 "<" : ("__lt__", "__gt__"),
3.12 "<=" : ("__le__", "__ge__"),
3.13 ">=" : ("__ge__", "__le__"),
4.1 --- a/micropython/opt.py Sat May 30 20:30:28 2009 +0200
4.2 +++ b/micropython/opt.py Sun May 31 18:44:40 2009 +0200
4.3 @@ -31,6 +31,7 @@
4.4 # Code generation optimisations:
4.5 "constant_storage", "constant_accessor", "known_target", "self_access",
4.6 "temp_storage", "load_operations", "no_operations", "unused_results",
4.7 + "unused_handlers",
4.8 # Inspection optimisations:
4.9 "unused_objects"
4.10 ]
4.11 @@ -142,6 +143,9 @@
4.12 def should_optimise_unused_results(self):
4.13 return "unused_results" in self.optimisations
4.14
4.15 + def should_optimise_unused_handlers(self):
4.16 + return "unused_handlers" in self.optimisations
4.17 +
4.18 # Simple tests.
4.19
4.20 def is_constant_input(self, instruction):
4.21 @@ -286,6 +290,15 @@
4.22
4.23 return 0
4.24
4.25 + def have_empty_handler(self, instruction):
4.26 +
4.27 + """
4.28 + Return whether the active instruction defined a handler for exceptions
4.29 + which is then discarded by the given 'instruction'.
4.30 + """
4.31 +
4.32 + return isinstance(self.translation.last_op(), PushHandler) and isinstance(instruction, PopHandler)
4.33 +
4.34 # Optimisation methods. See the supported_optimisations class attribute.
4.35
4.36 def optimise_constant_storage(self):
4.37 @@ -419,4 +432,14 @@
4.38 if self.should_optimise_unused_results() and self.have_simple_input():
4.39 self.remove_active_value()
4.40
4.41 + def optimise_unused_handlers(self, instruction):
4.42 +
4.43 + "Discard handlers which will not be used."
4.44 +
4.45 + if self.should_optimise_unused_handlers() and self.have_empty_handler(instruction):
4.46 + self.translation.remove_op()
4.47 + return 1
4.48 + else:
4.49 + return 0
4.50 +
4.51 # vim: tabstop=4 expandtab shiftwidth=4
5.1 --- a/micropython/rsvp.py Sat May 30 20:30:28 2009 +0200
5.2 +++ b/micropython/rsvp.py Sun May 31 18:44:40 2009 +0200
5.3 @@ -229,6 +229,7 @@
5.4
5.5 class LoadException(Instruction): "Load the raised exception."
5.6 class StoreException(Instruction): "Store the current object in the exception register."
5.7 +class ClearException(Instruction): "Reset the exception register."
5.8 class RaiseException(Instruction): "Raise an exception, jumping to the active handler."
5.9 class PushHandler(Address): "Push an exception handler onto the handler stack."
5.10 class PopHandler(Instruction): "Pop an exception handler from the handler stack."
6.1 --- a/micropython/trans.py Sat May 30 20:30:28 2009 +0200
6.2 +++ b/micropython/trans.py Sun May 31 18:44:40 2009 +0200
6.3 @@ -254,18 +254,22 @@
6.4
6.5 def new_op(self, op):
6.6
6.7 - "Add 'op' to the generated code."
6.8 + """
6.9 + Add 'op' to the generated code, returning a true value if an instruction
6.10 + was added.
6.11 + """
6.12
6.13 # Optimise load operations employed by this instruction.
6.14
6.15 self.optimiser.optimise_load_operations(op)
6.16 - if self.optimiser.optimise_away_no_operations(op):
6.17 - return
6.18 + if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op):
6.19 + return 0
6.20
6.21 # Add the operation to the current block.
6.22
6.23 self.blocks[-1].code.append(op)
6.24 self.optimiser.set_new(op)
6.25 + return 1
6.26
6.27 def remove_op(self):
6.28
6.29 @@ -962,7 +966,8 @@
6.30
6.31 method = unary_methods[node.__class__.__name__]
6.32
6.33 - type_error_block = self.new_block()
6.34 + handler_block = self.new_block()
6.35 + else_block = self.new_block()
6.36 end_block = self.new_block()
6.37
6.38 # Evaluate and store the operand in temporary storage.
6.39 @@ -972,12 +977,18 @@
6.40
6.41 self.new_op(temp)
6.42
6.43 + # Try to get the attribute, handling exceptions.
6.44 +
6.45 + self.new_op(PushHandler(handler_block))
6.46 +
6.47 # Get the method on temp.
6.48
6.49 self._generateAttr(node, method, self.attribute_load_instructions)
6.50 temp_method = self.optimiser.optimise_temp_storage()
6.51
6.52 - self._handleAttributeError(node, type_error_block)
6.53 + # Finish handling any attribute access exceptions.
6.54 +
6.55 + have_handler = self.new_op(PopHandler())
6.56
6.57 # Add arguments.
6.58 # NOTE: No support for defaults.
6.59 @@ -994,17 +1005,24 @@
6.60 temp_out = self.get_temp()
6.61 self.new_op(Jump(end_block))
6.62
6.63 + # End method attempt.
6.64 +
6.65 + self.set_block(handler_block)
6.66 +
6.67 + if have_handler:
6.68 + self.new_op(PopHandler())
6.69 + self._handleAttributeError(node, temp_method, else_block)
6.70 +
6.71 # Raise a TypeError.
6.72
6.73 - self.set_block(type_error_block)
6.74 + self.set_block(else_block)
6.75 self.make_exception("TypeError", node)
6.76 self.new_op(StoreException())
6.77 self.new_op(RaiseException())
6.78
6.79 - self.set_block(end_block)
6.80 -
6.81 # Produce the result.
6.82
6.83 + self.set_block(end_block)
6.84 self.new_op(temp_out)
6.85
6.86 # Compilation duties...
6.87 @@ -1066,22 +1084,26 @@
6.88 """
6.89
6.90 right_block = self.new_block()
6.91 - type_error_block = self.new_block()
6.92 + left_else_block = self.new_block()
6.93 + right_else_block = self.new_block()
6.94 end_block = self.new_block()
6.95
6.96 # Left method.
6.97
6.98 - temp_out = self._generateOpMethod(node, temp1, temp2, left_method, right_block, end_block)
6.99 + temp_out = self._generateOpMethod(node, temp1, temp2, left_method, left_else_block, right_block, end_block)
6.100 self.discard_temp(temp_out) # NOTE: Will re-use the same storage.
6.101
6.102 + self.set_block(left_else_block)
6.103 + self.new_op(ClearException())
6.104 +
6.105 # Right method.
6.106
6.107 self.set_block(right_block)
6.108 - temp_out = self._generateOpMethod(node, temp2, temp1, right_method, type_error_block, end_block)
6.109 + temp_out = self._generateOpMethod(node, temp2, temp1, right_method, right_else_block, right_else_block, end_block)
6.110
6.111 # Raise a TypeError.
6.112
6.113 - self.set_block(type_error_block)
6.114 + self.set_block(right_else_block)
6.115 self.make_exception("TypeError", node)
6.116 self.new_op(StoreException())
6.117 self.new_op(RaiseException())
6.118 @@ -1089,19 +1111,23 @@
6.119 self.set_block(end_block)
6.120 return temp_out
6.121
6.122 - def _generateOpMethod(self, node, temp1, temp2, method_name, next_method_block, end_block):
6.123 + def _generateOpMethod(self, node, temp1, temp2, method_name, handled_block, next_method_block, end_block):
6.124
6.125 """
6.126 For the given 'node', generate the operator method invocation using the
6.127 operands 'temp1' and 'temp2', employing the given 'method_name', and
6.128 - jumping appropriately to 'next_method_block' where a NotImplemented
6.129 - result is returned, or to 'end_block' if the method call was successful.
6.130 + jumping appropriately to 'handled_block' where an AttributeError was
6.131 + handled, to 'next_method_block' where a NotImplemented result is
6.132 + returned, or to 'end_block' if the method call was successful.
6.133
6.134 A temporary storage reference is returned from this method.
6.135 """
6.136
6.137 - end_attempt_block = self.new_block()
6.138 + handler_block = self.new_block()
6.139
6.140 + # Try to get the attribute, handling exceptions.
6.141 +
6.142 + self.new_op(PushHandler(handler_block))
6.143 self.new_op(temp1)
6.144
6.145 # Get method on temp1.
6.146 @@ -1109,7 +1135,9 @@
6.147 self._generateAttr(node, method_name, self.attribute_load_instructions)
6.148 temp_method = self.optimiser.optimise_temp_storage()
6.149
6.150 - self._handleAttributeError(node, end_attempt_block)
6.151 + # Finish handling any attribute access exceptions.
6.152 +
6.153 + have_handler = self.new_op(PopHandler())
6.154
6.155 # Add arguments.
6.156 # NOTE: No support for defaults.
6.157 @@ -1136,20 +1164,26 @@
6.158
6.159 # End method attempt.
6.160
6.161 - self.set_block(end_attempt_block)
6.162 + self.set_block(handler_block)
6.163 +
6.164 + if have_handler:
6.165 + self.new_op(PopHandler())
6.166 + self._handleAttributeError(node, temp_method, handled_block)
6.167 +
6.168 return temp_out
6.169
6.170 - def _handleAttributeError(self, node, end_call_block):
6.171 + def _handleAttributeError(self, node, temp_method, handled_block):
6.172
6.173 """
6.174 Add exception handling to the method acquisition instructions where the
6.175 attribute access cannot be resolved at compile-time.
6.176 """
6.177
6.178 - if not self.optimiser.optimise_known_target():
6.179 + if not (self.optimiser.should_optimise_known_target() and self.optimiser.is_constant_input(temp_method)):
6.180 self.load_builtin("AttributeError", node)
6.181 self.new_op(CheckException())
6.182 - self.new_op(JumpIfTrue(end_call_block))
6.183 + self.new_op(JumpIfTrue(handled_block))
6.184 + self.new_op(RaiseException())
6.185
6.186 def _generateSequence(self, sequence_type, node):
6.187
7.1 --- a/rsvp.py Sat May 30 20:30:28 2009 +0200
7.2 +++ b/rsvp.py Sun May 31 18:44:40 2009 +0200
7.3 @@ -53,6 +53,7 @@
7.4 """
7.5
7.6 from micropython.program import DataObject # for creating "nice" new objects
7.7 +import operator
7.8
7.9 class IllegalInstruction(Exception):
7.10 pass
7.11 @@ -650,6 +651,9 @@
7.12 def StoreException(self):
7.13 self.exception = self.value[1]
7.14
7.15 + def ClearException(self):
7.16 + self.exception = None
7.17 +
7.18 def RaiseException(self):
7.19 # NOTE: Adding the program counter as the first attribute.
7.20 self.save(self.exception + 1, self.pc)
7.21 @@ -734,8 +738,9 @@
7.22 right_context, right = self.frame_stack[frame + 1]
7.23
7.24 # Test operand suitability.
7.25 + # NOTE: Support other types.
7.26
7.27 - if not self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class):
7.28 + if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)):
7.29 self.exception = self._MakeObject(2, self.type_error_instance)
7.30 return self.RaiseException()
7.31
7.32 @@ -814,6 +819,50 @@
7.33
7.34 self.result = addr, addr
7.35
7.36 + def builtins_int_op(self, op, true_if_incompatible):
7.37 + frame = self.local_sp_stack[-1]
7.38 +
7.39 + # Get operands addresses.
7.40 +
7.41 + left_context, left = self.frame_stack[frame]
7.42 + right_context, right = self.frame_stack[frame + 1]
7.43 +
7.44 + # Test operand suitability.
7.45 + # NOTE: Support other types.
7.46 + # NOTE: Handle comparisons of incompatible types more appropriately.
7.47 +
7.48 + if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)):
7.49 + if true_if_incompatible:
7.50 + self.result = self.true_constant, self.true_constant
7.51 + else:
7.52 + self.result = self.false_constant, self.false_constant
7.53 + return
7.54 +
7.55 + # NOTE: Assume single location for data.
7.56 +
7.57 + left_data = left + 1
7.58 + right_data = right + 1
7.59 +
7.60 + # Test the data.
7.61 + # NOTE: The data is considered ready to use.
7.62 +
7.63 + if op(self.load(left_data), self.load(right_data)):
7.64 + self.result = self.true_constant, self.true_constant
7.65 + else:
7.66 + self.result = self.false_constant, self.false_constant
7.67 +
7.68 + def builtins_int_lt(self):
7.69 + return self.builtins_int_op(operator.lt, 0)
7.70 +
7.71 + def builtins_int_gt(self):
7.72 + return self.builtins_int_op(operator.gt, 0)
7.73 +
7.74 + def builtins_int_eq(self):
7.75 + return self.builtins_int_op(operator.eq, 0)
7.76 +
7.77 + def builtins_int_ne(self):
7.78 + return self.builtins_int_op(operator.ne, 1)
7.79 +
7.80 def builtins_bool_bool(self):
7.81 frame = self.local_sp_stack[-1]
7.82
7.83 @@ -867,6 +916,10 @@
7.84 "__builtins__.int.__add__" : builtins_int_add,
7.85 "__builtins__.int.__bool__" : builtins_int_bool,
7.86 "__builtins__.int.__neg__" : builtins_int_neg,
7.87 + "__builtins__.int.__lt__" : builtins_int_lt,
7.88 + "__builtins__.int.__gt__" : builtins_int_gt,
7.89 + "__builtins__.int.__eq__" : builtins_int_eq,
7.90 + "__builtins__.int.__ne__" : builtins_int_ne,
7.91 "__builtins__.bool.__bool__" : builtins_bool_bool,
7.92 "__builtins__.list" : builtins_list_new,
7.93 "__builtins__.list.__getitem__" : builtins_list_getitem,
8.1 --- a/tests/compare.py Sat May 30 20:30:28 2009 +0200
8.2 +++ b/tests/compare.py Sun May 31 18:44:40 2009 +0200
8.3 @@ -4,6 +4,12 @@
8.4 b = 2
8.5 c = 3
8.6
8.7 -a < b < c
8.8 +result_1 = 0
8.9 +result_2 = 0
8.10 +
8.11 +if a < b < c:
8.12 + result_1 = 1
8.13 +if c > b > a:
8.14 + result_2 = 2
8.15
8.16 # vim: tabstop=4 expandtab shiftwidth=4
9.1 --- a/tests/compare2.py Sat May 30 20:30:28 2009 +0200
9.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
9.3 @@ -1,17 +0,0 @@
9.4 -#!/usr/bin/env python
9.5 -
9.6 -a = 1
9.7 -b = 2
9.8 -c = 3
9.9 -
9.10 -class X:
9.11 - def __contains__(self, other):
9.12 - return 1
9.13 -
9.14 -x = X()
9.15 -
9.16 -a == x != b
9.17 -a is x is not b
9.18 -a in x
9.19 -
9.20 -# vim: tabstop=4 expandtab shiftwidth=4