1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/micropython/branch.py Tue Nov 06 00:16:26 2012 +0100
1.3 @@ -0,0 +1,762 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
1.8 +
1.9 +This program is free software; you can redistribute it and/or modify it under
1.10 +the terms of the GNU General Public License as published by the Free Software
1.11 +Foundation; either version 3 of the License, or (at your option) any later
1.12 +version.
1.13 +
1.14 +This program is distributed in the hope that it will be useful, but WITHOUT
1.15 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.16 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.17 +details.
1.18 +
1.19 +You should have received a copy of the GNU General Public License along with
1.20 +this program. If not, see <http://www.gnu.org/licenses/>.
1.21 +"""
1.22 +
1.23 +from micropython.errors import *
1.24 +from micropython.objectset import *
1.25 +from micropython.types import *
1.26 +
1.27 +try:
1.28 + set
1.29 +except NameError:
1.30 + from sets import Set as set
1.31 +
1.32 +class AttributeUsage:
1.33 +
1.34 + "Management of attribute usage in a program unit."
1.35 +
1.36 + def __init__(self):
1.37 +
1.38 + # Attributes accessed on objects, potentially narrowing their types.
1.39 + # Specific namespaces should define known names during initialisation.
1.40 + # For example, functions can define names belonging to parameters.
1.41 +
1.42 + # Attribute users, defining names which use attributes.
1.43 +
1.44 + self.attribute_users = [{}] # stack of assignments and branches
1.45 +
1.46 + # Define attribute usage to identify active program sections.
1.47 + # Attribute users are AST nodes defining names.
1.48 +
1.49 + self.all_attribute_users = set()
1.50 +
1.51 + # Finalisation of a program unit's information.
1.52 +
1.53 + def finalise_attribute_usage(self):
1.54 +
1.55 + "Propagate attribute usage for the namespace to the importer."
1.56 +
1.57 + module = self.module
1.58 + importer = module and module.importer
1.59 +
1.60 + if importer is not None:
1.61 +
1.62 + # Visit each user and examine the attribute usage for each name.
1.63 +
1.64 + for user in self.all_attribute_users:
1.65 +
1.66 + # First, visit the contributors and combine their attribute
1.67 + # usage with the usage recorded directly on the user.
1.68 +
1.69 + self.get_usage_from_contributors(user)
1.70 +
1.71 + # Record the defining user on each contributor.
1.72 +
1.73 + for contributor in user._attrcontributors:
1.74 + contributor._attrdefs.append(user)
1.75 +
1.76 + # Then, tell the importer about the usage.
1.77 +
1.78 + for name in user._attrnames.keys():
1.79 +
1.80 + # Only provide information about names defined by this user.
1.81 +
1.82 + usage = user._attrcombined.get(name, [])
1.83 +
1.84 + # Skip reporting where no actual usage occurs.
1.85 +
1.86 + if usage is None:
1.87 + continue
1.88 +
1.89 + # Eliminate non-usage.
1.90 +
1.91 + importer.use_names(user, name, tuple([attrnames for attrnames in usage if attrnames]), self.full_name())
1.92 +
1.93 + def finalise_users(self, objtable):
1.94 +
1.95 + "Record the object types for generating guards."
1.96 +
1.97 + # Visit each user and examine the attribute usage for each name.
1.98 +
1.99 + for user in self.all_attribute_users:
1.100 + user._attrtypes = self._deduce_types(user._attrcombined, objtable)
1.101 + self._finalise_contributor(user, objtable)
1.102 +
1.103 + def _finalise_contributors(self, node, objtable):
1.104 +
1.105 + """
1.106 + Visit the contributing branches of 'node', finalising them using the
1.107 + given 'objtable'.
1.108 + """
1.109 +
1.110 + for contributor in node._attrbranches:
1.111 + self._finalise_contributor(contributor, objtable)
1.112 +
1.113 + def _finalise_contributor(self, node, objtable):
1.114 +
1.115 + """
1.116 + Record the specific object types being used in various regions of a
1.117 + program unit.
1.118 + """
1.119 +
1.120 + if node._attrspecifictypes is None:
1.121 + merged = {}
1.122 +
1.123 + # Get the combined usage information from the user definitions.
1.124 +
1.125 + for user in node._attrdefs or [node]:
1.126 +
1.127 + # Filter the usage for each name using the local usage
1.128 + # information.
1.129 +
1.130 + for name, usage in user._attrcombined.items():
1.131 + localusage = node._attrnames.get(name)
1.132 +
1.133 + if usage and localusage:
1.134 + if not merged.has_key(name):
1.135 + merged[name] = ObjectSet()
1.136 +
1.137 + for attrnames, value in usage.items():
1.138 + if attrnames and localusage.issubset(attrnames):
1.139 + merged[name][attrnames] = value
1.140 +
1.141 + node._attrmerged = merged
1.142 + node._attrspecifictypes = self._deduce_types(node._attrmerged, objtable)
1.143 +
1.144 + self._finalise_contributors(node, objtable)
1.145 +
1.146 + def _deduce_types(self, usage, objtable):
1.147 +
1.148 + """
1.149 + Deduce the types for names from the given attribute 'usage' and using
1.150 + the given 'objtable'.
1.151 + """
1.152 +
1.153 + attrtypes = {}
1.154 + for name, combined_usage in usage.items():
1.155 + if combined_usage is not None:
1.156 + objtypes = get_object_types_for_usage(combined_usage, objtable, name, self.full_name(), True, self.module.importer)
1.157 + if objtypes:
1.158 + if self.is_method() and name == "self":
1.159 + objtypes = filter_using_self(objtypes, self.parent)
1.160 + attrtypes[name] = objtypes
1.161 + return attrtypes
1.162 +
1.163 + def get_usage_from_contributors(self, node):
1.164 +
1.165 + """
1.166 + Obtain usage information from the given 'node', combined with usage
1.167 + details from its contributors, returning a tuple containing a set of all
1.168 + contributors employed along with a dictionary mapping names to lists of
1.169 + usage possibilities (each a collection of attribute names).
1.170 + """
1.171 +
1.172 + unfinished = {}
1.173 +
1.174 + # Process any unprocessed contributors, indicating the unfinished state
1.175 + # of the associated data.
1.176 +
1.177 + if node._attrcombined is None:
1.178 + node._attrcombined = Unset
1.179 +
1.180 + for contributor in node._attrbranches:
1.181 +
1.182 + # Get contributor details.
1.183 +
1.184 + unfinished_contributors = self.get_usage_from_contributors(contributor)
1.185 +
1.186 + # Collect unfinished contributors and affected nodes.
1.187 +
1.188 + # Where the contributor is already set to Unset, a loop has
1.189 + # occurred and this node will need to have its usage
1.190 + # recalculated later for the unfinished contributor.
1.191 +
1.192 + if contributor._attrcombined is Unset:
1.193 + if not unfinished.has_key(contributor):
1.194 + unfinished[contributor] = []
1.195 + unfinished[contributor].append(node)
1.196 + continue
1.197 +
1.198 + # Where the contributor provides usage details, it may also
1.199 + # communicate unfinished contributor information. As a
1.200 + # consequence, this node is also affected.
1.201 +
1.202 + for unfinished_contributor, nodes in unfinished_contributors.items():
1.203 + if not unfinished.has_key(unfinished_contributor):
1.204 + unfinished[unfinished_contributor] = nodes
1.205 + else:
1.206 + unfinished[unfinished_contributor] += nodes
1.207 +
1.208 + if node not in unfinished[unfinished_contributor]:
1.209 + unfinished[unfinished_contributor].append(node)
1.210 +
1.211 + # Set the current state of the usage on this node.
1.212 +
1.213 + node._attrcombined, node._attrcontributors = \
1.214 + self.get_usage_from_contributors_for_node(node)
1.215 +
1.216 + # Complete unfinished contributors relying on this node.
1.217 +
1.218 + if unfinished.has_key(node):
1.219 + processed = set()
1.220 + for contributor in unfinished[node]:
1.221 + if not contributor in processed:
1.222 + processed.add(contributor)
1.223 +
1.224 + contributor._attrcombined, contributor._attrcontributors = \
1.225 + self.get_usage_from_contributors_for_node(contributor)
1.226 +
1.227 + del unfinished[node]
1.228 +
1.229 + return unfinished
1.230 +
1.231 + def get_usage_from_contributors_for_node(self, node):
1.232 +
1.233 + # Visit each contributor, gathering usage for each name.
1.234 +
1.235 + contributor_usage = {}
1.236 + all_contributions = []
1.237 + all_contributors = set()
1.238 +
1.239 + for contributor in node._attrbranches:
1.240 + usage = contributor._attrcombined
1.241 + if usage is not Unset:
1.242 + all_contributions.append(usage)
1.243 +
1.244 + all_contributors.add(contributor)
1.245 + contributors = contributor._attrcontributors
1.246 + if contributors is not None:
1.247 + all_contributors.update(contributors)
1.248 +
1.249 + # Get contributed usage for each contributor.
1.250 + # This gathers usage for each name such as {(a, b), (c, d)} and
1.251 + # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}.
1.252 +
1.253 + update_mapping_dict(contributor_usage, all_contributions)
1.254 +
1.255 + # Then get the resulting usage.
1.256 + # First, make the current usage compatible with the contributed
1.257 + # usage: this makes the attribute usage for each name merely one
1.258 + # member in a list of many possibilities.
1.259 + # Then, combine the current usage with the contributed usage.
1.260 + # Thus, usage of {(f, g)} combined with {(a, b), (c, d)} would give
1.261 + # {(f, g, a, b), (f, g, c, d)}.
1.262 +
1.263 + return combine_mapping_dicts(deepen_mapping_dict(node._attrnames), contributor_usage), all_contributors
1.264 +
1.265 + # Attribute usage methods.
1.266 +
1.267 + def use_attribute(self, name, attrname, value=None):
1.268 +
1.269 + """
1.270 + Note usage on the attribute user 'name' of the attribute 'attrname',
1.271 + noting an assignment if 'value' is specified.
1.272 + """
1.273 +
1.274 + return self._use_attribute(name, attrname, value)
1.275 +
1.276 + def use_specific_attribute(self, objname, attrname):
1.277 +
1.278 + "Declare the usage on 'objname' of the given 'attrname'."
1.279 +
1.280 + self._use_specific_attribute(objname, attrname)
1.281 +
1.282 + # These shadow various methods in the InspectedModule class, and provide
1.283 + # implementations generally.
1.284 +
1.285 + def _use_specific_attribute(self, objname, attrname, from_name=None):
1.286 +
1.287 + """
1.288 + Note attribute usage specifically on 'objname' - an object which is
1.289 + known at inspection time - or in the current unit if 'objname' is None,
1.290 + nominating a specific attribute 'attrname'.
1.291 +
1.292 + This bypasses attribute user mechanisms.
1.293 + """
1.294 +
1.295 + from_name = from_name or self.full_name()
1.296 + objname = objname or from_name
1.297 + module = self.module
1.298 + importer = module and module.importer
1.299 +
1.300 + if importer is not None:
1.301 + importer.use_specific_name(objname, attrname, from_name)
1.302 +
1.303 + def _use_attribute(self, name, attrname, value=None):
1.304 +
1.305 + """
1.306 + Indicate the use of the given 'name' in this namespace of an attribute
1.307 + with the given 'attrname'. If the optional 'value' is specified, an
1.308 + assignment using the given 'value' is recorded.
1.309 + """
1.310 +
1.311 + users = self.attribute_users[-1]
1.312 +
1.313 + # If no users are defined for the name, it cannot be handled.
1.314 +
1.315 + if not users.has_key(name):
1.316 + return []
1.317 +
1.318 + # Add the usage to all current users.
1.319 +
1.320 + for user in users[name]:
1.321 + values = user._attrnames[name]
1.322 + if values is None:
1.323 + values = user._attrnames[name] = ObjectSet()
1.324 +
1.325 + # Add an entry for the attribute, optionally with an assigned
1.326 + # value.
1.327 +
1.328 + values.add(attrname)
1.329 + if value is not None:
1.330 + values[attrname].add(value)
1.331 +
1.332 + return users[name]
1.333 +
1.334 + def _define_attribute_user(self, node):
1.335 +
1.336 + """
1.337 + Define 'node' as the user of attributes, indicating the point where the
1.338 + user is defined.
1.339 + """
1.340 +
1.341 + name = node.name
1.342 + self._define_attribute_user_for_name(node, name)
1.343 +
1.344 + def _define_attribute_user_for_name(self, node, name):
1.345 +
1.346 + "Define 'node' as the user of attributes for the given 'name'."
1.347 +
1.348 + users = self.attribute_users[-1]
1.349 +
1.350 + # This node overrides previous definitions.
1.351 +
1.352 + users[name] = set([node])
1.353 +
1.354 + # Record the attribute combinations for the name.
1.355 +
1.356 + self._init_attribute_user_for_name(node, name)
1.357 +
1.358 + # Remember this user.
1.359 +
1.360 + self.all_attribute_users.add(node)
1.361 +
1.362 + def _init_attribute_user_for_name(self, node, name):
1.363 +
1.364 + "Make sure that 'node' is initialised for 'name'."
1.365 +
1.366 + self._init_attribute_user(node)
1.367 + node._attrnames[name] = None
1.368 +
1.369 + def _init_attribute_user(self, node):
1.370 +
1.371 + # Attribute usage for names.
1.372 +
1.373 + if node._attrnames is None:
1.374 + node._attrnames = {}
1.375 + node._attrmerged = {}
1.376 +
1.377 + # Branches contributing usage to this node.
1.378 +
1.379 + if node._attrbranches is None:
1.380 + node._attrbranches = []
1.381 +
1.382 + # Definitions receiving usage from this node.
1.383 +
1.384 + if node._attrdefs is None:
1.385 + node._attrdefs = []
1.386 +
1.387 + def _define_attribute_accessor(self, name, attrname, node, value):
1.388 +
1.389 + # NOTE: Revisiting of nodes may occur for loops.
1.390 +
1.391 + if node._attrusers is None:
1.392 + node._attrusers = set()
1.393 +
1.394 + node._attrusers.update(self.use_attribute(name, attrname, value))
1.395 + node._username = name
1.396 +
1.397 +class ScopeUsage:
1.398 +
1.399 + "Scope usage tracking."
1.400 +
1.401 + def __init__(self):
1.402 +
1.403 + # Scope usage, indicating the origin of names.
1.404 +
1.405 + self.scope_usage = [{}] # stack of scope usage
1.406 +
1.407 + def define_scope(self, name, scope):
1.408 +
1.409 + """
1.410 + Define 'name' as being from the given 'scope' in the current namespace.
1.411 + """
1.412 +
1.413 + self.scope_usage[-1][name] = scope
1.414 +
1.415 + def note_scope(self, name, scope):
1.416 +
1.417 + """
1.418 + Note usage of 'name' from the given 'scope' in the current namespace.
1.419 + If a conflict has been recorded previously, raise an exception.
1.420 + """
1.421 +
1.422 + scope_usage = self.scope_usage[-1]
1.423 +
1.424 + if scope_usage.has_key(name):
1.425 + found_scope = scope_usage[name]
1.426 + if isinstance(found_scope, ScopeConflict):
1.427 + raise InspectError("Scope conflict for %r: defined as %s." % (
1.428 + name, ", ".join(found_scope.scopes)))
1.429 +
1.430 + scope_usage[name] = scope
1.431 +
1.432 + def used_in_scope(self, name, scope):
1.433 +
1.434 + """
1.435 + Return whether 'name' is used from the given 'scope' in the current
1.436 + namespace.
1.437 + """
1.438 +
1.439 + scope_usage = self.scope_usage[-1]
1.440 + return scope_usage.get(name) == scope
1.441 +
1.442 +class BranchTracking(AttributeUsage, ScopeUsage):
1.443 +
1.444 + """
1.445 + Management of branches, relying on attribute usage support.
1.446 + """
1.447 +
1.448 + def __init__(self):
1.449 + AttributeUsage.__init__(self)
1.450 + ScopeUsage.__init__(self)
1.451 +
1.452 + # Details of attribute users at each active branch level.
1.453 +
1.454 + self.attribute_user_shelves = []
1.455 +
1.456 + # Details of name scopes at each active branch level.
1.457 +
1.458 + self.scope_shelves = []
1.459 +
1.460 + # Suspended user details plus loop details.
1.461 +
1.462 + self.suspended_broken_users = [] # stack of lists of user dicts
1.463 + self.suspended_continuing_users = [] # stack of lists of user dicts
1.464 +
1.465 + # Abandoned usage, useful for reviving usage for exception handlers.
1.466 +
1.467 + self.abandoned_users = [[]] # stack of lists of users
1.468 +
1.469 + # Methods shadowing the convenience methods provided during inspection.
1.470 +
1.471 + def _new_branchpoint(self, loop_node=None):
1.472 +
1.473 + """
1.474 + Establish a new branchpoint where several control-flow branches diverge
1.475 + and subsequently converge.
1.476 + """
1.477 +
1.478 + self.attribute_user_shelves.append([])
1.479 + self.scope_shelves.append([])
1.480 +
1.481 + if loop_node is not None:
1.482 + self.suspended_broken_users.append([])
1.483 + self.suspended_continuing_users.append((loop_node, []))
1.484 +
1.485 + def _new_branch(self, node):
1.486 +
1.487 + """
1.488 + Establish a new control-flow branch, transferring attribute usage to
1.489 + the new branch so that it may be augmented for each name locally.
1.490 +
1.491 + Add the given 'node' as an active user to be informed of attribute
1.492 + usage.
1.493 + """
1.494 +
1.495 + attribute_users = self.attribute_users[-1]
1.496 +
1.497 + # Define this node as the active attribute user for all currently
1.498 + # defined names.
1.499 +
1.500 + new_users = {}
1.501 +
1.502 + for name in attribute_users.keys():
1.503 + new_users[name] = [node]
1.504 + self._init_attribute_user_for_name(node, name)
1.505 +
1.506 + self._init_attribute_user(node)
1.507 + self.attribute_users.append(new_users)
1.508 +
1.509 + # Add this user as a contributor to the previously active users.
1.510 +
1.511 + self._connect_users_to_branch(attribute_users, node)
1.512 +
1.513 + # Retain a record of scope usage.
1.514 +
1.515 + scope_usage = {}
1.516 + scope_usage.update(self.scope_usage[-1])
1.517 + self.scope_usage.append(scope_usage)
1.518 +
1.519 + # Retain a record of abandoned branch users.
1.520 +
1.521 + self.abandoned_users.append([])
1.522 +
1.523 + def _connect_users_to_branch(self, attribute_users, node):
1.524 +
1.525 + """
1.526 + Given the 'attribute_users' mapping, connect the users referenced in the
1.527 + mapping to the given branch 'node'.
1.528 + """
1.529 +
1.530 + all_users = set()
1.531 +
1.532 + for users in attribute_users.values():
1.533 + all_users.update(users)
1.534 +
1.535 + for user in all_users:
1.536 + self._init_attribute_user(user)
1.537 + user._attrbranches.append(node)
1.538 +
1.539 + def _abandon_branch(self, retain_branch=True):
1.540 +
1.541 + """
1.542 + Abandon scope usage, permitting locally different scopes for names,
1.543 + provided these cannot "escape" from the branch.
1.544 + """
1.545 +
1.546 + attribute_users = self.attribute_users[-1]
1.547 +
1.548 + self.attribute_users[-1] = {}
1.549 + self.scope_usage[-1] = abandoned_branch_scope
1.550 +
1.551 + if retain_branch:
1.552 + self.abandoned_users[-1].append(attribute_users)
1.553 +
1.554 + def _suspend_broken_branch(self):
1.555 +
1.556 + """
1.557 + Suspend a branch for resumption after the current loop.
1.558 + """
1.559 +
1.560 + attribute_users = self.attribute_users[-1]
1.561 +
1.562 + users = self.suspended_broken_users[-1]
1.563 + users.append(attribute_users)
1.564 + self._abandon_branch(False)
1.565 +
1.566 + def _suspend_continuing_branch(self):
1.567 +
1.568 + """
1.569 + Suspend a branch for resumption after the current iteration.
1.570 + """
1.571 +
1.572 + attribute_users = self.attribute_users[-1]
1.573 +
1.574 + loop_node, users = self.suspended_continuing_users[-1]
1.575 + users.append(attribute_users)
1.576 + self._abandon_branch(False)
1.577 +
1.578 + def _shelve_branch(self):
1.579 +
1.580 + """
1.581 + Shelve the current control-flow branch, recording the attribute usage
1.582 + for subsequent merging. If this branch should be abandoned, the usage
1.583 + observations are still recorded but will not contribute to subsequent
1.584 + observations after a merge.
1.585 + """
1.586 +
1.587 + users = self.attribute_users.pop()
1.588 + self.attribute_user_shelves[-1].append(users)
1.589 +
1.590 + scope_usage = self.scope_usage.pop()
1.591 + self.scope_shelves[-1].append(scope_usage)
1.592 +
1.593 + def _merge_branches(self):
1.594 +
1.595 + """
1.596 + Merge control-flow branches. This should find the users active within
1.597 + each branch, which have been "shelved", and update the active users
1.598 + dictionary with these contributions.
1.599 + """
1.600 +
1.601 + # Combine the attribute users. This ensures that a list of users
1.602 + # affected by attribute usage is maintained for the current branch.
1.603 +
1.604 + all_shelved_users = self.attribute_user_shelves.pop()
1.605 + new_users = merge_mapping_dicts(all_shelved_users)
1.606 + self.attribute_users[-1] = new_users
1.607 +
1.608 + # Abandoned users are retained for exception handling purposes.
1.609 +
1.610 + all_abandoned_users = self.abandoned_users.pop()
1.611 + new_abandoned_users = merge_mapping_dicts(all_abandoned_users)
1.612 + self.abandoned_users[-1].append(new_abandoned_users)
1.613 +
1.614 + # Combine the scope usage.
1.615 +
1.616 + scope_usage = self.scope_usage[-1]
1.617 + new_scope_usage = {}
1.618 +
1.619 + all_scope_usage = self.scope_shelves.pop()
1.620 + all_scope_names = set()
1.621 +
1.622 + # Find all the names for whom scope information has been defined.
1.623 +
1.624 + for shelved_usage in all_scope_usage:
1.625 + all_scope_names.update(shelved_usage.keys())
1.626 +
1.627 + for shelved_usage in all_scope_usage:
1.628 + for name in all_scope_names:
1.629 +
1.630 + # Find the recorded scope for the name.
1.631 +
1.632 + if shelved_usage.has_key(name):
1.633 + scope = shelved_usage[name]
1.634 + elif scope_usage.has_key(name):
1.635 + scope = scope_usage[name]
1.636 +
1.637 + # For abandoned branches, no scope is asserted for a name.
1.638 +
1.639 + elif isinstance(shelved_usage, AbandonedBranchScope):
1.640 + scope = None
1.641 +
1.642 + # If no scope is recorded, find a suitable external source.
1.643 +
1.644 + else:
1.645 + attr, scope, full_name = self._get_with_scope(name, external=1)
1.646 +
1.647 + # Attempt to record the scope, testing for conflicts.
1.648 +
1.649 + if scope:
1.650 + if not new_scope_usage.has_key(name):
1.651 + new_scope_usage[name] = scope
1.652 + else:
1.653 + new_scope = new_scope_usage[name]
1.654 + if new_scope != scope:
1.655 + if isinstance(new_scope, ScopeConflict):
1.656 + if isinstance(scope, ScopeConflict):
1.657 + scopes = scope.scopes.union(new_scope.scopes)
1.658 + else:
1.659 + scopes = new_scope.scopes.union(set([scope]))
1.660 + elif isinstance(scope, ScopeConflict):
1.661 + scopes = scope.scopes.union(set([new_scope]))
1.662 + else:
1.663 + scopes = set([scope, new_scope])
1.664 + new_scope_usage[name] = ScopeConflict(scopes)
1.665 +
1.666 + self.scope_usage[-1] = new_scope_usage
1.667 +
1.668 + def _resume_broken_branches(self):
1.669 +
1.670 + """
1.671 + Incorporate users from suspended broken branches into the current set of
1.672 + active users.
1.673 + """
1.674 +
1.675 + suspended_users = self.suspended_broken_users.pop()
1.676 + current_users = self.attribute_users[-1]
1.677 + new_users = merge_mapping_dicts(suspended_users + [current_users])
1.678 + self.attribute_users[-1] = new_users
1.679 +
1.680 + def _resume_continuing_branches(self):
1.681 +
1.682 + """
1.683 + Incorporate users from suspended continuing branches into the current
1.684 + set of active users, merging usage from the latter with the former.
1.685 + """
1.686 +
1.687 + loop_node, suspended_users = self.suspended_continuing_users.pop()
1.688 + current_users = self.attribute_users[-1]
1.689 +
1.690 + # Connect the suspended users to the loop node.
1.691 +
1.692 + for users in suspended_users:
1.693 + self._connect_users_to_branch(users, loop_node)
1.694 +
1.695 + # Merge suspended branches with the current branch.
1.696 +
1.697 + new_users = merge_mapping_dicts(suspended_users + [current_users])
1.698 + self.attribute_users[-1] = new_users
1.699 +
1.700 + def _resume_abandoned_branches(self):
1.701 +
1.702 + """
1.703 + Incorporate users from abandoned branches into the current set of active
1.704 + users. The abandoned branches are taken from the containing branch.
1.705 + """
1.706 +
1.707 + current_users = self.attribute_users[-1]
1.708 + abandoned_users = self.abandoned_users[-2]
1.709 + new_users = merge_mapping_dicts(abandoned_users + [current_users])
1.710 + self.attribute_users[-1] = new_users
1.711 +
1.712 +# Special helper classes for usage and scope resolution.
1.713 +
1.714 +class EmptyDict:
1.715 +
1.716 + "A class providing dictionaries which retain no information."
1.717 +
1.718 + def has_key(self, name):
1.719 + return False
1.720 +
1.721 + def __setitem__(self, name, value):
1.722 + pass
1.723 +
1.724 + def __getitem__(self, name):
1.725 + raise KeyError, name
1.726 +
1.727 + def get(self, name, default=None):
1.728 + return default
1.729 +
1.730 + def keys(self):
1.731 + return []
1.732 +
1.733 + values = items = keys
1.734 +
1.735 +class AbandonedBranchScope(EmptyDict):
1.736 +
1.737 + """
1.738 + A class providing a value or state for an abandoned branch distinct from an
1.739 + empty scope dictionary.
1.740 + """
1.741 +
1.742 + pass
1.743 +
1.744 +abandoned_branch_scope = AbandonedBranchScope()
1.745 +
1.746 +class ScopeConflict:
1.747 +
1.748 + """
1.749 + A scope conflict caused when different code branches contribute different
1.750 + sources of names.
1.751 + """
1.752 +
1.753 + def __init__(self, scopes):
1.754 + self.scopes = scopes
1.755 +
1.756 +class UnsetType:
1.757 +
1.758 + "A None-like value."
1.759 +
1.760 + def __nonzero__(self):
1.761 + return False
1.762 +
1.763 +Unset = UnsetType()
1.764 +
1.765 +# vim: tabstop=4 expandtab shiftwidth=4