1.1 --- a/docs/annotations.txt Fri Sep 17 00:48:32 2010 +0200
1.2 +++ b/docs/annotations.txt Tue Sep 21 00:11:34 2010 +0200
1.3 @@ -1,9 +1,30 @@
1.4 AST Node Annotations
1.5 ====================
1.6
1.7 +Attribute Users
1.8 +---------------
1.9 +
1.10 _attrnames defines a dictionary mapping local names to sets of attribute
1.11 - names found to be used with those names
1.12 + names found to be used with those names in a branch
1.13 +_attrcombined defines a dictionary mapping local names to sets of attribute
1.14 + names found to be used with those names for the entire lifetime
1.15 + of a particular attribute user
1.16 +_attrtypes defines types deduced from the combined attribute usage details
1.17 +_attrdefs defines definition-related users which consume usage details
1.18 + from the node
1.19 +
1.20 +Attribute Accessors
1.21 +-------------------
1.22 +
1.23 _attrusers defines a dictionary mapping local names to sets of nodes
1.24 defining those names
1.25 +
1.26 +Name Accessors
1.27 +--------------
1.28 +
1.29 _scope set as "constant", "local", "global" or "builtins"
1.30 +
1.31 +Program Units
1.32 +-------------
1.33 +
1.34 unit refers to a micropython Class, Function or Module instance
2.1 --- a/micropython/__init__.py Fri Sep 17 00:48:32 2010 +0200
2.2 +++ b/micropython/__init__.py Tue Sep 21 00:11:34 2010 +0200
2.3 @@ -277,8 +277,8 @@
2.4 full_name = obj.full_name()
2.5 name = obj.name
2.6
2.7 - if module.has_key(name) and module[name].defines_ambiguous_class():
2.8 - raise TableGenerationError, "Class %r in module %r is ambiguously defined." % (name, module.full_name())
2.9 + #if module.has_key(name) and module[name].defines_ambiguous_class():
2.10 + # raise TableGenerationError, "Class %r in module %r is ambiguously defined." % (name, module.full_name())
2.11
2.12 # Define a table entry for the class.
2.13
3.1 --- a/micropython/common.py Fri Sep 17 00:48:32 2010 +0200
3.2 +++ b/micropython/common.py Tue Sep 21 00:11:34 2010 +0200
3.3 @@ -31,6 +31,12 @@
3.4 "Merge the given 'dicts' mapping keys to sets of objects."
3.5
3.6 new_dict = {}
3.7 + update_set_dict(new_dict, dicts)
3.8 + return new_dict
3.9 +
3.10 +def update_set_dict(new_dict, dicts):
3.11 +
3.12 + "Update 'new_dict' with the contents of the set dictionary 'dicts'."
3.13
3.14 for old_dict in dicts:
3.15 for key, values in old_dict.items():
3.16 @@ -39,7 +45,46 @@
3.17 else:
3.18 new_dict[key].update(values)
3.19
3.20 - return new_dict
3.21 +def deepen_set_dict(d):
3.22 +
3.23 + """
3.24 + Return a new dictionary mapping keys to sets of tuples of values, using data
3.25 + from 'd' which is a dictionary mapping keys to sets of values.
3.26 + """
3.27 +
3.28 + d2 = {}
3.29 + for key, values in d.items():
3.30 + d2[key] = set([tuple(values)])
3.31 + return d2
3.32 +
3.33 +def combine_set_dicts(d1, d2):
3.34 +
3.35 + """
3.36 + Combine dictionaries 'd1' and 'd2' in a resulting dictionary, with the set
3.37 + values of the contributing dictionaries being merged such that a "product"
3.38 + of the values for a given key are stored in the combined dictionary.
3.39 + """
3.40 +
3.41 + combined = {}
3.42 + d2_keys = d2.keys()
3.43 +
3.44 + for key in d2_keys:
3.45 +
3.46 + d1_values = d1.get(key)
3.47 +
3.48 + if d1_values is None:
3.49 + combined[key] = d2[key]
3.50 + else:
3.51 + combined[key] = set()
3.52 + for d2_value in d2[key]:
3.53 + for d1_value in d1_values:
3.54 + combined[key].add(tuple(set(d1_value).union(d2_value)))
3.55 +
3.56 + for key in d1.keys():
3.57 + if key not in d2_keys:
3.58 + combined[key] = d1[key]
3.59 +
3.60 + return combined
3.61
3.62 # Visitors and activities related to node annotations.
3.63
4.1 --- a/micropython/data.py Fri Sep 17 00:48:32 2010 +0200
4.2 +++ b/micropython/data.py Tue Sep 21 00:11:34 2010 +0200
4.3 @@ -53,7 +53,8 @@
4.4 """
4.5
4.6 from micropython.program import DataObject, DataValue, ReplaceableContext, PlaceholderContext
4.7 -from micropython.common import AtLeast, InspectError, merge_set_dicts
4.8 +from micropython.common import AtLeast, InspectError, \
4.9 + merge_set_dicts, update_set_dict, deepen_set_dict, combine_set_dicts
4.10
4.11 def shortrepr(obj):
4.12 if obj is None:
4.13 @@ -105,7 +106,8 @@
4.14
4.15 # Suspended user details plus loop details.
4.16
4.17 - self.suspended_users = [] # stack of lists of users
4.18 + self.suspended_broken_users = [] # stack of lists of user dicts
4.19 + self.suspended_continuing_users = [] # stack of lists of user dicts
4.20
4.21 # Scope usage, indicating the origin of names.
4.22
4.23 @@ -414,7 +416,9 @@
4.24
4.25 if not hasattr(node, "_attrcombined"):
4.26
4.27 - usage = {}
4.28 + node._attrcontributors = set()
4.29 + node._attrcombined = {}
4.30 +
4.31 contributor_usage = {}
4.32 all_contributors = set()
4.33
4.34 @@ -427,11 +431,7 @@
4.35 # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}.
4.36
4.37 contributors, contributed_usage = self.get_usage_from_contributors(contributor)
4.38 -
4.39 - for name, all_usage in contributed_usage.items():
4.40 - if not contributor_usage.has_key(name):
4.41 - contributor_usage[name] = set()
4.42 - contributor_usage[name].update(all_usage)
4.43 + update_set_dict(contributor_usage, [contributed_usage])
4.44
4.45 # Record all contributors.
4.46
4.47 @@ -439,35 +439,11 @@
4.48 all_contributors.update(contributors)
4.49
4.50 # Then get the resulting usage.
4.51 -
4.52 - contributed_names = contributor_usage.keys()
4.53 - current_usage = node._attrnames
4.54 -
4.55 - for name in contributed_names:
4.56 -
4.57 - # Where contributors define names not present at this level, just
4.58 - # use the contributed usage.
4.59 -
4.60 - current_usage_for_name = current_usage.get(name)
4.61 -
4.62 - if current_usage_for_name is None:
4.63 - usage[name] = contributor_usage[name]
4.64 -
4.65 - # Otherwise, combine the current usage with the contributed usage.
4.66 - # Thus, usage of (f, g) combined with {(a, b), (c, d)} would give
4.67 - # {(f, g, a, b), (f, g, c, d)}.
4.68 -
4.69 - else:
4.70 - usage[name] = set()
4.71 - for attrnames in contributor_usage[name]:
4.72 - usage[name].add(tuple(current_usage_for_name.union(attrnames)))
4.73 -
4.74 - # Maintain usage not affected by contributors.
4.75 - # Usage of (f, g) would become {(f, g)}.
4.76 -
4.77 - for name in current_usage.keys():
4.78 - if name not in contributed_names:
4.79 - usage[name] = set([tuple(current_usage[name])])
4.80 + # Combine the current usage with the contributed usage.
4.81 + # Thus, usage of {(f, g)} combined with {(a, b), (c, d)} would give
4.82 + # {(f, g, a, b), (f, g, c, d)}.
4.83 +
4.84 + usage = combine_set_dicts(deepen_set_dict(node._attrnames), contributor_usage)
4.85
4.86 node._attrcontributors = all_contributors
4.87 node._attrcombined = usage
4.88 @@ -584,7 +560,7 @@
4.89
4.90 # Branch management methods.
4.91
4.92 - def _new_branchpoint(self, is_loop=0):
4.93 + def _new_branchpoint(self, loop_node=None):
4.94
4.95 """
4.96 Establish a new branchpoint where several control-flow branches diverge
4.97 @@ -594,8 +570,9 @@
4.98 self.attribute_user_shelves.append([])
4.99 self.scope_shelves.append([])
4.100
4.101 - if is_loop:
4.102 - self.suspended_users.append([])
4.103 + if loop_node is not None:
4.104 + self.suspended_broken_users.append([])
4.105 + self.suspended_continuing_users.append((loop_node, []))
4.106
4.107 def _new_branch(self, node):
4.108
4.109 @@ -623,6 +600,21 @@
4.110
4.111 # Add this user as a contributor to the previously active users.
4.112
4.113 + self._connect_users_to_branch(attribute_users, node)
4.114 +
4.115 + # Retain a record of scope usage.
4.116 +
4.117 + scope_usage = {}
4.118 + scope_usage.update(self.scope_usage[-1])
4.119 + self.scope_usage.append(scope_usage)
4.120 +
4.121 + def _connect_users_to_branch(self, attribute_users, node):
4.122 +
4.123 + """
4.124 + Given the 'attribute_users' mapping, connect the users referenced in the
4.125 + mapping to the given branch 'node'.
4.126 + """
4.127 +
4.128 all_users = set()
4.129
4.130 for users in attribute_users.values():
4.131 @@ -632,12 +624,6 @@
4.132 self._init_attribute_user(user)
4.133 user._attrbranches.append(node)
4.134
4.135 - # Retain a record of scope usage.
4.136 -
4.137 - scope_usage = {}
4.138 - scope_usage.update(self.scope_usage[-1])
4.139 - self.scope_usage.append(scope_usage)
4.140 -
4.141 def _abandon_branch(self):
4.142
4.143 """
4.144 @@ -648,15 +634,31 @@
4.145 self.attribute_users[-1] = AbandonedBranchUsers()
4.146 self.scope_usage[-1] = AbandonedBranchScope()
4.147
4.148 - def _suspend_branch(self):
4.149 + def _suspend_broken_branch(self):
4.150
4.151 """
4.152 Suspend a branch for resumption after the current loop.
4.153 """
4.154
4.155 - users = self.suspended_users[-1]
4.156 - users.append(self.attribute_users[-1])
4.157 - self._abandon_branch()
4.158 + attribute_users = self.attribute_users[-1]
4.159 +
4.160 + if not isinstance(attribute_users, AbandonedBranchUsers):
4.161 + users = self.suspended_broken_users[-1]
4.162 + users.append(attribute_users)
4.163 + self._abandon_branch()
4.164 +
4.165 + def _suspend_continuing_branch(self):
4.166 +
4.167 + """
4.168 + Suspend a branch for resumption after the current iteration.
4.169 + """
4.170 +
4.171 + attribute_users = self.attribute_users[-1]
4.172 +
4.173 + if not isinstance(attribute_users, AbandonedBranchUsers):
4.174 + loop_node, users = self.suspended_continuing_users[-1]
4.175 + users.append(attribute_users)
4.176 + self._abandon_branch()
4.177
4.178 def _shelve_branch(self):
4.179
4.180 @@ -731,16 +733,36 @@
4.181
4.182 self.scope_usage[-1] = new_scope_usage
4.183
4.184 - def _resume_branches(self):
4.185 + def _resume_broken_branches(self):
4.186 +
4.187 + """
4.188 + Incorporate users from suspended broken branches into the current set of
4.189 + active users.
4.190 + """
4.191 +
4.192 + suspended_users = self.suspended_broken_users.pop()
4.193 + current_users = self.attribute_users[-1]
4.194 + new_users = merge_set_dicts(suspended_users + [current_users])
4.195 + self.attribute_users[-1] = new_users
4.196 +
4.197 + def _resume_continuing_branches(self):
4.198
4.199 """
4.200 - Incorporate users from suspended branches into the current set of active
4.201 - users.
4.202 + Incorporate users from suspended continuing branches into the current
4.203 + set of active users, merging usage from the latter with the former.
4.204 """
4.205
4.206 - users = self.suspended_users.pop()
4.207 + loop_node, suspended_users = self.suspended_continuing_users.pop()
4.208 current_users = self.attribute_users[-1]
4.209 - new_users = merge_set_dicts(users + [current_users])
4.210 +
4.211 + # Connect the suspended users to the loop node.
4.212 +
4.213 + for users in suspended_users:
4.214 + self._connect_users_to_branch(users, loop_node)
4.215 +
4.216 + # Merge suspended branches with the current branch.
4.217 +
4.218 + new_users = merge_set_dicts(suspended_users + [current_users])
4.219 self.attribute_users[-1] = new_users
4.220
4.221 # Scope usage methods.
5.1 --- a/micropython/inspect.py Fri Sep 17 00:48:32 2010 +0200
5.2 +++ b/micropython/inspect.py Tue Sep 21 00:11:34 2010 +0200
5.3 @@ -347,8 +347,8 @@
5.4 # These are convenience methods which refer to the specific namespace's
5.5 # implementation of these operations.
5.6
5.7 - def new_branchpoint(self, is_loop=0):
5.8 - self.get_namespace()._new_branchpoint(is_loop)
5.9 + def new_branchpoint(self, loop_node=None):
5.10 + self.get_namespace()._new_branchpoint(loop_node)
5.11
5.12 def new_branch(self, node):
5.13 self.get_namespace()._new_branch(node)
5.14 @@ -356,8 +356,11 @@
5.15 def abandon_branch(self):
5.16 self.get_namespace()._abandon_branch()
5.17
5.18 - def suspend_branch(self):
5.19 - self.get_namespace()._suspend_branch()
5.20 + def suspend_broken_branch(self):
5.21 + self.get_namespace()._suspend_broken_branch()
5.22 +
5.23 + def suspend_continuing_branch(self):
5.24 + self.get_namespace()._suspend_continuing_branch()
5.25
5.26 def shelve_branch(self):
5.27 self.get_namespace()._shelve_branch()
5.28 @@ -365,8 +368,11 @@
5.29 def merge_branches(self):
5.30 self.get_namespace()._merge_branches()
5.31
5.32 - def resume_branches(self):
5.33 - self.get_namespace()._resume_branches()
5.34 + def resume_broken_branches(self):
5.35 + self.get_namespace()._resume_broken_branches()
5.36 +
5.37 + def resume_continuing_branches(self):
5.38 + self.get_namespace()._resume_continuing_branches()
5.39
5.40 def define_attribute_user(self, node):
5.41
5.42 @@ -407,10 +413,6 @@
5.43 self.NOP(node)
5.44 self.abandon_branch()
5.45
5.46 - def NOP_SUSPEND(self, node):
5.47 - self.NOP(node)
5.48 - self.suspend_branch()
5.49 -
5.50 def OP(self, node):
5.51 for n in node.getChildNodes():
5.52 self.dispatch(n)
5.53 @@ -664,7 +666,9 @@
5.54
5.55 visitBitxor = _visitBinary
5.56
5.57 - visitBreak = NOP_SUSPEND
5.58 + def visitBreak(self, node):
5.59 + self.NOP(node)
5.60 + self.suspend_broken_branch()
5.61
5.62 visitCallFunc = OP
5.63
5.64 @@ -773,7 +777,9 @@
5.65 self.use_name(self.importer.get_constant_type_name(node.value), node)
5.66 return self.importer.make_constant(node.value)
5.67
5.68 - visitContinue = NOP_ABANDON
5.69 + def visitContinue(self, node):
5.70 + self.NOP(node)
5.71 + self.suspend_continuing_branch()
5.72
5.73 visitDecorators = NOP
5.74
5.75 @@ -792,7 +798,7 @@
5.76 visitFloorDiv = _visitBinary
5.77
5.78 def visitFor(self, node):
5.79 - self.new_branchpoint(is_loop=1)
5.80 + self.new_branchpoint(node)
5.81
5.82 # Declare names which will be used by generated code.
5.83
5.84 @@ -811,10 +817,7 @@
5.85 self.new_branch(node)
5.86 self.dispatch(node.body)
5.87
5.88 - # Incorporate the else clause in the branch, if present.
5.89 -
5.90 - if node.else_ is not None:
5.91 - self.dispatch(node.else_)
5.92 + self.resume_continuing_branches()
5.93
5.94 self.shelve_branch()
5.95
5.96 @@ -834,7 +837,7 @@
5.97
5.98 # Any suspended branches from the loop can now be resumed.
5.99
5.100 - self.resume_branches()
5.101 + self.resume_broken_branches()
5.102
5.103 return None
5.104
5.105 @@ -1062,7 +1065,7 @@
5.106 visitUnarySub = _visitUnary
5.107
5.108 def visitWhile(self, node):
5.109 - self.new_branchpoint(is_loop=1)
5.110 + self.new_branchpoint(node)
5.111
5.112 # Propagate attribute usage to branches.
5.113
5.114 @@ -1075,6 +1078,9 @@
5.115
5.116 self.new_branch(node)
5.117 self.dispatch(node.body)
5.118 +
5.119 + self.resume_continuing_branches()
5.120 +
5.121 self.dispatch(node.test)
5.122 self.shelve_branch()
5.123
5.124 @@ -1094,7 +1100,7 @@
5.125
5.126 # Any suspended branches from the loop can now be resumed.
5.127
5.128 - self.resume_branches()
5.129 + self.resume_broken_branches()
5.130
5.131 return None
5.132
6.1 --- a/tests/attribute_access_type_restriction_loop_break.py Fri Sep 17 00:48:32 2010 +0200
6.2 +++ b/tests/attribute_access_type_restriction_loop_break.py Tue Sep 21 00:11:34 2010 +0200
6.3 @@ -12,7 +12,7 @@
6.4 return 6
6.5
6.6 def f(self):
6.7 - return 0 # stops the test loop
6.8 + return 0
6.9
6.10 def g(self):
6.11 return 3
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/tests/attribute_access_type_restriction_loop_continue.py Tue Sep 21 00:11:34 2010 +0200
7.3 @@ -0,0 +1,43 @@
7.4 +#!/usr/bin/env python
7.5 +
7.6 +class C:
7.7 + def e(self):
7.8 + return 0 # stops the test loop
7.9 +
7.10 + def f(self):
7.11 + return 7
7.12 +
7.13 +class D:
7.14 + def e(self):
7.15 + return 6
7.16 +
7.17 + def f(self):
7.18 + return 1
7.19 +
7.20 + def g(self):
7.21 + return 3
7.22 +
7.23 +class E:
7.24 + def f(self):
7.25 + return 4
7.26 +
7.27 + def g(self):
7.28 + return 5
7.29 +
7.30 +def test_loop(obj, obj2):
7.31 + obj.e()
7.32 + obj.g()
7.33 + while obj.f():
7.34 + if obj.e():
7.35 + obj = obj2
7.36 + continue
7.37 + if not obj.e():
7.38 + break
7.39 + return obj.f()
7.40 +
7.41 +c = C()
7.42 +d = D()
7.43 +e = E()
7.44 +result1_7 = test_loop(d, c)
7.45 +
7.46 +# vim: tabstop=4 expandtab shiftwidth=4