1.1 --- a/micropython/ast.py Thu May 17 23:37:05 2012 +0200
1.2 +++ b/micropython/ast.py Fri May 18 01:21:20 2012 +0200
1.3 @@ -3,7 +3,7 @@
1.4 """
1.5 Translate the AST of a Python program into a more interpretable representation.
1.6
1.7 -Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -382,13 +382,84 @@
1.13 self._visitAttr(node, self.attribute_load_instructions)
1.14
1.15 def visitList(self, node):
1.16 + self._generateList(node, node.nodes)
1.17 +
1.18 + def visitListComp(self, node):
1.19 self._generateList(node)
1.20 + temp_list = self.optimiser.optimise_temp_storage()
1.21 +
1.22 + # Get the list's append method.
1.23 +
1.24 + self._generateAttr(node, "append", self.attribute_load_instructions)
1.25 + temp_method = self.optimiser.optimise_temp_storage()
1.26 +
1.27 + self.visitListCompFor(node.quals[0], node.quals[1:], node.expr, temp_list, temp_method)
1.28 +
1.29 + # Generate a reference to the list.
1.30 +
1.31 + self.new_op(temp_list)
1.32
1.33 - def visitListComp(self, node): raise TranslationNotImplementedError("ListComp")
1.34 + self.discard_temp(temp_method)
1.35 + self.discard_temp(temp_list)
1.36 +
1.37 + def visitListCompFor(self, node, following_quals, expr, temp_list, temp_method):
1.38 + temp_iterator, next_block, exit_block, else_block = self._startFor(node)
1.39 +
1.40 + # Explicit dispatch to tests, following loops, expression.
1.41 +
1.42 + if node.ifs:
1.43 + self.visitListCompIf(node.ifs[0], node.ifs[1:], following_quals, expr, temp_list, temp_method)
1.44 +
1.45 + # Explicit dispatch to following loops, expression.
1.46 +
1.47 + elif following_quals:
1.48 + self.visitListCompFor(following_quals[0], following_quals[1:], expr, temp_list, temp_method)
1.49 +
1.50 + # Explicit dispatch to the expression.
1.51 +
1.52 + else:
1.53 + self.dispatch(expr)
1.54
1.55 - def visitListCompFor(self, node): raise TranslationNotImplementedError("ListCompFor")
1.56 + # Append the value to the list.
1.57 +
1.58 + temp_value = self.optimiser.optimise_temp_storage()
1.59 + self._generateInvocation(temp_method, (temp_list, temp_value,))
1.60 +
1.61 + self._endFor(node, temp_iterator, next_block, exit_block, else_block)
1.62 +
1.63 + def visitListCompIf(self, node, following_tests, following_quals, expr, temp_list, temp_method):
1.64 + exit_block = self.new_block()
1.65 +
1.66 + # Evaluate the test and jump beyond the body if it is not satisfied.
1.67 +
1.68 + self.dispatch(node.test)
1.69 +
1.70 + temp = self.optimiser.optimise_temp_storage()
1.71 + self.new_op(temp)
1.72 + self._generateTestBoolean(node, temp)
1.73 + self.new_op(JumpIfFalse(exit_block, working="status"))
1.74
1.75 - def visitListCompIf(self, node): raise TranslationNotImplementedError("ListCompIf")
1.76 + # Explicit dispatch to tests, following loops, expression.
1.77 +
1.78 + if following_tests:
1.79 + self.visitListCompIf(following_tests[0], following_tests[1:], following_quals, expr, temp_list, temp_method)
1.80 +
1.81 + # Explicit dispatch to following loops, expression.
1.82 +
1.83 + elif following_quals:
1.84 + self.visitListCompFor(following_quals[0], following_quals[1:], expr, temp_list, temp_method)
1.85 +
1.86 + # Explicit dispatch to the expression.
1.87 +
1.88 + else:
1.89 + self.dispatch(expr)
1.90 +
1.91 + # Append the value to the list.
1.92 +
1.93 + temp_value = self.optimiser.optimise_temp_storage()
1.94 + self._generateInvocation(temp_method, (temp_list, temp_value,))
1.95 +
1.96 + self.set_block(exit_block)
1.97
1.98 def visitName(self, node):
1.99 self._visitName(node, self.name_load_instructions)
1.100 @@ -638,114 +709,9 @@
1.101 self.optimiser.optimise_unused_results()
1.102
1.103 def visitFor(self, node):
1.104 - next_handler_block = self.new_block()
1.105 - end_handler_block = self.new_block()
1.106 - exit_block = self.new_block()
1.107 - next_block = self.new_block()
1.108 - else_block = self.new_block()
1.109 -
1.110 - # Get the "list" to be iterated over, obtain its iterator.
1.111 -
1.112 - self._startCallFunc()
1.113 - self.dispatch(node.list)
1.114 - self._generateAttr(node, "__iter__", self.attribute_load_instructions)
1.115 - temp_target, target, temp_context = self._generateCallFunc([], node)
1.116 - self._doCallFunc(temp_target, target)
1.117 - self._endCallFunc(temp_target, temp_context)
1.118 -
1.119 - # Use a long-lasting temporary storage slot, since any result from the
1.120 - # __iter__ method will not remain around for long.
1.121 -
1.122 - temp_iterator = self.get_temp()
1.123 -
1.124 - # In the loop...
1.125 -
1.126 - self.set_block(next_block)
1.127 -
1.128 - # Handle exceptions when calling "next"...
1.129 -
1.130 - self.add_exception_blocks(next_handler_block, end_handler_block)
1.131 - self.new_op(PushHandler(next_handler_block))
1.132 -
1.133 - # Use the iterator to get the next value.
1.134 -
1.135 - self._startCallFunc()
1.136 - self.new_op(temp_iterator)
1.137 - self._generateAttr(node, "next", self.attribute_load_instructions)
1.138 - temp_target, target, temp_context = self._generateCallFunc([], node)
1.139 - self._doCallFunc(temp_target, target)
1.140 - self._endCallFunc(temp_target, temp_context)
1.141 -
1.142 - # Record the value to be assigned.
1.143 -
1.144 - self.record_value()
1.145 -
1.146 - # Skip the handler where the call was successful.
1.147 -
1.148 - self.new_op(PopHandler(1))
1.149 - self.new_op(Jump(end_handler_block))
1.150 -
1.151 - # Enter the exception handler.
1.152 -
1.153 - self.set_block(next_handler_block)
1.154 - self.new_op(PopHandler(1))
1.155 -
1.156 - # Disable the handlers.
1.157 -
1.158 - self.drop_exception_blocks()
1.159 -
1.160 - # Test for StopIteration.
1.161 -
1.162 - self.load_builtin("StopIteration", node)
1.163 - self.new_op(CheckException(target="status"))
1.164 - if node.else_ is not None:
1.165 - self.new_op(JumpIfTrue(else_block, working="status"))
1.166 - else:
1.167 - self.new_op(JumpIfTrue(exit_block, working="status"))
1.168 -
1.169 - # Re-raise the exception otherwise.
1.170 -
1.171 - self.new_op(RaiseException())
1.172 -
1.173 - # After the handler, clear the exception.
1.174 -
1.175 - self.set_block(end_handler_block)
1.176 -
1.177 - # Assign to the target.
1.178 -
1.179 - self.dispatch(node.assign)
1.180 - self.discard_value()
1.181 -
1.182 - # Process the body with the current next and exit points.
1.183 -
1.184 - self.add_loop_blocks(next_block, exit_block)
1.185 + temp_iterator, next_block, exit_block, else_block = self._startFor(node, node.else_)
1.186 self.dispatch(node.body)
1.187 - self.drop_loop_blocks()
1.188 -
1.189 - # Repeat the loop.
1.190 -
1.191 - self.new_op(Jump(next_block))
1.192 -
1.193 - # Produce the "else" section.
1.194 -
1.195 - if node.else_ is not None:
1.196 - self.set_block(else_block)
1.197 - self.new_op(ClearException(target="exception"))
1.198 - self.dispatch(node.else_)
1.199 -
1.200 - # After the loop...
1.201 -
1.202 - self.set_block(exit_block)
1.203 -
1.204 - else:
1.205 - # After the loop...
1.206 -
1.207 - self.set_block(exit_block)
1.208 - self.new_op(ClearException(target="exception"))
1.209 -
1.210 - # Compilation duties...
1.211 -
1.212 - self.discard_temp(temp_iterator)
1.213 + self._endFor(node, temp_iterator, next_block, exit_block, else_block, node.else_)
1.214
1.215 def visitIf(self, node):
1.216 first = 1
2.1 --- a/micropython/inspect.py Thu May 17 23:37:05 2012 +0200
2.2 +++ b/micropython/inspect.py Fri May 18 01:21:20 2012 +0200
2.3 @@ -3,7 +3,7 @@
2.4 """
2.5 Inspect source files, obtaining details of classes and attributes.
2.6
2.7 -Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk>
2.8 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
2.9
2.10 This program is free software; you can redistribute it and/or modify it under
2.11 the terms of the GNU General Public License as published by the Free Software
2.12 @@ -1063,12 +1063,14 @@
2.13 return self.OP(node)
2.14
2.15 def visitListComp(self, node):
2.16 - for qual in node.quals:
2.17 - self.dispatch(qual)
2.18 - self.dispatch(node.expr)
2.19 +
2.20 + # Note that explicit dispatch is performed.
2.21 +
2.22 + if node.quals:
2.23 + self.visitListCompFor(node.quals[0], node.quals[1:], node.expr)
2.24 return make_instance()
2.25
2.26 - def visitListCompFor(self, node):
2.27 + def visitListCompFor(self, node, following_quals, expr):
2.28 self.new_branchpoint()
2.29
2.30 # Declare names which will be used by generated code.
2.31 @@ -1091,15 +1093,45 @@
2.32
2.33 self.new_branch(node)
2.34
2.35 - for if_ in node.ifs:
2.36 - self.dispatch(if_)
2.37 + # Note that explicit dispatch is performed.
2.38 +
2.39 + if node.ifs:
2.40 + self.visitListCompIf(node.ifs[0], node.ifs[1:], following_quals, expr)
2.41 + elif following_quals:
2.42 + self.visitListCompFor(following_quals[0], following_quals[1:], expr)
2.43 + else:
2.44 + self.dispatch(expr)
2.45
2.46 self.shelve_branch()
2.47 self.in_loop = in_loop
2.48
2.49 self.merge_branches()
2.50
2.51 - visitListCompIf = TEST_NOP
2.52 + def visitListCompIf(self, node, following_ifs, following_quals, expr):
2.53 + self.use_name("__bool__", node)
2.54 + self.new_branchpoint()
2.55 +
2.56 + # Propagate attribute usage to branches.
2.57 +
2.58 + self.dispatch(node.test)
2.59 +
2.60 + # Note that explicit dispatch is performed.
2.61 +
2.62 + if following_ifs:
2.63 + self.visitListCompIf(following_ifs[0], following_ifs[1:], following_quals, expr)
2.64 + elif following_quals:
2.65 + self.visitListCompFor(following_quals[0], following_quals[1:], expr)
2.66 + else:
2.67 + self.new_branch(expr)
2.68 + self.dispatch(expr)
2.69 + self.shelve_branch()
2.70 +
2.71 + # Maintain a branch for the else clause.
2.72 +
2.73 + self.new_branch(NullBranch())
2.74 + self.shelve_branch()
2.75 +
2.76 + self.merge_branches()
2.77
2.78 visitMod = _visitBinary
2.79
3.1 --- a/micropython/trans.py Thu May 17 23:37:05 2012 +0200
3.2 +++ b/micropython/trans.py Fri May 18 01:21:20 2012 +0200
3.3 @@ -3,7 +3,7 @@
3.4 """
3.5 Translate the AST of a Python program into a more interpretable representation.
3.6
3.7 -Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk>
3.8 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
3.9
3.10 This program is free software; you can redistribute it and/or modify it under
3.11 the terms of the GNU General Public License as published by the Free Software
3.12 @@ -1139,20 +1139,22 @@
3.13
3.14 # Store using 0-based index values.
3.15
3.16 - self._populateSequence(temp, node)
3.17 + self._populateSequence(temp, node.nodes)
3.18
3.19 self.new_op(temp)
3.20 self.discard_temp(temp)
3.21
3.22 - def _generateList(self, node):
3.23 + def _generateList(self, node, nodes=None):
3.24
3.25 - "Make a list using the given program 'node'."
3.26 + "Make a list using the given program 'node' and optional 'nodes'."
3.27 +
3.28 + nodes = nodes or []
3.29
3.30 # Make a fragment containing the list elements.
3.31
3.32 - self.new_op(MakeFragment(len(node.nodes) + 1))
3.33 + self.new_op(MakeFragment(len(nodes) + 1))
3.34 temp = self.get_temp()
3.35 - self._populateSequence(temp, node)
3.36 + self._populateSequence(temp, nodes)
3.37 self.new_op(temp)
3.38 self.record_value()
3.39
3.40 @@ -1171,13 +1173,13 @@
3.41 self.discard_temp(temp)
3.42 self.discard_temp(list_temp)
3.43
3.44 - def _populateSequence(self, temp, node, offset=0):
3.45 + def _populateSequence(self, temp, nodes, offset=0):
3.46
3.47 """
3.48 - Populate a sequence using the given 'temp' reference and program 'node'.
3.49 + Populate a sequence using the given 'temp' reference and 'nodes'.
3.50 """
3.51
3.52 - for i, n in enumerate(node.nodes):
3.53 + for i, n in enumerate(nodes):
3.54 self.dispatch(n)
3.55 self.record_value()
3.56 self._storeInSequence(temp, i, offset)
3.57 @@ -1236,6 +1238,144 @@
3.58
3.59 self.set_block(end_block, [false_block, true_block])
3.60
3.61 + # Common AST operations.
3.62 +
3.63 + def _startFor(self, node, else_=None):
3.64 +
3.65 + """
3.66 + Generate the start of a loop using the given 'node' and 'else_' clause,
3.67 + if defined. The iterator and next, exit and else blocks are returned.
3.68 + """
3.69 +
3.70 + next_handler_block = self.new_block()
3.71 + end_handler_block = self.new_block()
3.72 + exit_block = self.new_block()
3.73 + next_block = self.new_block()
3.74 + else_block = self.new_block()
3.75 +
3.76 + temp_iterator = self._visitForList(node)
3.77 +
3.78 + # In the loop...
3.79 +
3.80 + self.set_block(next_block)
3.81 +
3.82 + # Handle exceptions when calling "next"...
3.83 +
3.84 + self.add_exception_blocks(next_handler_block, end_handler_block)
3.85 + self.new_op(PushHandler(next_handler_block))
3.86 +
3.87 + # Invoke the next method.
3.88 +
3.89 + self._visitForNext(node, temp_iterator)
3.90 +
3.91 + # Record the value to be assigned.
3.92 +
3.93 + self.record_value()
3.94 +
3.95 + # Skip the handler where the call was successful.
3.96 +
3.97 + self.new_op(PopHandler(1))
3.98 + self.new_op(Jump(end_handler_block))
3.99 +
3.100 + # Enter the exception handler.
3.101 +
3.102 + self.set_block(next_handler_block)
3.103 + self.new_op(PopHandler(1))
3.104 +
3.105 + # Disable the handlers.
3.106 +
3.107 + self.drop_exception_blocks()
3.108 +
3.109 + # Test for StopIteration.
3.110 +
3.111 + self.load_builtin("StopIteration", node)
3.112 + self.new_op(CheckException(target="status"))
3.113 + if else_ is not None:
3.114 + self.new_op(JumpIfTrue(else_block, working="status"))
3.115 + else:
3.116 + self.new_op(JumpIfTrue(exit_block, working="status"))
3.117 +
3.118 + # Re-raise the exception otherwise.
3.119 +
3.120 + self.new_op(RaiseException())
3.121 +
3.122 + # After the handler, clear the exception.
3.123 +
3.124 + self.set_block(end_handler_block)
3.125 +
3.126 + # Assign to the target.
3.127 +
3.128 + self.dispatch(node.assign)
3.129 + self.discard_value()
3.130 +
3.131 + # Process the body with the current next and exit points.
3.132 +
3.133 + self.add_loop_blocks(next_block, exit_block)
3.134 +
3.135 + return temp_iterator, next_block, exit_block, else_block
3.136 +
3.137 + def _endFor(self, node, temp_iterator, next_block, exit_block, else_block=None, else_=None):
3.138 +
3.139 + """
3.140 + Generate the end of a loop for the given 'node' using the given
3.141 + 'temp_iterator' and 'next_block', 'exit_block' and 'else_block'
3.142 + definitions, together with an 'else_' clause, if defined.
3.143 + """
3.144 +
3.145 + self.drop_loop_blocks()
3.146 +
3.147 + # Repeat the loop.
3.148 +
3.149 + self.new_op(Jump(next_block))
3.150 +
3.151 + # Produce the "else" section.
3.152 +
3.153 + if else_ is not None:
3.154 + self.set_block(else_block)
3.155 + self.new_op(ClearException(target="exception"))
3.156 + self.dispatch(else_)
3.157 +
3.158 + # After the loop...
3.159 +
3.160 + self.set_block(exit_block)
3.161 +
3.162 + else:
3.163 + # After the loop...
3.164 +
3.165 + self.set_block(exit_block)
3.166 + self.new_op(ClearException(target="exception"))
3.167 +
3.168 + # Compilation duties...
3.169 +
3.170 + self.discard_temp(temp_iterator)
3.171 +
3.172 + def _visitForList(self, node):
3.173 +
3.174 + "Get the list to be iterated over, returning its iterator."
3.175 +
3.176 + self._startCallFunc()
3.177 + self.dispatch(node.list)
3.178 + self._generateAttr(node, "__iter__", self.attribute_load_instructions)
3.179 + temp_target, target, temp_context = self._generateCallFunc([], node)
3.180 + self._doCallFunc(temp_target, target)
3.181 + self._endCallFunc(temp_target, temp_context)
3.182 +
3.183 + # Use a long-lasting temporary storage slot, since any result from the
3.184 + # __iter__ method will not remain around for long.
3.185 +
3.186 + return self.get_temp()
3.187 +
3.188 + def _visitForNext(self, node, temp_iterator):
3.189 +
3.190 + "Use the iterator to get the next value."
3.191 +
3.192 + self._startCallFunc()
3.193 + self.new_op(temp_iterator)
3.194 + self._generateAttr(node, "next", self.attribute_load_instructions)
3.195 + temp_target, target, temp_context = self._generateCallFunc([], node)
3.196 + self._doCallFunc(temp_target, target)
3.197 + self._endCallFunc(temp_target, temp_context)
3.198 +
3.199 def _visitPrint(self, node, function_name):
3.200 self._startCallFunc()
3.201 self.load_builtin(function_name, node)
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/tests/listcomp.py Fri May 18 01:21:20 2012 +0200
4.3 @@ -0,0 +1,17 @@
4.4 +#!/usr/bin/env python
4.5 +
4.6 +class C:
4.7 + def __init__(self, x):
4.8 + self.x = x
4.9 +
4.10 +a = [C(1), C(2), C(3)]
4.11 +b = [x.x for x in a]
4.12 +c = [x.x for x in a if x.x > 1]
4.13 +
4.14 +result1_1 = b[0]
4.15 +result1_2 = b[1]
4.16 +result1_3 = b[2]
4.17 +result2_2 = c[0]
4.18 +result2_3 = c[1]
4.19 +
4.20 +# vim: tabstop=4 expandtab shiftwidth=4